#
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
#
-# Copyright 2022 Olaf Wintermann. All rights reserved.
+# 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:
all: $(BUILD_ROOT)/build/bin/$(BINTARGET)
$(BUILD_ROOT)/build/bin/$(BINTARGET): $(OBJ)
- $(CC) -o $(BUILD_ROOT)/build/bin/$(BINTARGET) $(OBJ) -L$(BUILD_ROOT)/build/lib -lucx $(LDFLAGS) $(APP_LDFLAGS)
+ $(CC) -o $(BUILD_ROOT)/build/bin/$(BINTARGET) $(OBJ) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(LIB_EXT) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)ucx$(LIB_EXT) $(LDFLAGS) $(TK_LDFLAGS)
$(BUILD_ROOT)/build/application/%.$(OBJ_EXT): %.c
- $(CC) $(CFLAGS) $(APP_CFLAGS) -o $@ -c $<
+ $(CC) $(CFLAGS) $(TK_CFLAGS) -o $@ -c $<
# $PLATFORM is used for platform dependent dependency selection
OS=`uname -s`
OS_VERSION=`uname -r`
+ARCH=`uname -m`
printf "detect platform... "
if [ "$OS" = "SunOS" ]; then
PLATFORM="solaris sunos unix svr4"
--debug add extra compile flags for debug builds
--release add extra compile flags for release builds
+Options:
+ --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif)
+
__EOF__
}
"--help"*) printhelp; abort_configure ;;
"--debug") BUILD_TYPE="debug" ;;
"--release") BUILD_TYPE="release" ;;
+ "--toolkit="*) OPT_TOOLKIT=${ARG#--toolkit=} ;;
+ "--toolkit") echo "option '$ARG' needs a value:"; echo " $ARG=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif)"; abort_configure ;;
"-"*) echo "unknown option: $ARG"; abort_configure ;;
esac
done
printf "loading site defaults... "
. "$prefix/etc/config.site"
echo ok
+else
+ # try to detect the correct libdir on our own, except it was changed by the user
+ if test "$libdir" = '${exec_prefix}/lib'; then
+ if [ "$OS" = "SunOS" ]; then
+ test -d "${exec_prefix}/lib/amd64" && libdir='${exec_prefix}/lib/amd64'
+ else
+ # check if the standard libdir even exists
+ if test -d "${exec_prefix}/lib" ; then
+ :
+ else
+ # if it does not, maybe a lib32 exists
+ test -d "${exec_prefix}/lib32" && libdir='${exec_prefix}/lib32'
+ fi
+ # now check if there is a special 64bit libdir that we should use
+ for i in x86_64 ppc64 s390x aarch64 aarch64_be arm64 ; do
+ if [ $ARCH = $i ]; then
+ test -d "${exec_prefix}/lib64" && libdir='${exec_prefix}/lib64'
+ break
+ fi
+ done
+ fi
+ fi
fi
# check languages
lang_c=
lang_cpp=
+if detect_cpp_compiler ; then
+ lang_cpp=1
+fi
if detect_c_compiler ; then
lang_c=1
fi
fi
}
-dependency_error_xft()
+dependency_error_gtk2legacy()
+{
+ print_check_msg "$dep_checked_gtk2legacy" "checking for gtk2legacy... "
+ # dependency gtk2legacy
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ 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
+ 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_qt5()
+{
+ print_check_msg "$dep_checked_qt5" "checking for qt5... "
+ # dependency qt5
+ while true
+ do
+ if [ -z "$lang_cpp" ] ; then
+ break
+ fi
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if which qmake-qt5 > /dev/null ; then
+ :
+ else
+ break
+ fi
+ if test_pkg_config "Qt5Widgets" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags Qt5Widgets`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs Qt5Widgets`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_QT5"
+ cat >> $TEMP_DIR/make.mk << __EOF__
+# Dependency: qt5
+QMAKE = qmake-qt5
+QT_PRO_FILE = qt5.pro
+__EOF__
+ print_check_msg "$dep_checked_qt5" "yes\n"
+ dep_checked_qt5=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_qt5" "no\n"
+ dep_checked_qt5=1
+ return 0
+}
+dependency_error_gtk2()
{
- print_check_msg "$dep_checked_xft" "checking for xft... "
- # dependency xft
+ 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
+ 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
+ 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
+
+ print_check_msg "$dep_checked_gtk2" "no\n"
+ dep_checked_gtk2=1
+ return 0
+}
+dependency_error_gtk3()
+{
+ 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
+ fi
+ if test_pkg_config "gtk4" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk4`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk4`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK4"
+ 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_libadwaita()
+{
+ print_check_msg "$dep_checked_libadwaita" "checking for libadwaita... "
+ # dependency libadwaita
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "libadwaita-1" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags libadwaita-1`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs libadwaita-1`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK4 -DUI_LIBADWAITA"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+ print_check_msg "$dep_checked_libadwaita" "yes\n"
+ dep_checked_libadwaita=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_libadwaita" "no\n"
+ dep_checked_libadwaita=1
+ return 0
+}
+dependency_error_webkitgtk6()
+{
+ print_check_msg "$dep_checked_webkitgtk6" "checking for webkitgtk6... "
+ # dependency webkitgtk6
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "webkitgtk-6.0" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags webkitgtk-6.0`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs webkitgtk-6.0`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WEBVIEW"
+ print_check_msg "$dep_checked_webkitgtk6" "yes\n"
+ dep_checked_webkitgtk6=1
+ return 1
+ done
+
+ # dependency webkitgtk6
+ while true
+ do
+ print_check_msg "$dep_checked_webkitgtk6" "yes\n"
+ dep_checked_webkitgtk6=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_webkitgtk6" "no\n"
+ dep_checked_webkitgtk6=1
+ return 0
+}
+dependency_error_motif()
+{
+ print_check_msg "$dep_checked_motif" "checking for motif... "
+ # dependency motif platform="bsd"
+ while true
+ do
+ if notisplatform "bsd"; then
+ break
+ fi
if [ -z "$PKG_CONFIG" ]; then
break
fi
else
break
fi
- print_check_msg "$dep_checked_xft" "yes\n"
- dep_checked_xft=1
+ 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
- print_check_msg "$dep_checked_xft" "no\n"
- dep_checked_xft=1
- return 0
-}
-dependency_error_motif()
-{
- print_check_msg "$dep_checked_motif" "checking for motif... "
# dependency motif
while true
do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "xft" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags xft`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs xft`"
+ else
+ break
+ fi
+ if test_pkg_config "fontconfig" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags fontconfig`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs fontconfig`"
+ else
+ break
+ fi
TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF"
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread"
print_check_msg "$dep_checked_motif" "yes\n"
dep_checked_motif=1
return 1
dep_checked_motif=1
return 0
}
+dependency_error_webkit2gtk4()
+{
+ print_check_msg "$dep_checked_webkit2gtk4" "checking for webkit2gtk4... "
+ # dependency webkit2gtk4
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "webkit2gtk-4.1" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags webkit2gtk-4.1`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs webkit2gtk-4.1`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WEBVIEW"
+ print_check_msg "$dep_checked_webkit2gtk4" "yes\n"
+ dep_checked_webkit2gtk4=1
+ return 1
+ done
+
+ # dependency webkit2gtk4
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "webkit2gtk-4.0" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags webkit2gtk-4.0`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs webkit2gtk-4.0`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WEBVIEW"
+ print_check_msg "$dep_checked_webkit2gtk4" "yes\n"
+ dep_checked_webkit2gtk4=1
+ return 1
+ done
+
+ # dependency webkit2gtk4
+ while true
+ do
+ print_check_msg "$dep_checked_webkit2gtk4" "yes\n"
+ dep_checked_webkit2gtk4=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_webkit2gtk4" "no\n"
+ dep_checked_webkit2gtk4=1
+ return 0
+}
+dependency_error_cocoa()
+{
+ 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 notisplatform "windows"; then
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WINUI"
+ print_check_msg "$dep_checked_winui" "yes\n"
+ dep_checked_winui=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_winui" "no\n"
+ dep_checked_winui=1
+ return 0
+}
# start collecting dependency information
echo > "$TEMP_DIR/flags.mk"
break
fi
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+ cat >> "$TEMP_DIR/make.mk" << __EOF__
+LD = \$(CC)
+__EOF__
break
done
break
do
cat >> "$TEMP_DIR/make.mk" << __EOF__
-OBJ_EXT = o
-LIB_EXT = a
+SYS_MAKEFILE = Makefile.unix
+__EOF__
+ break
+ done
+ break
+done
+while true
+do
+ if notisplatform "windows"; then
+ break
+ fi
+ while true
+ do
+
+ cat >> "$TEMP_DIR/make.mk" << __EOF__
+SYS_MAKEFILE = Makefile.win32
+__EOF__
+ 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
+LIB_EXT = .a
+SHLIB_EXT = .dylib
+LIB_PREFIX = lib
+PACKAGE_SCRIPT = package_osx.sh
+__EOF__
+ break
+ done
+ break
+done
+while true
+do
+ if notisplatform "unix"; then
+ break
+ fi
+ if isplatform "macos" || istoolchain "macos"; then
+ break
+ fi
+ while true
+ do
+
+ cat >> "$TEMP_DIR/make.mk" << __EOF__
+OBJ_EXT = .o
+LIB_EXT = .a
+SHLIB_EXT = .so
+LIB_PREFIX = lib
PACKAGE_SCRIPT = package_unix.sh
__EOF__
break
break
done
while true
+do
+ while true
+ do
+
+ cat >> "$TEMP_DIR/make.mk" << __EOF__
+BUILD_BIN_DIR = bin
+BUILD_LIB_DIR = lib
+__EOF__
+ break
+ done
+ break
+done
+while true
do
if notisplatform "bsd"; then
break
# build type
if [ "$BUILD_TYPE" = "debug" ]; then
- TEMP_CFLAGS="\${DEBUG_CC_FLAGS} $TEMP_CFLAGS"
- TEMP_CXXFLAGS="\${DEBUG_CXX_FLAGS} $TEMP_CXXFLAGS"
+ TEMP_CFLAGS="\${DEBUG_CFLAGS} $TEMP_CFLAGS"
+ TEMP_CXXFLAGS="\${DEBUG_CXXFLAGS} $TEMP_CXXFLAGS"
fi
if [ "$BUILD_TYPE" = "release" ]; then
- TEMP_CFLAGS="\${RELEASE_CC_FLAGS} $TEMP_CFLAGS"
- TEMP_CXXFLAGS="\${RELEASE_CXX_FLAGS} $TEMP_CXXFLAGS"
+ TEMP_CFLAGS="\${RELEASE_CFLAGS} $TEMP_CFLAGS"
+ TEMP_CXXFLAGS="\${RELEASE_CXXFLAGS} $TEMP_CXXFLAGS"
fi
# add general dependency flags to flags.mk
#
# OPTION VALUES
#
+checkopt_toolkit_libadwaita()
+{
+ VERR=0
+ if dependency_error_libadwaita ; then
+ VERR=1
+ fi
+ if dependency_error_webkitgtk6 ; 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
+}
+checkopt_toolkit_gtk4()
+{
+ VERR=0
+ if dependency_error_gtk4 ; then
+ VERR=1
+ fi
+ if dependency_error_webkitgtk6 ; 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
+}
+checkopt_toolkit_gtk3()
+{
+ VERR=0
+ if dependency_error_gtk3 ; then
+ VERR=1
+ fi
+ if dependency_error_webkit2gtk4 ; 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
+}
+checkopt_toolkit_gtk2()
+{
+ 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
+}
+checkopt_toolkit_gtk2legacy()
+{
+ 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
+}
+checkopt_toolkit_qt5()
+{
+ 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
+}
+checkopt_toolkit_qt4()
+{
+ 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
+}
+checkopt_toolkit_cocoa()
+{
+ VERR=0
+ if dependency_error_cocoa ; then
+ VERR=1
+ fi
+ if [ $VERR -ne 0 ]; then
+ return 1
+ fi
+ cat >> "$TEMP_DIR/make.mk" << __EOF__
+TOOLKIT = cocoa
+__EOF__
+ return 0
+}
+checkopt_toolkit_motif()
+{
+ 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
+}
#
# TARGETS
#
echo >> "$TEMP_DIR/flags.mk"
-echo "configuring target: app"
-echo "# flags for target app" >> "$TEMP_DIR/flags.mk"
+echo "configuring target: tk"
+echo "# flags for target tk" >> "$TEMP_DIR/flags.mk"
TEMP_CFLAGS=
TEMP_CXXFLAGS=
TEMP_LDFLAGS=
-if dependency_error_motif; then
- DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED motif "
- ERROR=1
-fi
-if dependency_error_xft; then
- DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED xft "
- ERROR=1
-fi
# Features
+# Option: --toolkit
+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_gtk4 ; then
+ echo " toolkit: gtk4" >> "$TEMP_DIR/options"
+ ERROR=0
+ break
+ fi
+ if checkopt_toolkit_gtk3 ; then
+ echo " toolkit: gtk3" >> "$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
+ echo "checking option toolkit = $OPT_TOOLKIT"
+ if false; then
+ false
+ elif [ "$OPT_TOOLKIT" = "libadwaita" ]; then
+ echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+ if checkopt_toolkit_libadwaita ; then
+ :
+ else
+ ERROR=1
+ DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+ fi
+ 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" = "cocoa" ]; then
+ echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+ if checkopt_toolkit_cocoa ; 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
+ else
+ echo
+ echo "Invalid option value - usage:"
+ echo " --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif)"
+ abort_configure
+ fi
+fi
if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then
- echo "APP_CFLAGS += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk"
+ echo "TK_CFLAGS += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk"
fi
if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then
- echo "APP_CXXFLAGS += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk"
+ echo "TK_CXXFLAGS += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk"
fi
if [ -n "${TEMP_LDFLAGS}" ]; then
- echo "APP_LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk"
+ echo "TK_LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk"
fi
echo " localedir: $localedir"
fi
echo
+echo "Options:"
+cat "$TEMP_DIR/options"
+echo
# generate the config.mk file
pwd=`pwd`
#
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
#
-# Copyright 2021 Olaf Wintermann. All rights reserved.
+# Copyright 2023 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:
include config.mk
BUILD_DIRS = build/bin build/lib
-BUILD_DIRS += build/ucx
-BUILD_DIRS += build/application
+BUILD_DIRS += build/application build/ucx
+BUILD_DIRS += build/ui/common build/ui/$(TOOLKIT)
-all: $(BUILD_DIRS) ucx application
+all: $(BUILD_DIRS) ucx ui application
make/$(PACKAGE_SCRIPT)
$(BUILD_DIRS):
mkdir -p $@
-ucx: $(BUILD_DIRS) FORCE
- cd ucx; $(MAKE)
+ui: ucx FORCE
+ cd ui; $(MAKE) all
-application: $(BUILD_DIRS) ucx FORCE
+ucx: FORCE
+ cd ucx; $(MAKE) all
+
+application: ui FORCE
cd application; $(MAKE)
FORCE:
# $PLATFORM is used for platform dependent dependency selection
OS=`uname -s`
OS_VERSION=`uname -r`
+ARCH=`uname -m`
printf "detect platform... "
if [ "$OS" = "SunOS" ]; then
PLATFORM="solaris sunos unix svr4"
printf "loading site defaults... "
. "$prefix/etc/config.site"
echo ok
+else
+ # try to detect the correct libdir on our own, except it was changed by the user
+ if test "$libdir" = '${exec_prefix}/lib'; then
+ if [ "$OS" = "SunOS" ]; then
+ test -d "${exec_prefix}/lib/amd64" && libdir='${exec_prefix}/lib/amd64'
+ else
+ # check if the standard libdir even exists
+ if test -d "${exec_prefix}/lib" ; then
+ :
+ else
+ # if it does not, maybe a lib32 exists
+ test -d "${exec_prefix}/lib32" && libdir='${exec_prefix}/lib32'
+ fi
+ # now check if there is a special 64bit libdir that we should use
+ for i in x86_64 ppc64 s390x aarch64 aarch64_be arm64 ; do
+ if [ $ARCH = $i ]; then
+ test -d "${exec_prefix}/lib64" && libdir='${exec_prefix}/lib64'
+ break
+ fi
+ done
+ fi
+ fi
fi
]]#
## End of unparsed content **
# build type
if [ "$BUILD_TYPE" = "debug" ]; then
- TEMP_CFLAGS="\${DEBUG_CC_FLAGS} $TEMP_CFLAGS"
- TEMP_CXXFLAGS="\${DEBUG_CXX_FLAGS} $TEMP_CXXFLAGS"
+ TEMP_CFLAGS="\${DEBUG_CFLAGS} $TEMP_CFLAGS"
+ TEMP_CXXFLAGS="\${DEBUG_CXXFLAGS} $TEMP_CXXFLAGS"
fi
if [ "$BUILD_TYPE" = "release" ]; then
- TEMP_CFLAGS="\${RELEASE_CC_FLAGS} $TEMP_CFLAGS"
- TEMP_CXXFLAGS="\${RELEASE_CXX_FLAGS} $TEMP_CXXFLAGS"
+ TEMP_CFLAGS="\${RELEASE_CFLAGS} $TEMP_CFLAGS"
+ TEMP_CXXFLAGS="\${RELEASE_CXXFLAGS} $TEMP_CXXFLAGS"
fi
# add general dependency flags to flags.mk
--- /dev/null
+#
+# 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
+MSBUILD = MSBuild.exe
+
+CFLAGS = -std=gnu99 -c -O2 -m64
+COFLAGS = -o
+LDFLAGS =
+LOFLAGS = -o
+ARFLAGS = -r
+RMFLAGS = -f
+
+OBJ_EXT = o
+LIB_EXT = a
+APP_EXT = .exe
+
+PACKAGE_SCRIPT = package_windows.sh
\ No newline at end of file
--- /dev/null
+#
+# 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
--- /dev/null
+#!/bin/sh
+
+# create .app
+rm -Rf build/mk12.app
+cp -R resource/template.app build/mk12.app
+
+mkdir -p build/mk12.app/Contents/MacOS/
+
+cp build/bin/mk12 build/mk12.app/Contents/MacOS/
+
+cp -R resource/locales build/mk12.app/Contents/Resources/
+
--- /dev/null
+#!/bin/sh
+
<project version="0.3" xmlns="http://unixwork.de/uwproj">
<dependency>
<lang>c</lang>
+ <make>LD = \$(CC)</make>
+ </dependency>
+
+ <dependency platform="unix">
+ <make>SYS_MAKEFILE = Makefile.unix</make>
+ </dependency>
+ <dependency platform="windows">
+ <make>SYS_MAKEFILE = Makefile.win32</make>
+ </dependency>
+
+ <dependency name="libadwaita">
+ <pkgconfig>libadwaita-1</pkgconfig>
+ <cflags>-DUI_GTK4 -DUI_LIBADWAITA</cflags>
<ldflags>-lpthread</ldflags>
</dependency>
-
- <dependency name="motif">
- <cflags>-DUI_MOTIF</cflags>
- <ldflags>-lXm -lXt -lX11</ldflags>
+ <dependency name="gtk4">
+ <pkgconfig>gtk4</pkgconfig>
+ <cflags>-DUI_GTK4</cflags>
+ <ldflags>-lpthread</ldflags>
+ </dependency>
+ <dependency name="gtk3">
+ <pkgconfig>gtk+-3.0</pkgconfig>
+ <cflags>-DUI_GTK3</cflags>
+ <ldflags>-lpthread</ldflags>
+ </dependency>
+ <dependency name="gtk2">
+ <test>pkg-config --atleast-version=2.20 gtk+-2.0</test>
+ <pkgconfig>gtk+-2.0</pkgconfig>
+ <cflags>-DUI_GTK2</cflags>
+ <ldflags>-lpthread</ldflags>
+ </dependency>
+ <dependency name="gtk2legacy">
+ <pkgconfig>gtk+-2.0</pkgconfig>
+ <cflags>-DUI_GTK2 -DUI_GTK2LEGACY</cflags>
+ <ldflags>-lpthread</ldflags>
+ </dependency>
+ <dependency name="winui" platform="windows">
+ <cflags>-DUI_WINUI</cflags>
+ </dependency>
+ <!--
+ <dependency name="qt4">
+ <test>which qmake-qt4</test>
+ <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>
+ <lang>cpp</lang>
+ <cflags>-DUI_QT5</cflags>
+ <pkgconfig>Qt5Widgets</pkgconfig>
+ <make>QMAKE = qmake-qt5</make>
+ <make>QT_PRO_FILE = qt5.pro</make>
</dependency>
- <dependency platform="unix">
- <make>OBJ_EXT = o</make>
- <make>LIB_EXT = a</make>
- <make>PACKAGE_SCRIPT = package_unix.sh</make>
+ <dependency name="cocoa" platform="macos">
+ <cflags>-DUI_COCOA</cflags>
+ <ldflags>-lobjc -framework Cocoa</ldflags>
</dependency>
- <dependency name="xft">
+ <dependency name="motif" platform="bsd">
+ <cflags>-DUI_MOTIF -I/usr/local/include/X11</cflags>
+ <ldflags>-lXm -lXt -lX11 -lpthread</ldflags>
<pkgconfig>xft</pkgconfig>
<pkgconfig>fontconfig</pkgconfig>
</dependency>
+ <dependency name="motif">
+ <cflags>-DUI_MOTIF</cflags>
+ <ldflags>-lXm -lXt -lX11 -lpthread</ldflags>
+ <pkgconfig>xft</pkgconfig>
+ <pkgconfig>fontconfig</pkgconfig>
+ </dependency>
+
+ <dependency name="webkitgtk6">
+ <pkgconfig>webkitgtk-6.0</pkgconfig>
+ <cflags>-DUI_WEBVIEW</cflags>
+ </dependency>
+
+ <dependency name="webkitgtk6">
+ <!-- webview unsupported -->
+ </dependency>
+
+ <dependency name="webkit2gtk4">
+ <pkgconfig>webkit2gtk-4.1</pkgconfig>
+ <cflags>-DUI_WEBVIEW</cflags>
+ </dependency>
+
+ <dependency name="webkit2gtk4">
+ <pkgconfig>webkit2gtk-4.0</pkgconfig>
+ <cflags>-DUI_WEBVIEW</cflags>
+ </dependency>
+
+ <dependency name="webkit2gtk4">
+ <!-- webview unsupported -->
+ </dependency>
+
+ <dependency platform="macos">
+ <make>OBJ_EXT = .o</make>
+ <make>LIB_EXT = .a</make>
+ <make>SHLIB_EXT = .dylib</make>
+ <make>LIB_PREFIX = lib</make>
+ <make>PACKAGE_SCRIPT = package_osx.sh</make>
+ </dependency>
+ <dependency platform="unix" not="macos">
+ <make>OBJ_EXT = .o</make>
+ <make>LIB_EXT = .a</make>
+ <make>SHLIB_EXT = .so</make>
+ <make>LIB_PREFIX = lib</make>
+ <make>PACKAGE_SCRIPT = package_unix.sh</make>
+ </dependency>
+ <dependency>
+ <make>BUILD_BIN_DIR = bin</make>
+ <make>BUILD_LIB_DIR = lib</make>
+ </dependency>
+
<dependency platform="bsd">
<cflags>-I/usr/local/include</cflags>
<ldflags>-L/usr/local/lib</ldflags>
</dependency>
- <target name="app">
- <dependencies>motif,xft</dependencies>
+ <target name="tk">
+ <option arg="toolkit">
+ <value str="libadwaita">
+ <dependencies>libadwaita,webkitgtk6</dependencies>
+ <make>TOOLKIT = gtk</make>
+ <make>GTKOBJ = draw_cairo.o</make>
+ </value>
+ <value str="gtk4">
+ <dependencies>gtk4,webkitgtk6</dependencies>
+ <make>TOOLKIT = gtk</make>
+ <make>GTKOBJ = draw_cairo.o</make>
+ </value>
+ <value str="gtk3">
+ <dependencies>gtk3,webkit2gtk4</dependencies>
+ <make>TOOLKIT = gtk</make>
+ <make>GTKOBJ = draw_cairo.o</make>
+ </value>
+ <value str="gtk2">
+ <dependencies>gtk2</dependencies>
+ <make>TOOLKIT = gtk</make>
+ <make>GTKOBJ = draw_cairo.o</make>
+ </value>
+ <value str="gtk2legacy">
+ <dependencies>gtk2legacy</dependencies>
+ <make>TOOLKIT = gtk</make>
+ <make>GTKOBJ = draw_gdk.o</make>
+ </value>
+ <value str="qt5">
+ <dependencies>qt5</dependencies>
+ <make>TOOLKIT = qt</make>
+ <make>LD = \$(CXX)</make>
+ </value>
+ <value str="qt4">
+ <dependencies>qt4</dependencies>
+ <make>TOOLKIT = qt</make>
+ <make>LD = \$(CXX)</make>
+ </value>
+ <value str="cocoa">
+ <dependencies>cocoa</dependencies>
+ <make>TOOLKIT = cocoa</make>
+ </value>
+ <value str="motif">
+ <dependencies>motif</dependencies>
+ <make>TOOLKIT = motif</make>
+ </value>
+ <default value="winui" platform="windows" />
+ <default value="cocoa" platform="macos" />
+ <default value="gtk4" />
+ <default value="gtk3" />
+ <!--<default value="qt5" />-->
+ <default value="gtk2" />
+ <default value="qt4" />
+ <default value="motif" />
+ </option>
</target>
</project>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">\r
+ <assemblyIdentity version="1.0.0.0" name="test.app"/>\r
+\r
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">\r
+ <application>\r
+ <!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8. \r
+ For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 \r
+ \r
+ It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.-->\r
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />\r
+ </application>\r
+ </compatibility>\r
+ \r
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">\r
+ <windowsSettings>\r
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>\r
+ </windowsSettings>\r
+ </application>\r
+</assembly>
\ No newline at end of file
--- /dev/null
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2023 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#include <Windows.h>\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdbool.h>\r
+\r
+#include <ui/ui.h>\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include <ui/ui.h>\r
+\r
+\r
+typedef struct {\r
+ UiString *str1;\r
+ UiString *str2;\r
+ UiString *path;\r
+ UiText *text;\r
+ UiDouble *progress;\r
+ UiList *list;\r
+ UiList *menulist;\r
+ UiInteger *radio;\r
+ UiInteger *tabview;\r
+ UiGeneric *image;\r
+} MyDocument;\r
+\r
+MyDocument *doc1;\r
+MyDocument *doc2;\r
+\r
+UIWIDGET tabview;\r
+\r
+static UiCondVar *cond;\r
+static int thr_end = 0;\r
+static int thr_started = 0;\r
+\r
+int threadfunc(void *data) {\r
+ printf("thr wait for data...\n");\r
+ ui_condvar_wait(cond);\r
+ printf("thr data received: {%s} [%d]\n", cond->data, cond->intdata);\r
+ ui_condvar_destroy(cond);\r
+ cond = NULL;\r
+\r
+ return 0;\r
+}\r
+\r
+void action_start_thread(UiEvent *event, void *data) {\r
+ if(!thr_started) {\r
+ cond = ui_condvar_create();\r
+ ui_job(event->obj, threadfunc, NULL, NULL, NULL);\r
+ thr_started = 1;\r
+ }\r
+}\r
+\r
+void action_notify_thread(UiEvent *event, void *data) {\r
+ if(!thr_end) {\r
+ ui_condvar_signal(cond, "hello thread", 123);\r
+ thr_end = 1;\r
+ }\r
+}\r
+\r
+void action_menu(UiEvent *event, void *userdata) {\r
+\r
+}\r
+\r
+void action_file_selected(UiEvent *event, void *userdata) {\r
+ UiFileList *files = event->eventdata;\r
+ MyDocument *doc = event->document;\r
+ printf("files: %d\n", (int)files->nfiles);\r
+ if(files->nfiles > 0) {\r
+ printf("selected file: %s\n", files->files[0]);\r
+ ui_image_load_file(doc->image, files->files[0]);\r
+ }\r
+}\r
+\r
+void action_button(UiEvent *event, void *userdata) {\r
+ ui_openfiledialog(event->obj, UI_FILEDIALOG_SELECT_SINGLE, action_file_selected, NULL);\r
+}\r
+\r
+void action_switch(UiEvent *event, void *userdata) {\r
+\r
+}\r
+\r
+void action_toolbar_button(UiEvent *event, void *userdata) {\r
+ printf("toolbar button\n");\r
+\r
+ ui_dialog(event->obj, .title = "Dialog Title", .content = "Content Label", .button1_label = "btn1", .button2_label = "btn2", .input = TRUE, .closebutton_label = "Cancel");\r
+}\r
+\r
+void action_dialog_button(UiEvent *event, void *userdata) {\r
+ ui_close(event->obj);\r
+}\r
+\r
+void action_toolbar_dialog(UiEvent *event, void *userdata) {\r
+\r
+ UiObject *dialog = ui_dialog_window(event->obj, .title = "Dialog Window", .lbutton1 = "Cancel 1", .lbutton2 = "Btn 2", .rbutton3 = "Btn3", .rbutton4 = "Login 4", .onclick = action_dialog_button, .default_button = 4, .show_closebutton = UI_OFF);\r
+\r
+ ui_vbox(dialog, .margin = 10, .spacing = 10) {\r
+ ui_label(dialog, .label = "Enter password:");\r
+ ui_passwordfield(dialog, .varname = "password");\r
+ }\r
+\r
+ ui_show(dialog);\r
+}\r
+\r
+UiObject *new_window;\r
+\r
+static void action_unref_newwindow(UiEvent *event, void *userdata) {\r
+ ui_object_unref(event->obj);\r
+ new_window = NULL;\r
+}\r
+\r
+void action_toolbar_newwindow(UiEvent *event, void *userdata) {\r
+ if (new_window) {\r
+ ui_show(new_window);\r
+ return;\r
+ }\r
+\r
+ UiObject *obj = ui_simple_window("New Window", NULL);\r
+ new_window = obj;\r
+ ui_object_ref(obj);\r
+\r
+ ui_headerbar0(obj) {\r
+ ui_headerbar_start(obj) {\r
+ ui_button(obj, .label = "Open");\r
+ }\r
+ ui_headerbar_end(obj) {\r
+ ui_button(obj, .label = "Unref", .onclick = action_unref_newwindow);\r
+ }\r
+ }\r
+\r
+ ui_textarea(obj, .varname="text");\r
+\r
+ ui_show(obj);\r
+}\r
+\r
+MyDocument* create_doc(void) {\r
+ MyDocument *doc = ui_document_new(sizeof(MyDocument));\r
+ UiContext *docctx = ui_document_context(doc);\r
+ doc->str1 = ui_string_new(docctx, "str1");\r
+ doc->str1 = ui_string_new(docctx, "str2");\r
+ doc->path = ui_string_new(docctx, "path");\r
+ doc->progress = ui_double_new(docctx, "progress");\r
+ doc->list = ui_list_new(docctx, "list");\r
+ ui_list_append(doc->list, "test1");\r
+ ui_list_append(doc->list, "test2");\r
+ ui_list_append(doc->list, "test3");\r
+ doc->radio = ui_int_new(docctx, "radio");\r
+ doc->tabview = ui_int_new(docctx, "tabview");\r
+ doc->image = ui_generic_new(docctx, "image");\r
+ //doc->text = ui_text_new(docctx, "text");\r
+ return doc;\r
+}\r
+\r
+UiIcon *icon = NULL;\r
+\r
+static void* list_getvalue(void *elm, int col) {\r
+ /*\r
+ if(col == 0) {\r
+ if(!icon) {\r
+ icon = ui_icon("folder", 24);\r
+ }\r
+ return icon;\r
+ }\r
+ */\r
+\r
+ char *str = elm;\r
+ return col == 0 ? str : "x";\r
+}\r
+\r
+static UiList *menu_list;\r
+int new_item_count = 0;\r
+\r
+void action_add_menu_item(UiEvent *event, void *userdata) {\r
+ char str[64];\r
+ snprintf(str, 64, "new item %d", new_item_count++);\r
+\r
+ ui_list_append(menu_list, strdup(str));\r
+ ui_list_notify(menu_list);\r
+}\r
+\r
+void action_menu_list(UiEvent *event, void *userdata) {\r
+ printf("menu list item: %d\n", event->intval);\r
+}\r
+\r
+static int tab_x = 0;\r
+void action_tab2_button(UiEvent *event, void *userdata) {\r
+ MyDocument *doc = event->document;\r
+ printf("current page: %d\n", (int)ui_get(doc->tabview));\r
+ ui_set(doc->tabview, 0);\r
+}\r
+\r
+\r
+void action_group1(UiEvent *event, void *userdata) {\r
+ UiContext *ctx = event->obj->ctx;\r
+ if(userdata) {\r
+ ui_unset_group(ctx, 1);\r
+ } else {\r
+ ui_set_group(ctx, 1);\r
+ }\r
+}\r
+\r
+void action_group2(UiEvent *event, void *userdata) {\r
+ UiContext *ctx = event->obj->ctx;\r
+ if(userdata) {\r
+ ui_unset_group(ctx, 2);\r
+ } else {\r
+ ui_set_group(ctx, 2);\r
+ }\r
+}\r
+\r
+void application_startup(UiEvent *event, void *data) {\r
+ // global list\r
+ UiContext *global = ui_global_context();\r
+ menu_list = ui_list_new(global, "menulist");\r
+ ui_list_append(menu_list, "menu list item 1");\r
+ ui_list_append(menu_list, "menu list item 2");\r
+ ui_list_append(menu_list, "menu list item 3");\r
+\r
+\r
+\r
+ UiObject *obj = ui_window("Test", NULL);\r
+\r
+ MyDocument *doc = create_doc();\r
+ ui_attach_document(obj->ctx, doc);\r
+\r
+ ui_tabview(obj, .spacing=10, .margin=10, .tabview = UI_TABVIEW_NAVIGATION_SIDE, .varname="tabview") {\r
+ ui_tab(obj, "Tab 1") {\r
+ ui_vbox(obj, .fill = UI_OFF, .margin = 15, .spacing = 15) {\r
+ ui_button(obj, .label = "Test Button", .icon = "application-x-generic", .onclick = action_button);\r
+ ui_togglebutton(obj, .label = "Toggle");\r
+ ui_checkbox(obj, .label = "My Checkbox");\r
+ }\r
+ ui_grid(obj, .fill = UI_OFF, .columnspacing = 15, .rowspacing = 15, .margin = 15) {\r
+ ui_button(obj, .label = "Activate Group 1", .hexpand = TRUE, .onclick = action_group1);\r
+ ui_button(obj, .label = "Disable Group 1", .onclick = action_group1, .onclickdata = "disable");\r
+ ui_newline(obj);\r
+ ui_button(obj, .label = "Activate Group 2", .hexpand = TRUE, .onclick = action_group2);\r
+ ui_button(obj, .label = "Disable Group 2", .onclick = action_group2, .onclickdata = "disable");\r
+ ui_newline(obj);\r
+\r
+ ui_button(obj, .label = "Groups 1,2", .colspan = 2, .groups = UI_GROUPS(1, 2));\r
+ ui_newline(obj);\r
+\r
+ ui_label(obj, .label = "Label Col 1", .align = UI_ALIGN_LEFT);\r
+ ui_label(obj, .label = "Label Col 2", .style = UI_LABEL_STYLE_TITLE, .align = UI_ALIGN_RIGHT);\r
+ ui_newline(obj);\r
+\r
+ //ui_spinner(obj, .step = 5);\r
+ //ui_newline(obj);\r
+\r
+ ui_progressbar(obj, .colspan = 2, .varname = "progress");\r
+ ui_set(doc->progress, 0.75);\r
+ ui_newline(obj);\r
+\r
+ ui_textfield(obj, .value = doc->str1);\r
+ ui_newline(obj);\r
+\r
+ //ui_button(obj, .label="Test");\r
+ ui_path_textfield(obj, .varname = "path");\r
+ ui_set(doc->path, "/test/path/longdirectoryname/123");\r
+ ui_newline(obj);\r
+\r
+ //UiModel *model = ui_model(obj->ctx, UI_ICON_TEXT, "Col 1", UI_STRING, "Col 2", -1);\r
+ //model->getvalue = list_getvalue;\r
+ ui_combobox(obj, .hexpand = true, .vexpand = false, .colspan = 2, .varname = "list", .getvalue = list_getvalue);\r
+ ui_newline(obj);\r
+\r
+ ui_hbox0(obj) {\r
+ ui_radiobutton(obj, .label = "Radio 1", .varname = "radio");\r
+ ui_radiobutton(obj, .label = "Radio 2", .varname = "radio");\r
+ ui_radiobutton(obj, .label = "Radio 3", .varname = "radio");\r
+ }\r
+ }\r
+ }\r
+ ui_tab(obj, "Tab 2") {\r
+ ui_button(obj, .label = "Button 1 Start Thread", .onclick=action_start_thread);\r
+ ui_button(obj, .label = "Button 2 Notify Thread", .onclick=action_notify_thread);\r
+ ui_button(obj, .label = "Button 3", .onclick=action_tab2_button);\r
+ ui_button(obj, .label = "Button 4", .onclick=action_tab2_button);\r
+ ui_button(obj, .label = "Button 5", .onclick=action_tab2_button);\r
+ ui_button(obj, .label = "Button 6", .onclick=action_tab2_button);\r
+ }\r
+ ui_tab(obj, "Tab 3") {\r
+ UiTabViewArgs args = {0};\r
+ UI_CTN(obj, tabview=ui_tabview_create(obj, &args)) {\r
+ UiObject *tab1 = ui_tabview_add(tabview, "Sub 1", -1);\r
+ ui_button(tab1, .label = "Button 1");\r
+\r
+\r
+ UiObject *tab2 = ui_tabview_add(tabview, "Sub 2", -1);\r
+ ui_button(tab2, .label = "Button 2");\r
+ }\r
+ }\r
+ ui_tab(obj, "Tab 4") {\r
+ ui_grid0(obj) {\r
+ ui_button(obj, .label = "test1");\r
+ ui_newline(obj);\r
+ ui_textarea(obj, .varname = "text", .vexpand = TRUE, .hexpand = TRUE);\r
+ }\r
+ }\r
+ ui_tab(obj, "Tab 5") {\r
+ ui_button(obj, .label = "Test Button", .icon = "application-x-generic", .onclick = action_button);\r
+ ui_imageviewer(obj, .varname = "image", .style_class = "imageviewer");\r
+ }\r
+\r
+ ui_tab(obj, "Tab 6") {\r
+ ui_scrolledwindow(obj, .fill = UI_ON) {\r
+ ui_expander(obj, .label = "Expander", .margin = 10, .spacing = 10) {\r
+ ui_label(obj, .label = "Test");\r
+ ui_button(obj, .label = "Button");\r
+ }\r
+\r
+ ui_frame(obj, .label = "Frame", .margin = 10, .spacing = 10) {\r
+ ui_label(obj, .label = "Title", .style = UI_LABEL_STYLE_TITLE);\r
+ ui_label(obj, .label = "Sub-Title", .style = UI_LABEL_STYLE_SUBTITLE);\r
+ ui_label(obj, .label = "Dim Label", .style = UI_LABEL_STYLE_DIM);\r
+ ui_label(obj, .label = "No Style");\r
+ }\r
+\r
+ for(int i=0;i<100;i++) {\r
+ char labelstr[32];\r
+ snprintf(labelstr, 32, "button %d", i);\r
+ ui_button(obj, .label = labelstr);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /*\r
+\r
+ */\r
+\r
+ ui_show(obj);\r
+}\r
+\r
+/*\r
+typedef struct WindowData {\r
+ UiInteger* check;\r
+ UiInteger* toggle;\r
+ UiInteger* radio;\r
+ UiString* text;\r
+ UiString* password;\r
+ UiList* list;\r
+ UiString* t1;\r
+ UiString* t2;\r
+ UiString* t3;\r
+ UiString* path;\r
+ UiList* list2;\r
+ UiList* list3;\r
+ UiDouble* progress;\r
+ UiInteger* spinner;\r
+} WindowData;\r
+\r
+static UiIcon* folder_icon;\r
+\r
+UiList* menuList;\r
+\r
+void event_mt(UiEvent* event, void* data) {\r
+ char* mt_str = data;\r
+\r
+ printf("%s\n", mt_str);\r
+}\r
+\r
+int test_threadfunc(void *data) {\r
+ char* str = data;\r
+ \r
+ return 0;\r
+}\r
+\r
+void action_thread_test(UiEvent* event, void* data) {\r
+ ui_job(event->obj, test_threadfunc, "testdata", event_mt, "testdata2");\r
+}\r
+\r
+void action1(UiEvent* event, void* data) {\r
+ char* action = data;\r
+ \r
+ WindowData* wdata = event->window;\r
+ int64_t is_checked = ui_get(wdata->check);\r
+ int64_t radio = ui_get(wdata->radio);\r
+\r
+ printf("data: %s %d\n", data, is_checked);\r
+\r
+ double d = ui_get(wdata->progress);\r
+ ui_set(wdata->progress, d + 1);\r
+\r
+ int spinner_active = ui_get(wdata->spinner);\r
+ ui_set(wdata->spinner, !spinner_active);\r
+\r
+ ui_list_append(menuList, "List Item X");\r
+ ui_list_append(menuList, "List Item X");\r
+ ui_notify(menuList->observers, NULL);\r
+}\r
+\r
+void action_set_checkbox(UiEvent* event, void* data) {\r
+ char* action = data;\r
+\r
+ WindowData* wdata = event->window;\r
+ wdata->check->set(wdata->check, 1);\r
+}\r
+\r
+void action_onchange(UiEvent* event, void* data) {\r
+ printf("onchange: %d\n", event->intval);\r
+}\r
+\r
+void action_switch(UiEvent* event, void* data) {\r
+ printf("onchange: %d\n", event->intval);\r
+}\r
+\r
+void action_toolbar_button(UiEvent* event, void *data) {\r
+ printf("toolbar action\n");\r
+}\r
+\r
+\r
+void action_listselection_changed(UiEvent* event, void* data) {\r
+ printf("selection changed\n");\r
+ UiListSelection* sel = event->eventdata;\r
+ for (int i = 0; i < sel->count; i++) {\r
+ int row = sel->rows[i];\r
+ printf("row: %d\n", row);\r
+ }\r
+}\r
+\r
+void action_onactivate(UiEvent* event, void* Data) {\r
+ printf("activate\n");\r
+ UiListSelection* sel = event->eventdata;\r
+ for (int i = 0; i < sel->count; i++) {\r
+ int row = sel->rows[i];\r
+ printf("row: %d\n", row);\r
+ }\r
+}\r
+\r
+typedef struct TableData {\r
+ char* col1;\r
+ char* col2;\r
+ char* col3;\r
+} TableData;\r
+\r
+void* table_getvalue(void* data, int i) {\r
+ TableData* t = data;\r
+ switch (i) {\r
+ case 0: return folder_icon;\r
+ case 1: return t->col1;\r
+ case 2: return t->col2;\r
+ case 3: return t->col3;\r
+ }\r
+ return NULL;\r
+}\r
+\r
+void action_add(UiEvent* event, void* data) {\r
+ WindowData* wdata = event->window;\r
+ char* t1 = wdata->t1->get(wdata->t1);\r
+ char* t2 = wdata->t2->get(wdata->t2);\r
+ char* t3 = wdata->t3->get(wdata->t3);\r
+\r
+ TableData* tdat = malloc(sizeof(TableData));\r
+ tdat->col1 = _strdup(t1);\r
+ tdat->col2 = _strdup(t2);\r
+ tdat->col3 = _strdup(t3);\r
+ ui_list_append(wdata->list2, tdat);\r
+ wdata->list2->update(wdata->list2, 0);\r
+\r
+}\r
+\r
+void action_breadcrumb(UiEvent* event, void* data) {\r
+ int i = event->intval;\r
+ char* c = event->eventdata;\r
+ printf("index: %d\n", i);\r
+}\r
+\r
+void dragstart(UiEvent* event, void* data) {\r
+ UiListDnd* ldnd = event->eventdata;\r
+ ui_selection_settext(ldnd->dnd, "Hello World!", -1);\r
+}\r
+\r
+void dragcomplete(UiEvent* event, void* data) {\r
+\r
+}\r
+\r
+void dragover(UiEvent* event, void* data) {\r
+\r
+}\r
+\r
+void drop(UiEvent* event, void* data) {\r
+\r
+}\r
+\r
+void dialog_result(UiEvent *evt, void *data) {\r
+ char *str = evt->eventdata;\r
+ printf("dialog: %d\n", (int)evt->intval);\r
+}\r
+\r
+void btn_dialog(UiEvent *evt, void *data) {\r
+ ui_dialog(evt->obj, .title = "Title", .input = TRUE, .content = "Hello World", .button1_label = "Yes", .button2_label = "No", .closebutton_label = "Close", .result = dialog_result);\r
+}\r
+\r
+\r
+\r
+\r
+void application_startup(UiEvent* event, void* data) {\r
+ UiContext* gctx = ui_global_context();\r
+ menuList = ui_list_new(gctx, "menulist");\r
+ ui_list_append(menuList, "List Item 1");\r
+ ui_list_append(menuList, "List Item 2");\r
+ ui_list_append(menuList, "List Item 3");\r
+ ui_list_append(menuList, "List Item 4");\r
+ ui_list_append(menuList, "List Item 5");\r
+ ui_list_append(menuList, "List Item 6");\r
+\r
+ UiObject* obj = ui_window("Test", NULL);\r
+ WindowData* wdata = ui_malloc(obj->ctx, sizeof(WindowData));\r
+ obj->window = wdata;\r
+ wdata->check = ui_int_new(obj->ctx, "check");\r
+ wdata->toggle = ui_int_new(obj->ctx, "toggle");\r
+ wdata->radio = ui_int_new(obj->ctx, "radio");\r
+ wdata->text = ui_string_new(obj->ctx, "text");\r
+ wdata->password = ui_string_new(obj->ctx, "password");\r
+ wdata->list = ui_list_new(obj->ctx, "list");\r
+ wdata->list2 = ui_list_new(obj->ctx, "list2");\r
+ wdata->list3 = ui_list_new(obj->ctx, "list3");\r
+ wdata->t1 = ui_string_new(obj->ctx, "t1");\r
+ wdata->t2 = ui_string_new(obj->ctx, "t2");\r
+ wdata->t3 = ui_string_new(obj->ctx, "t3");\r
+ wdata->path = ui_string_new(obj->ctx, "path");\r
+ wdata->progress = ui_double_new(obj->ctx, "progress");\r
+ wdata->spinner = ui_int_new(obj->ctx, "spinner");\r
+\r
+ ui_list_append(wdata->list, "Hello");\r
+ ui_list_append(wdata->list, "World");\r
+ ui_list_append(wdata->list, "Item3");\r
+ ui_list_append(wdata->list, "Item4");\r
+ ui_list_append(wdata->list, "Item5");\r
+ ui_list_append(wdata->list, "Item6");\r
+\r
+ ui_list_append(wdata->list3, "usr");\r
+ ui_list_append(wdata->list3, "share");\r
+ ui_list_append(wdata->list3, "test");\r
+ ui_list_append(wdata->list3, "dir");\r
+\r
+ //folder_icon = ui_icon("Folder", 32);\r
+ folder_icon = ui_foldericon(16);\r
+\r
+ TableData* td1 = malloc(sizeof(TableData));\r
+ TableData* td2 = malloc(sizeof(TableData));\r
+ TableData* td3 = malloc(sizeof(TableData));\r
+ TableData* td4 = malloc(sizeof(TableData));\r
+ TableData* td5 = malloc(sizeof(TableData));\r
+ TableData* td6 = malloc(sizeof(TableData));\r
+ td1->col1 = "a1";\r
+ td1->col2 = "b1";\r
+ td1->col3 = "c1";\r
+ td2->col1 = "a2";\r
+ td2->col2 = "b2";\r
+ td2->col3 = "b3";\r
+ td3->col1 = "a3";\r
+ td3->col2 = "b3";\r
+ td3->col3 = "c3";\r
+ td4->col1 = "a3";\r
+ td4->col2 = "b3";\r
+ td4->col3 = "c3";\r
+ td5->col1 = "a3";\r
+ td5->col2 = "b3";\r
+ td5->col3 = "c3";\r
+ td6->col1 = "a3";\r
+ td6->col2 = "b3";\r
+ td6->col3 = "c3";\r
+\r
+ ui_list_append(wdata->list2, td1);\r
+ ui_list_append(wdata->list2, td2);\r
+ ui_list_append(wdata->list2, td3);\r
+ ui_list_append(wdata->list2, td4);\r
+ ui_list_append(wdata->list2, td5);\r
+ ui_list_append(wdata->list2, td6);\r
+\r
+ ui_scrolledwindow0(obj) {\r
+ ui_grid(obj, .margin = 10, .columnspacing = 5, .rowspacing = 20) {\r
+ ui_button(obj, .label = "Thread Test", .onclick = action_thread_test, .onclickdata = "action1");\r
+ ui_button(obj, .label = "Button2", .icon = "Back", .onclick = action1, .onclickdata = "action2");\r
+ ui_button(obj, .icon = "Forward", .onclick = action1, .onclickdata = "action3", .hexpand = true);\r
+ ui_newline(obj);\r
+\r
+ ui_button(obj, .label = "Dialog Test", .onclick = btn_dialog, .onclickdata = "action4");\r
+ ui_button(obj, .label = "Button5", .onclick = action1, .onclickdata = "action5", .colspan = 2);\r
+ ui_newline(obj);\r
+\r
+ ui_button(obj, .label = "Very Long Button Label Text ____________ Test", .onclick = action_set_checkbox);\r
+ ui_newline(obj);\r
+\r
+ ui_checkbox(obj, .label = "Option 1", .value = wdata->check, .onchange = action_onchange);\r
+ ui_togglebutton(obj, .label = "Option 2", .value = wdata->toggle);\r
+ ui_newline(obj);\r
+\r
+ ui_label(obj, .label = "Progress");\r
+ ui_progressspinner(obj, .value = wdata->spinner);\r
+ ui_newline(obj);\r
+ \r
+ ui_hbox(obj, .colspan = 3) {\r
+ ui_radiobutton(obj, .label = "Radio 1", .value = wdata->radio);\r
+ ui_radiobutton(obj, .label = "Radio 2", .value = wdata->radio);\r
+ ui_radiobutton(obj, .label = "Radio 3", .value = wdata->radio);\r
+ }\r
+ ui_newline(obj);\r
+ ui_radiobutton(obj, .label = "Radio 4", .value = wdata->radio);\r
+ ui_switch(obj, .label = "test", .onchange = action_switch);\r
+ ui_newline(obj);\r
+\r
+ //ui_breadcrumbbar(obj, .list = wdata->list3, .onactivate=action_breadcrumb);\r
+ ui_textfield(obj, .varname = "newtext");\r
+ ui_path_textfield(obj, .colspan = 2, .value=wdata->path, .onactivate = action_breadcrumb);\r
+ ui_newline(obj);\r
+ wdata->path->set(wdata->path, "/usr/path/test");\r
+ \r
+ ui_textfield(obj, .value = wdata->text);\r
+ ui_passwordfield(obj, .value = wdata->password);\r
+ ui_newline(obj);\r
+\r
+ ui_frame(obj, .label = "Test", .colspan = 3) {\r
+ ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1");\r
+ }\r
+ ui_newline(obj);\r
+ \r
+ ui_expander(obj, .label = "Expand", .colspan = 3, .margin = 10, .spacing = 5, .isexpanded = false) {\r
+ ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1");\r
+ ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1");\r
+ ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1");\r
+ }\r
+ ui_newline(obj);\r
+\r
+ ui_combobox(obj, .list = wdata->list, .onselection= action_listselection_changed, .onactivate= action_onactivate);\r
+ ui_newline(obj);\r
+\r
+ ui_tabview(obj, .colspan = 3, .vexpand = true, .hexpand = true, .tabview = UI_TABVIEW_NAVIGATION_SIDE) {\r
+ ui_tab(obj, "Tab 1") {\r
+ ui_button(obj, .label = "Tab 1 Button");\r
+ }\r
+ ui_tab(obj, "Tab 2") {\r
+ ui_button(obj, .label = "Tab 2 Button");\r
+ }\r
+ ui_tab(obj, "Tab 3") {\r
+\r
+ }\r
+ }\r
+ ui_newline(obj);\r
+\r
+ ui_label(obj, .label = "Test Label");\r
+ ui_progressbar(obj, .value = wdata->progress, .colspan = 2);\r
+ ui_newline(obj);\r
+\r
+ ui_newline(obj);\r
+ ui_textfield(obj, .value = wdata->t1);\r
+ ui_textfield(obj, .value = wdata->t2);\r
+ ui_textfield(obj, .value = wdata->t3);\r
+ ui_newline(obj);\r
+ ui_button(obj, .label = "Add", .onclick = action_add);\r
+ ui_newline(obj);\r
+\r
+\r
+ ui_newline(obj);\r
+\r
+ UiModel* model = ui_model(obj->ctx, UI_ICON_TEXT, "Col 1", UI_STRING, "Col 2", UI_STRING, "Col 3", -1);\r
+ model->getvalue = table_getvalue;\r
+ ui_table(obj, .colspan = 3, .model = model, .list = wdata->list2, .onactivate = action_onactivate,\r
+ .onselection = action_listselection_changed,\r
+ .ondragstart = dragstart, .ondragcomplete = dragcomplete, .ondrop = drop);\r
+ ui_model_free(obj->ctx, model);\r
+ }\r
+ } \r
+\r
+ ui_show(obj);\r
+}\r
+\r
+*/\r
+\r
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow)\r
+{ \r
+ ui_init("app1", NULL, 0);\r
+ ui_onstartup(application_startup, NULL);\r
+\r
+ // menu\r
+ ui_menu("File") {\r
+ ui_menuitem(.label = "Test");\r
+ }\r
+\r
+ ui_toolbar_item("Test", .label = "Test", .onclick = action_toolbar_button);\r
+ ui_toolbar_item("Test2", .label = "New Window", .onclick = action_toolbar_newwindow);\r
+ ui_toolbar_item("Test3", .label = "Dialog", .onclick = action_toolbar_dialog);\r
+ ui_toolbar_item("Test4", .label = "Test 4", .onclick = action_toolbar_button);\r
+ ui_toolbar_item("Test5", .label = "Test 5", .onclick = action_toolbar_button);\r
+ ui_toolbar_item("Test6", .label = "Test 6", .onclick = action_toolbar_button);\r
+ ui_toolbar_toggleitem("Toggle", .label = "Toggle", .onchange = action_toolbar_button);\r
+ ui_toolbar_menu("Menu", .label = "Menu") {\r
+ ui_menuitem("Secondary Test", .onclick = action_toolbar_button, NULL);\r
+ ui_menu("Secondary Sub") {\r
+ ui_menuitem("Secondary subitem", NULL, NULL);\r
+ }\r
+ ui_menuseparator();\r
+ ui_menu_itemlist(.varname = "menulist", .onselect=action_menu_list);\r
+ ui_menuseparator();\r
+ ui_menuitem("last", .onclick = action_add_menu_item);\r
+ }\r
+\r
+ ui_toolbar_appmenu() {\r
+ ui_menuitem("New");\r
+ ui_menuitem("Open");\r
+ ui_menuitem("Save");\r
+\r
+ ui_menuseparator();\r
+\r
+ ui_menuitem("Close");\r
+ }\r
+\r
+ ui_toolbar_add_default("Test", UI_TOOLBAR_LEFT);\r
+ ui_toolbar_add_default("Test6", UI_TOOLBAR_LEFT);\r
+ ui_toolbar_add_default("Toggle", UI_TOOLBAR_LEFT);\r
+ ui_toolbar_add_default("Menu", UI_TOOLBAR_LEFT);\r
+\r
+ ui_toolbar_add_default("Test2", UI_TOOLBAR_CENTER);\r
+ ui_toolbar_add_default("Test3", UI_TOOLBAR_CENTER);\r
+\r
+ ui_toolbar_add_default("Test4", UI_TOOLBAR_RIGHT);\r
+ ui_toolbar_add_default("Test5", UI_TOOLBAR_RIGHT);\r
+\r
+ ui_main();\r
+\r
+ return (EXIT_SUCCESS);\r
+ \r
+ /*\r
+ ui_init("app1", 0, NULL);\r
+ ui_onstartup(application_startup, NULL);\r
+ \r
+ ui_menu("File") {\r
+ ui_menuitem(.label = "Item 1");\r
+ ui_menuitem(.label = "Item 2");\r
+ ui_menuseparator();\r
+ ui_menu("File Sub") {\r
+ ui_menuitem(.label = "Sub Item");\r
+ }\r
+\r
+ ui_menuitem(.label = "Exit");\r
+ }\r
+\r
+ ui_toolbar_item("Test", .label = "Home", .icon = "Home", .onclick = action_toolbar_button);\r
+ ui_toolbar_toggleitem("Toggle", .label = "Toggle", .onchange = action_toolbar_button);\r
+ ui_toolbar_toggleitem("Toggle2", .label = "Toggle2", .onchange = action_toolbar_button);\r
+ ui_toolbar_toggleitem("Toggle3", .label = "Toggle3", .onchange = action_toolbar_button);\r
+\r
+ ui_toolbar_menu("Menu", .label = "Menu") {\r
+ \r
+ ui_menuitem(.label = "x", NULL, NULL);\r
+ ui_menuitem(.label = "x", NULL, NULL);\r
+ ui_menu_itemlist(.varname = "menulist");\r
+ ui_menuitem(.label = "x", NULL, NULL);\r
+ ui_menuitem(.label = "x", NULL, NULL);\r
+ ui_menuitem(.label = "x", NULL, NULL);\r
+ ui_menu("TB Sub") {\r
+ ui_menuitem("TB subitem", NULL, NULL);\r
+ }\r
+ }\r
+\r
+ ui_toolbar_menu(NULL, .label = "Menu") {\r
+ ui_menuitem("Secondary Test", NULL, NULL);\r
+ ui_menu("Secondary Sub") {\r
+ ui_menuitem("Secondary subitem", NULL, NULL);\r
+ }\r
+ }\r
+\r
+ ui_toolbar_add_default("Test", UI_TOOLBAR_LEFT);\r
+ ui_toolbar_add_default("Toggle", UI_TOOLBAR_LEFT);\r
+ ui_toolbar_add_default("Toggle2", UI_TOOLBAR_CENTER);\r
+ ui_toolbar_add_default("Toggle3", UI_TOOLBAR_CENTER);\r
+ ui_toolbar_add_default("Menu", UI_TOOLBAR_RIGHT);\r
+\r
+ ui_main();\r
+\r
+ return (EXIT_SUCCESS);\r
+ */\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<packages>\r
+ <package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />\r
+ <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240122.1" targetFramework="native" />\r
+ <package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.3233" targetFramework="native" />\r
+ <package id="Microsoft.WindowsAppSDK" version="1.5.241001000" targetFramework="native" />\r
+</packages>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <Import Project="..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props')" />\r
+ <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />\r
+ <Import Project="..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props')" />\r
+ <ItemGroup Label="ProjectConfigurations">\r
+ <ProjectConfiguration Include="Debug|Win32">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|Win32">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Debug|x64">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|x64">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ </ItemGroup>\r
+ <PropertyGroup Label="Globals">\r
+ <VCProjectVersion>16.0</VCProjectVersion>\r
+ <Keyword>Win32Proj</Keyword>\r
+ <ProjectGuid>{3541f08b-e6cc-4c23-a0d3-51983aab33c6}</ProjectGuid>\r
+ <RootNamespace>testapp</RootNamespace>\r
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r
+ <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>\r
+ <WindowsPackageType>None</WindowsPackageType>\r
+ <AppxPackage>false</AppxPackage>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>true</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>false</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <WholeProgramOptimization>true</WholeProgramOptimization>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>true</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>false</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <WholeProgramOptimization>true</WholeProgramOptimization>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+ <ImportGroup Label="ExtensionSettings">\r
+ </ImportGroup>\r
+ <ImportGroup Label="Shared">\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <PropertyGroup Label="UserMacros" />\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir>\r
+ <IntDir>..\..\..\build\vs\testapp\$(Platform)\$(Configuration)\</IntDir>\r
+ </PropertyGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <FunctionLevelLinking>true</FunctionLevelLinking>\r
+ <IntrinsicFunctions>true</IntrinsicFunctions>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <SDLCheck>false</SDLCheck>\r
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;UI_WINUI;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ <AdditionalIncludeDirectories>C:\Users\Olaf\Projekte\toolkit\ui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
+ <LanguageStandard_C>stdc17</LanguageStandard_C>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Windows</SubSystem>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <FunctionLevelLinking>true</FunctionLevelLinking>\r
+ <IntrinsicFunctions>true</IntrinsicFunctions>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemGroup>\r
+ <ClCompile Include="main.c" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <None Include="packages.config" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ProjectReference Include="..\..\..\ui\winui\winui.vcxproj">\r
+ <Project>{59f97886-bf49-4b3f-9ef6-fa7a84f3ab56}</Project>\r
+ </ProjectReference>\r
+ <ProjectReference Include="..\ucx\ucx.vcxproj">\r
+ <Project>{27da0164-3475-43e2-a1a4-a5d07d305749}</Project>\r
+ </ProjectReference>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <Manifest Include="app.manifest" />\r
+ </ItemGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+ <ImportGroup Label="ExtensionTargets">\r
+ <Import Project="..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets')" />\r
+ <Import Project="..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />\r
+ <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />\r
+ <Import Project="..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets')" />\r
+ </ImportGroup>\r
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">\r
+ <PropertyGroup>\r
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>\r
+ </PropertyGroup>\r
+ <Error Condition="!Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props'))" />\r
+ <Error Condition="!Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets'))" />\r
+ <Error Condition="!Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />\r
+ <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />\r
+ <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />\r
+ <Error Condition="!Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props'))" />\r
+ <Error Condition="!Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets'))" />\r
+ </Target>\r
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <ItemGroup>\r
+ <Filter Include="Quelldateien">\r
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r
+ </Filter>\r
+ <Filter Include="Headerdateien">\r
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r
+ <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>\r
+ </Filter>\r
+ <Filter Include="Ressourcendateien">\r
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r
+ </Filter>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ClCompile Include="main.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <None Include="packages.config" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <Manifest Include="app.manifest" />\r
+ </ItemGroup>\r
+</Project>
\ No newline at end of file
--- /dev/null
+\r
+Microsoft Visual Studio Solution File, Format Version 12.00\r
+# Visual Studio Version 17\r
+VisualStudioVersion = 17.5.33530.505\r
+MinimumVisualStudioVersion = 10.0.40219.1\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testapp", "testapp\testapp.vcxproj", "{3541F08B-E6CC-4C23-A0D3-51983AAB33C6}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ucx", "ucx\ucx.vcxproj", "{27DA0164-3475-43E2-A1A4-A5D07D305749}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winui", "..\..\ui\winui\winui.vcxproj", "{59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toolkit", "toolkit\toolkit.vcxproj", "{DF3F075F-1D6A-4B76-8479-94558ACEEA4C}"\r
+EndProject\r
+Global\r
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+ Debug|ARM64 = Debug|ARM64\r
+ Debug|x64 = Debug|x64\r
+ Debug|x86 = Debug|x86\r
+ Release|ARM64 = Release|ARM64\r
+ Release|x64 = Release|x64\r
+ Release|x86 = Release|x86\r
+ EndGlobalSection\r
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|ARM64.ActiveCfg = Debug|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|ARM64.Build.0 = Debug|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x64.ActiveCfg = Debug|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x64.Build.0 = Debug|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x86.Build.0 = Debug|Win32\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|ARM64.ActiveCfg = Release|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|ARM64.Build.0 = Release|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x64.ActiveCfg = Release|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x64.Build.0 = Release|x64\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x86.ActiveCfg = Release|Win32\r
+ {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x86.Build.0 = Release|Win32\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|ARM64.ActiveCfg = Debug|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|ARM64.Build.0 = Debug|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x64.ActiveCfg = Debug|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x64.Build.0 = Debug|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x86.Build.0 = Debug|Win32\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|ARM64.ActiveCfg = Release|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|ARM64.Build.0 = Release|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x64.ActiveCfg = Release|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x64.Build.0 = Release|x64\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x86.ActiveCfg = Release|Win32\r
+ {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x86.Build.0 = Release|Win32\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|ARM64.ActiveCfg = Debug|ARM64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|ARM64.Build.0 = Debug|ARM64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|ARM64.Deploy.0 = Debug|ARM64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x64.ActiveCfg = Debug|x64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x64.Build.0 = Debug|x64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x64.Deploy.0 = Debug|x64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x86.Build.0 = Debug|Win32\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x86.Deploy.0 = Debug|Win32\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|ARM64.ActiveCfg = Release|ARM64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|ARM64.Build.0 = Release|ARM64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|ARM64.Deploy.0 = Release|ARM64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x64.ActiveCfg = Release|x64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x64.Build.0 = Release|x64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x64.Deploy.0 = Release|x64\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x86.ActiveCfg = Release|Win32\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x86.Build.0 = Release|Win32\r
+ {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x86.Deploy.0 = Release|Win32\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Debug|ARM64.ActiveCfg = Debug|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Debug|ARM64.Build.0 = Debug|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Debug|x64.ActiveCfg = Debug|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Debug|x64.Build.0 = Debug|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Debug|x86.ActiveCfg = Debug|Win32\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Debug|x86.Build.0 = Debug|Win32\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Release|ARM64.ActiveCfg = Release|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Release|ARM64.Build.0 = Release|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Release|x64.ActiveCfg = Release|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Release|x64.Build.0 = Release|x64\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Release|x86.ActiveCfg = Release|Win32\r
+ {DF3F075F-1D6A-4B76-8479-94558ACEEA4C}.Release|x86.Build.0 = Release|Win32\r
+ EndGlobalSection\r
+ GlobalSection(SolutionProperties) = preSolution\r
+ HideSolutionNode = FALSE\r
+ EndGlobalSection\r
+ GlobalSection(ExtensibilityGlobals) = postSolution\r
+ SolutionGuid = {141CA624-F556-4BE7-9218-8D6EEAB95C95}\r
+ EndGlobalSection\r
+EndGlobal\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <ItemGroup Label="ProjectConfigurations">\r
+ <ProjectConfiguration Include="Debug|Win32">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|Win32">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Debug|x64">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|x64">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ </ItemGroup>\r
+ <PropertyGroup Label="Globals">\r
+ <VCProjectVersion>16.0</VCProjectVersion>\r
+ <Keyword>Win32Proj</Keyword>\r
+ <ProjectGuid>{27da0164-3475-43e2-a1a4-a5d07d305749}</ProjectGuid>\r
+ <RootNamespace>ucx</RootNamespace>\r
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>true</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>false</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <WholeProgramOptimization>true</WholeProgramOptimization>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+ <ConfigurationType>StaticLibrary</ConfigurationType>\r
+ <UseDebugLibraries>true</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>false</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <WholeProgramOptimization>true</WholeProgramOptimization>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+ <ImportGroup Label="ExtensionSettings">\r
+ </ImportGroup>\r
+ <ImportGroup Label="Shared">\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <PropertyGroup Label="UserMacros" />\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir>\r
+ <IntDir>..\..\..\build\vs\testapp\$(Platform)\$(Configuration)\</IntDir>\r
+ </PropertyGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <FunctionLevelLinking>true</FunctionLevelLinking>\r
+ <IntrinsicFunctions>true</IntrinsicFunctions>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <SDLCheck>false</SDLCheck>\r
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ <LanguageStandard_C>stdc17</LanguageStandard_C>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <FunctionLevelLinking>true</FunctionLevelLinking>\r
+ <IntrinsicFunctions>true</IntrinsicFunctions>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemGroup>\r
+ <ClCompile Include="..\..\..\ucx\allocator.c" />\r
+ <ClCompile Include="..\..\..\ucx\array_list.c" />\r
+ <ClCompile Include="..\..\..\ucx\buffer.c" />\r
+ <ClCompile Include="..\..\..\ucx\compare.c" />\r
+ <ClCompile Include="..\..\..\ucx\hash_key.c" />\r
+ <ClCompile Include="..\..\..\ucx\hash_map.c" />\r
+ <ClCompile Include="..\..\..\ucx\iterator.c" />\r
+ <ClCompile Include="..\..\..\ucx\json.c" />\r
+ <ClCompile Include="..\..\..\ucx\linked_list.c" />\r
+ <ClCompile Include="..\..\..\ucx\list.c" />\r
+ <ClCompile Include="..\..\..\ucx\map.c" />\r
+ <ClCompile Include="..\..\..\ucx\mempool.c" />\r
+ <ClCompile Include="..\..\..\ucx\printf.c" />\r
+ <ClCompile Include="..\..\..\ucx\properties.c" />\r
+ <ClCompile Include="..\..\..\ucx\streams.c" />\r
+ <ClCompile Include="..\..\..\ucx\string.c" />\r
+ <ClCompile Include="..\..\..\ucx\szmul.c" />\r
+ <ClCompile Include="..\..\..\ucx\tree.c" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ClInclude Include="..\..\..\ucx\cx\allocator.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\array_list.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\buffer.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\collection.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\common.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\compare.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\hash_key.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\hash_map.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\iterator.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\json.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\linked_list.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\list.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\map.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\mempool.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\printf.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\properties.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\streams.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\string.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\test.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\tree.h" />\r
+ <ClInclude Include="..\..\..\ucx\cx\utils.h" />\r
+ </ItemGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+ <ImportGroup Label="ExtensionTargets">\r
+ </ImportGroup>\r
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <ItemGroup>\r
+ <Filter Include="Quelldateien">\r
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r
+ </Filter>\r
+ <Filter Include="Headerdateien">\r
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\r
+ <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>\r
+ </Filter>\r
+ <Filter Include="Ressourcendateien">\r
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r
+ </Filter>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ClCompile Include="..\..\..\ucx\allocator.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\array_list.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\buffer.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\compare.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\hash_key.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\hash_map.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\linked_list.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\list.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\map.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\printf.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\string.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\mempool.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\iterator.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\szmul.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\tree.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\json.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\properties.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ucx\streams.c">\r
+ <Filter>Quelldateien</Filter>\r
+ </ClCompile>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ClInclude Include="..\..\..\ucx\cx\allocator.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\array_list.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\buffer.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\collection.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\common.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\compare.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\hash_key.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\hash_map.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\iterator.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\linked_list.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\list.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\map.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\mempool.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\printf.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\string.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\test.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\tree.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\json.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\properties.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\streams.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ucx\cx\utils.h">\r
+ <Filter>Headerdateien</Filter>\r
+ </ClInclude>\r
+ </ItemGroup>\r
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <ItemGroup Label="ProjectConfigurations">\r
+ <ProjectConfiguration Include="Debug|Win32">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|Win32">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Debug|x64">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|x64">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ </ItemGroup>\r
+ <PropertyGroup Label="Globals">\r
+ <VCProjectVersion>17.0</VCProjectVersion>\r
+ <Keyword>Win32Proj</Keyword>\r
+ <ProjectGuid>{8b88698e-c185-4383-99fe-0c34d6deed2e}</ProjectGuid>\r
+ <RootNamespace>uicommon</RootNamespace>\r
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>true</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>false</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <WholeProgramOptimization>true</WholeProgramOptimization>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+ <ConfigurationType>StaticLibrary</ConfigurationType>\r
+ <UseDebugLibraries>true</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <UseDebugLibraries>false</UseDebugLibraries>\r
+ <PlatformToolset>v143</PlatformToolset>\r
+ <WholeProgramOptimization>true</WholeProgramOptimization>\r
+ <CharacterSet>Unicode</CharacterSet>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+ <ImportGroup Label="ExtensionSettings">\r
+ </ImportGroup>\r
+ <ImportGroup Label="Shared">\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ </ImportGroup>\r
+ <PropertyGroup Label="UserMacros" />\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir>\r
+ <IntDir>..\..\..\build\vs\testapp\$(Platform)\$(Configuration)\</IntDir>\r
+ </PropertyGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <FunctionLevelLinking>true</FunctionLevelLinking>\r
+ <IntrinsicFunctions>true</IntrinsicFunctions>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <SDLCheck>false</SDLCheck>\r
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;UI_WINUI;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ <LanguageStandard_C>stdc17</LanguageStandard_C>\r
+ <AdditionalIncludeDirectories>$(SolutionDir)..\..\ucx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+ <ClCompile>\r
+ <WarningLevel>Level3</WarningLevel>\r
+ <FunctionLevelLinking>true</FunctionLevelLinking>\r
+ <IntrinsicFunctions>true</IntrinsicFunctions>\r
+ <SDLCheck>true</SDLCheck>\r
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <ConformanceMode>true</ConformanceMode>\r
+ </ClCompile>\r
+ <Link>\r
+ <SubSystem>Console</SubSystem>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemGroup>\r
+ <ClCompile Include="..\..\..\ui\common\context.c" />\r
+ <ClCompile Include="..\..\..\ui\common\document.c" />\r
+ <ClCompile Include="..\..\..\ui\common\menu.c" />\r
+ <ClCompile Include="..\..\..\ui\common\object.c" />\r
+ <ClCompile Include="..\..\..\ui\common\properties.c" />\r
+ <ClCompile Include="..\..\..\ui\common\toolbar.c" />\r
+ <ClCompile Include="..\..\..\ui\common\types.c" />\r
+ <ClCompile Include="..\..\..\ui\common\ucx_properties.c" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ClInclude Include="..\..\..\ui\common\context.h" />\r
+ <ClInclude Include="..\..\..\ui\common\document.h" />\r
+ <ClInclude Include="..\..\..\ui\common\menu.h" />\r
+ <ClInclude Include="..\..\..\ui\common\object.h" />\r
+ <ClInclude Include="..\..\..\ui\common\properties.h" />\r
+ <ClInclude Include="..\..\..\ui\common\toolbar.h" />\r
+ <ClInclude Include="..\..\..\ui\common\types.h" />\r
+ <ClInclude Include="..\..\..\ui\common\ucx_properties.h" />\r
+ </ItemGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+ <ImportGroup Label="ExtensionTargets">\r
+ </ImportGroup>\r
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <ItemGroup>\r
+ <Filter Include="Ressourcendateien">\r
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\r
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\r
+ </Filter>\r
+ <Filter Include="Ressourcendateien\Source">\r
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\r
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\r
+ </Filter>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ClCompile Include="..\..\..\ui\common\context.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ui\common\document.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ui\common\menu.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ui\common\object.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ui\common\properties.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ui\common\toolbar.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ui\common\types.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\ui\common\ucx_properties.c">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClCompile>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ClInclude Include="..\..\..\ui\common\context.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ui\common\document.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ui\common\menu.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ui\common\object.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ui\common\properties.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ui\common\toolbar.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ui\common\types.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ <ClInclude Include="..\..\..\ui\common\ucx_properties.h">\r
+ <Filter>Ressourcendateien\Source</Filter>\r
+ </ClInclude>\r
+ </ItemGroup>\r
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <PropertyGroup />\r
+</Project>
\ No newline at end of file
--- /dev/null
+#
+# 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
+
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 77;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ ED18C9232E76CA5500B64EA5 /* entry.m in Sources */ = {isa = PBXBuildFile; fileRef = ED18C9222E76CA5500B64EA5 /* entry.m */; };
+ ED2F55AE2E34FAD800A84793 /* Toolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = ED2F55AD2E34FAD800A84793 /* Toolbar.m */; };
+ ED52BFB02D86FC5D00FD8BE5 /* text.m in Sources */ = {isa = PBXBuildFile; fileRef = ED52BFAF2D86FC5D00FD8BE5 /* text.m */; };
+ ED6580EE2CFF19F900F5402F /* context.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580DD2CFF19F900F5402F /* context.c */; };
+ ED6580EF2CFF19F900F5402F /* menu.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E12CFF19F900F5402F /* menu.c */; };
+ ED6580F02CFF19F900F5402F /* threadpool.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E72CFF19F900F5402F /* threadpool.c */; };
+ ED6580F12CFF19F900F5402F /* condvar.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580DB2CFF19F900F5402F /* condvar.c */; };
+ ED6580F22CFF19F900F5402F /* document.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580DF2CFF19F900F5402F /* document.c */; };
+ ED6580F32CFF19F900F5402F /* object.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E32CFF19F900F5402F /* object.c */; };
+ ED6580F42CFF19F900F5402F /* toolbar.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E92CFF19F900F5402F /* toolbar.c */; };
+ ED6580F52CFF19F900F5402F /* types.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580EB2CFF19F900F5402F /* types.c */; };
+ ED6580F62CFF19F900F5402F /* properties.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E52CFF19F900F5402F /* properties.c */; };
+ ED6580F72CFF19F900F5402F /* ucx_properties.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580ED2CFF19F900F5402F /* ucx_properties.c */; };
+ ED65811D2CFF1A3000F5402F /* linked_list.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581142CFF1A3000F5402F /* linked_list.c */; };
+ ED65811E2CFF1A3000F5402F /* tree.c in Sources */ = {isa = PBXBuildFile; fileRef = ED65811B2CFF1A3000F5402F /* tree.c */; };
+ ED6581202CFF1A3000F5402F /* mempool.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581172CFF1A3000F5402F /* mempool.c */; };
+ ED6581212CFF1A3000F5402F /* map.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581162CFF1A3000F5402F /* map.c */; };
+ ED6581222CFF1A3000F5402F /* hash_map.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581122CFF1A3000F5402F /* hash_map.c */; };
+ ED6581232CFF1A3000F5402F /* array_list.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580FA2CFF1A3000F5402F /* array_list.c */; };
+ ED6581242CFF1A3000F5402F /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581152CFF1A3000F5402F /* list.c */; };
+ ED6581252CFF1A3000F5402F /* compare.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580FC2CFF1A3000F5402F /* compare.c */; };
+ ED6581262CFF1A3000F5402F /* hash_key.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581112CFF1A3000F5402F /* hash_key.c */; };
+ ED6581272CFF1A3000F5402F /* iterator.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581132CFF1A3000F5402F /* iterator.c */; };
+ ED6581282CFF1A3000F5402F /* string.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581192CFF1A3000F5402F /* string.c */; };
+ ED6581292CFF1A3000F5402F /* allocator.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580F92CFF1A3000F5402F /* allocator.c */; };
+ ED65812B2CFF1A3000F5402F /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580FB2CFF1A3000F5402F /* buffer.c */; };
+ ED65812C2CFF1A3000F5402F /* printf.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581182CFF1A3000F5402F /* printf.c */; };
+ ED6581312CFF1A8800F5402F /* toolkit.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581302CFF1A8800F5402F /* toolkit.m */; };
+ ED6581342CFF1F1900F5402F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581332CFF1F1900F5402F /* AppDelegate.m */; };
+ ED6581392CFF287300F5402F /* EventData.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581362CFF287300F5402F /* EventData.m */; };
+ ED6581432CFF3BCE00F5402F /* window.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581422CFF3BCE00F5402F /* window.m */; };
+ ED6581442CFF3BCE00F5402F /* button.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65813C2CFF3BCE00F5402F /* button.m */; };
+ ED6581452CFF3BCE00F5402F /* Container.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65813E2CFF3BCE00F5402F /* Container.m */; };
+ ED6581462CFF3BCE00F5402F /* GridLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581402CFF3BCE00F5402F /* GridLayout.m */; };
+ ED65815C2CFF3EE900F5402F /* MainWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65815B2CFF3EE900F5402F /* MainWindow.m */; };
+ ED65815F2CFF4BF200F5402F /* WindowManager.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65815E2CFF4BF200F5402F /* WindowManager.m */; };
+ ED679B0A2E5B266C001D4F71 /* label.m in Sources */ = {isa = PBXBuildFile; fileRef = ED679B092E5B266C001D4F71 /* label.m */; };
+ ED679B0D2E5B2FB0001D4F71 /* UiThread.m in Sources */ = {isa = PBXBuildFile; fileRef = ED679B0C2E5B2FB0001D4F71 /* UiThread.m */; };
+ ED6FB03D2E95466F006C6E8E /* args.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6FB0382E95466F006C6E8E /* args.c */; };
+ ED6FB03E2E95466F006C6E8E /* container.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6FB03A2E95466F006C6E8E /* container.c */; };
+ ED6FB03F2E95466F006C6E8E /* wrapper.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6FB03C2E95466F006C6E8E /* wrapper.c */; };
+ ED83C2BF2E8EA49200054B22 /* BoxContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = ED83C2BE2E8EA49200054B22 /* BoxContainer.m */; };
+ ED8687E52D999CF3002F3EC2 /* menu.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8687E42D999CF3002F3EC2 /* menu.m */; };
+ ED895DAA2EA0CACC00040078 /* TabView.m in Sources */ = {isa = PBXBuildFile; fileRef = ED895DA92EA0CACC00040078 /* TabView.m */; };
+ ED99F04A2E5CBD2E00A4CC97 /* widget.m in Sources */ = {isa = PBXBuildFile; fileRef = ED99F0492E5CBD2E00A4CC97 /* widget.m */; };
+ EDB452C32E302C65006FB12D /* image.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB452C22E302C65006FB12D /* image.m */; };
+ EDBC4D272EAD4BB0005CDF38 /* webview.m in Sources */ = {isa = PBXBuildFile; fileRef = EDBC4D262EAD4BB0005CDF38 /* webview.m */; };
+ EDC315A92E9A739300403776 /* json.c in Sources */ = {isa = PBXBuildFile; fileRef = EDC315A62E9A739300403776 /* json.c */; };
+ EDC315AA2E9A739300403776 /* properties.c in Sources */ = {isa = PBXBuildFile; fileRef = EDC315A72E9A739300403776 /* properties.c */; };
+ EDC315AB2E9A739300403776 /* streams.c in Sources */ = {isa = PBXBuildFile; fileRef = EDC315A82E9A739300403776 /* streams.c */; };
+ EDCD22272E59EEF5000612AF /* list.m in Sources */ = {isa = PBXBuildFile; fileRef = EDCD22262E59EEF5000612AF /* list.m */; };
+ EDCD22352E59F3B1000612AF /* ListDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = EDCD22342E59F3B1000612AF /* ListDataSource.m */; };
+ EDCD22382E5A160A000612AF /* ListDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EDCD22372E5A160A000612AF /* ListDelegate.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ ED18C9212E76CA5500B64EA5 /* entry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = entry.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/entry.h; sourceTree = "<absolute>"; };
+ ED18C9222E76CA5500B64EA5 /* entry.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = entry.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/entry.m; sourceTree = "<absolute>"; };
+ ED2F55AC2E34FAD800A84793 /* Toolbar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Toolbar.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/Toolbar.h; sourceTree = "<absolute>"; };
+ ED2F55AD2E34FAD800A84793 /* Toolbar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Toolbar.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/Toolbar.m; sourceTree = "<absolute>"; };
+ ED52BFAE2D86FC5D00FD8BE5 /* text.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = text.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/text.h; sourceTree = "<absolute>"; };
+ ED52BFAF2D86FC5D00FD8BE5 /* text.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = text.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/text.m; sourceTree = "<absolute>"; };
+ ED6580AC2CFF122700F5402F /* toolkit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = toolkit.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ ED6580DA2CFF19F900F5402F /* condvar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = condvar.h; path = /Users/olaf/Projekte/toolkit/ui/common/condvar.h; sourceTree = "<absolute>"; };
+ ED6580DB2CFF19F900F5402F /* condvar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = condvar.c; path = /Users/olaf/Projekte/toolkit/ui/common/condvar.c; sourceTree = "<absolute>"; };
+ ED6580DC2CFF19F900F5402F /* context.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = context.h; path = /Users/olaf/Projekte/toolkit/ui/common/context.h; sourceTree = "<absolute>"; };
+ ED6580DD2CFF19F900F5402F /* context.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = context.c; path = /Users/olaf/Projekte/toolkit/ui/common/context.c; sourceTree = "<absolute>"; };
+ ED6580DE2CFF19F900F5402F /* document.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = document.h; path = /Users/olaf/Projekte/toolkit/ui/common/document.h; sourceTree = "<absolute>"; };
+ ED6580DF2CFF19F900F5402F /* document.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = document.c; path = /Users/olaf/Projekte/toolkit/ui/common/document.c; sourceTree = "<absolute>"; };
+ ED6580E02CFF19F900F5402F /* menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = menu.h; path = /Users/olaf/Projekte/toolkit/ui/common/menu.h; sourceTree = "<absolute>"; };
+ ED6580E12CFF19F900F5402F /* menu.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = menu.c; path = /Users/olaf/Projekte/toolkit/ui/common/menu.c; sourceTree = "<absolute>"; };
+ ED6580E22CFF19F900F5402F /* object.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = object.h; path = /Users/olaf/Projekte/toolkit/ui/common/object.h; sourceTree = "<absolute>"; };
+ ED6580E32CFF19F900F5402F /* object.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = object.c; path = /Users/olaf/Projekte/toolkit/ui/common/object.c; sourceTree = "<absolute>"; };
+ ED6580E42CFF19F900F5402F /* properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = properties.h; path = /Users/olaf/Projekte/toolkit/ui/common/properties.h; sourceTree = "<absolute>"; };
+ ED6580E52CFF19F900F5402F /* properties.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = properties.c; path = /Users/olaf/Projekte/toolkit/ui/common/properties.c; sourceTree = "<absolute>"; };
+ ED6580E62CFF19F900F5402F /* threadpool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = threadpool.h; path = /Users/olaf/Projekte/toolkit/ui/common/threadpool.h; sourceTree = "<absolute>"; };
+ ED6580E72CFF19F900F5402F /* threadpool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = threadpool.c; path = /Users/olaf/Projekte/toolkit/ui/common/threadpool.c; sourceTree = "<absolute>"; };
+ ED6580E82CFF19F900F5402F /* toolbar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolbar.h; path = /Users/olaf/Projekte/toolkit/ui/common/toolbar.h; sourceTree = "<absolute>"; };
+ ED6580E92CFF19F900F5402F /* toolbar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = toolbar.c; path = /Users/olaf/Projekte/toolkit/ui/common/toolbar.c; sourceTree = "<absolute>"; };
+ ED6580EA2CFF19F900F5402F /* types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = types.h; path = /Users/olaf/Projekte/toolkit/ui/common/types.h; sourceTree = "<absolute>"; };
+ ED6580EB2CFF19F900F5402F /* types.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = types.c; path = /Users/olaf/Projekte/toolkit/ui/common/types.c; sourceTree = "<absolute>"; };
+ ED6580EC2CFF19F900F5402F /* ucx_properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ucx_properties.h; path = /Users/olaf/Projekte/toolkit/ui/common/ucx_properties.h; sourceTree = "<absolute>"; };
+ ED6580ED2CFF19F900F5402F /* ucx_properties.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = ucx_properties.c; path = /Users/olaf/Projekte/toolkit/ui/common/ucx_properties.c; sourceTree = "<absolute>"; };
+ ED6580F92CFF1A3000F5402F /* allocator.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = allocator.c; path = /Users/olaf/Projekte/toolkit/ucx/allocator.c; sourceTree = "<absolute>"; };
+ ED6580FA2CFF1A3000F5402F /* array_list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = array_list.c; path = /Users/olaf/Projekte/toolkit/ucx/array_list.c; sourceTree = "<absolute>"; };
+ ED6580FB2CFF1A3000F5402F /* buffer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = buffer.c; path = /Users/olaf/Projekte/toolkit/ucx/buffer.c; sourceTree = "<absolute>"; };
+ ED6580FC2CFF1A3000F5402F /* compare.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = compare.c; path = /Users/olaf/Projekte/toolkit/ucx/compare.c; sourceTree = "<absolute>"; };
+ ED6580FD2CFF1A3000F5402F /* allocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = allocator.h; sourceTree = "<group>"; };
+ ED6580FE2CFF1A3000F5402F /* array_list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = array_list.h; sourceTree = "<group>"; };
+ ED6580FF2CFF1A3000F5402F /* buffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = buffer.h; sourceTree = "<group>"; };
+ ED6581002CFF1A3000F5402F /* collection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = collection.h; sourceTree = "<group>"; };
+ ED6581012CFF1A3000F5402F /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
+ ED6581032CFF1A3000F5402F /* compare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = compare.h; sourceTree = "<group>"; };
+ ED6581042CFF1A3000F5402F /* hash_key.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hash_key.h; sourceTree = "<group>"; };
+ ED6581052CFF1A3000F5402F /* hash_map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hash_map.h; sourceTree = "<group>"; };
+ ED6581062CFF1A3000F5402F /* iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iterator.h; sourceTree = "<group>"; };
+ ED6581072CFF1A3000F5402F /* linked_list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = linked_list.h; sourceTree = "<group>"; };
+ ED6581082CFF1A3000F5402F /* list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = "<group>"; };
+ ED6581092CFF1A3000F5402F /* map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = map.h; sourceTree = "<group>"; };
+ ED65810A2CFF1A3000F5402F /* mempool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mempool.h; sourceTree = "<group>"; };
+ ED65810B2CFF1A3000F5402F /* printf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = printf.h; sourceTree = "<group>"; };
+ ED65810C2CFF1A3000F5402F /* string.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = string.h; sourceTree = "<group>"; };
+ ED65810D2CFF1A3000F5402F /* test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test.h; sourceTree = "<group>"; };
+ ED65810E2CFF1A3000F5402F /* tree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tree.h; sourceTree = "<group>"; };
+ ED6581112CFF1A3000F5402F /* hash_key.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = hash_key.c; path = /Users/olaf/Projekte/toolkit/ucx/hash_key.c; sourceTree = "<absolute>"; };
+ ED6581122CFF1A3000F5402F /* hash_map.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = hash_map.c; path = /Users/olaf/Projekte/toolkit/ucx/hash_map.c; sourceTree = "<absolute>"; };
+ ED6581132CFF1A3000F5402F /* iterator.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = iterator.c; path = /Users/olaf/Projekte/toolkit/ucx/iterator.c; sourceTree = "<absolute>"; };
+ ED6581142CFF1A3000F5402F /* linked_list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = linked_list.c; path = /Users/olaf/Projekte/toolkit/ucx/linked_list.c; sourceTree = "<absolute>"; };
+ ED6581152CFF1A3000F5402F /* list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = list.c; path = /Users/olaf/Projekte/toolkit/ucx/list.c; sourceTree = "<absolute>"; };
+ ED6581162CFF1A3000F5402F /* map.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = map.c; path = /Users/olaf/Projekte/toolkit/ucx/map.c; sourceTree = "<absolute>"; };
+ ED6581172CFF1A3000F5402F /* mempool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = mempool.c; path = /Users/olaf/Projekte/toolkit/ucx/mempool.c; sourceTree = "<absolute>"; };
+ ED6581182CFF1A3000F5402F /* printf.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = printf.c; path = /Users/olaf/Projekte/toolkit/ucx/printf.c; sourceTree = "<absolute>"; };
+ ED6581192CFF1A3000F5402F /* string.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = string.c; path = /Users/olaf/Projekte/toolkit/ucx/string.c; sourceTree = "<absolute>"; };
+ ED65811B2CFF1A3000F5402F /* tree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = tree.c; path = /Users/olaf/Projekte/toolkit/ucx/tree.c; sourceTree = "<absolute>"; };
+ ED65812F2CFF1A8800F5402F /* toolkit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolkit.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/toolkit.h; sourceTree = "<absolute>"; };
+ ED6581302CFF1A8800F5402F /* toolkit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = toolkit.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/toolkit.m; sourceTree = "<absolute>"; };
+ ED6581322CFF1F1900F5402F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/AppDelegate.h; sourceTree = "<absolute>"; };
+ ED6581332CFF1F1900F5402F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/AppDelegate.m; sourceTree = "<absolute>"; };
+ ED6581352CFF287300F5402F /* EventData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EventData.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/EventData.h; sourceTree = "<absolute>"; };
+ ED6581362CFF287300F5402F /* EventData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = EventData.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/EventData.m; sourceTree = "<absolute>"; };
+ ED65813B2CFF3BCE00F5402F /* button.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = button.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/button.h; sourceTree = "<absolute>"; };
+ ED65813C2CFF3BCE00F5402F /* button.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = button.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/button.m; sourceTree = "<absolute>"; };
+ ED65813D2CFF3BCE00F5402F /* Container.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Container.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/Container.h; sourceTree = "<absolute>"; };
+ ED65813E2CFF3BCE00F5402F /* Container.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Container.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/Container.m; sourceTree = "<absolute>"; };
+ ED65813F2CFF3BCE00F5402F /* GridLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GridLayout.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/GridLayout.h; sourceTree = "<absolute>"; };
+ ED6581402CFF3BCE00F5402F /* GridLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = GridLayout.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/GridLayout.m; sourceTree = "<absolute>"; };
+ ED6581412CFF3BCE00F5402F /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = window.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/window.h; sourceTree = "<absolute>"; };
+ ED6581422CFF3BCE00F5402F /* window.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = window.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/window.m; sourceTree = "<absolute>"; };
+ ED6581482CFF3CA000F5402F /* button.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = button.h; path = /Users/olaf/Projekte/toolkit/ui/ui/button.h; sourceTree = "<absolute>"; };
+ ED6581492CFF3CA000F5402F /* container.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = container.h; path = /Users/olaf/Projekte/toolkit/ui/ui/container.h; sourceTree = "<absolute>"; };
+ ED65814A2CFF3CA000F5402F /* display.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = display.h; path = /Users/olaf/Projekte/toolkit/ui/ui/display.h; sourceTree = "<absolute>"; };
+ ED65814B2CFF3CA000F5402F /* dnd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dnd.h; path = /Users/olaf/Projekte/toolkit/ui/ui/dnd.h; sourceTree = "<absolute>"; };
+ ED65814C2CFF3CA000F5402F /* entry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = entry.h; path = /Users/olaf/Projekte/toolkit/ui/ui/entry.h; sourceTree = "<absolute>"; };
+ ED65814D2CFF3CA000F5402F /* graphics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = graphics.h; path = /Users/olaf/Projekte/toolkit/ui/ui/graphics.h; sourceTree = "<absolute>"; };
+ ED65814E2CFF3CA000F5402F /* icons.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = icons.h; path = /Users/olaf/Projekte/toolkit/ui/ui/icons.h; sourceTree = "<absolute>"; };
+ ED65814F2CFF3CA000F5402F /* image.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = image.h; path = /Users/olaf/Projekte/toolkit/ui/ui/image.h; sourceTree = "<absolute>"; };
+ ED6581502CFF3CA000F5402F /* menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = menu.h; path = /Users/olaf/Projekte/toolkit/ui/ui/menu.h; sourceTree = "<absolute>"; };
+ ED6581512CFF3CA000F5402F /* properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = properties.h; path = /Users/olaf/Projekte/toolkit/ui/ui/properties.h; sourceTree = "<absolute>"; };
+ ED6581522CFF3CA000F5402F /* range.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = range.h; path = /Users/olaf/Projekte/toolkit/ui/ui/range.h; sourceTree = "<absolute>"; };
+ ED6581532CFF3CA000F5402F /* stock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = stock.h; path = /Users/olaf/Projekte/toolkit/ui/ui/stock.h; sourceTree = "<absolute>"; };
+ ED6581542CFF3CA000F5402F /* text.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = text.h; path = /Users/olaf/Projekte/toolkit/ui/ui/text.h; sourceTree = "<absolute>"; };
+ ED6581552CFF3CA000F5402F /* toolbar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolbar.h; path = /Users/olaf/Projekte/toolkit/ui/ui/toolbar.h; sourceTree = "<absolute>"; };
+ ED6581562CFF3CA000F5402F /* toolkit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolkit.h; path = /Users/olaf/Projekte/toolkit/ui/ui/toolkit.h; sourceTree = "<absolute>"; };
+ ED6581572CFF3CA000F5402F /* tree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = tree.h; path = /Users/olaf/Projekte/toolkit/ui/ui/tree.h; sourceTree = "<absolute>"; };
+ ED6581582CFF3CA000F5402F /* ui.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ui.h; path = /Users/olaf/Projekte/toolkit/ui/ui/ui.h; sourceTree = "<absolute>"; };
+ ED6581592CFF3CA000F5402F /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = window.h; path = /Users/olaf/Projekte/toolkit/ui/ui/window.h; sourceTree = "<absolute>"; };
+ ED65815A2CFF3EE900F5402F /* MainWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MainWindow.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/MainWindow.h; sourceTree = "<absolute>"; };
+ ED65815B2CFF3EE900F5402F /* MainWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MainWindow.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/MainWindow.m; sourceTree = "<absolute>"; };
+ ED65815D2CFF4BF200F5402F /* WindowManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WindowManager.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/WindowManager.h; sourceTree = "<absolute>"; };
+ ED65815E2CFF4BF200F5402F /* WindowManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = WindowManager.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/WindowManager.m; sourceTree = "<absolute>"; };
+ ED679B082E5B266C001D4F71 /* label.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = label.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/label.h; sourceTree = "<absolute>"; };
+ ED679B092E5B266C001D4F71 /* label.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = label.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/label.m; sourceTree = "<absolute>"; };
+ ED679B0B2E5B2FB0001D4F71 /* UiThread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = UiThread.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/UiThread.h; sourceTree = "<absolute>"; };
+ ED679B0C2E5B2FB0001D4F71 /* UiThread.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = UiThread.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/UiThread.m; sourceTree = "<absolute>"; };
+ ED6FB0372E95466F006C6E8E /* args.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = args.h; path = /Users/olaf/Projekte/toolkit/ui/common/args.h; sourceTree = "<absolute>"; };
+ ED6FB0382E95466F006C6E8E /* args.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = args.c; path = /Users/olaf/Projekte/toolkit/ui/common/args.c; sourceTree = "<absolute>"; };
+ ED6FB0392E95466F006C6E8E /* container.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = container.h; path = /Users/olaf/Projekte/toolkit/ui/common/container.h; sourceTree = "<absolute>"; };
+ ED6FB03A2E95466F006C6E8E /* container.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = container.c; path = /Users/olaf/Projekte/toolkit/ui/common/container.c; sourceTree = "<absolute>"; };
+ ED6FB03B2E95466F006C6E8E /* wrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = wrapper.h; path = /Users/olaf/Projekte/toolkit/ui/common/wrapper.h; sourceTree = "<absolute>"; };
+ ED6FB03C2E95466F006C6E8E /* wrapper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = wrapper.c; path = /Users/olaf/Projekte/toolkit/ui/common/wrapper.c; sourceTree = "<absolute>"; };
+ ED83C2BD2E8EA49200054B22 /* BoxContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BoxContainer.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/BoxContainer.h; sourceTree = "<absolute>"; };
+ ED83C2BE2E8EA49200054B22 /* BoxContainer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BoxContainer.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/BoxContainer.m; sourceTree = "<absolute>"; };
+ ED8687E32D999CF3002F3EC2 /* menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = menu.h; path = ../../../ui/cocoa/menu.h; sourceTree = SOURCE_ROOT; };
+ ED8687E42D999CF3002F3EC2 /* menu.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = menu.m; path = ../../../ui/cocoa/menu.m; sourceTree = SOURCE_ROOT; };
+ ED895DA82EA0CACC00040078 /* TabView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TabView.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/TabView.h; sourceTree = "<absolute>"; };
+ ED895DA92EA0CACC00040078 /* TabView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TabView.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/TabView.m; sourceTree = "<absolute>"; };
+ ED99F0482E5CBD2E00A4CC97 /* widget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = widget.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/widget.h; sourceTree = "<absolute>"; };
+ ED99F0492E5CBD2E00A4CC97 /* widget.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = widget.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/widget.m; sourceTree = "<absolute>"; };
+ ED99F04B2E5CBE5000A4CC97 /* webview.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = webview.h; path = /Users/olaf/Projekte/toolkit/ui/ui/webview.h; sourceTree = "<absolute>"; };
+ ED99F04C2E5CBE5000A4CC97 /* widget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = widget.h; path = /Users/olaf/Projekte/toolkit/ui/ui/widget.h; sourceTree = "<absolute>"; };
+ EDB452C12E302C65006FB12D /* image.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = image.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/image.h; sourceTree = "<absolute>"; };
+ EDB452C22E302C65006FB12D /* image.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = image.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/image.m; sourceTree = "<absolute>"; };
+ EDBC4D252EAD4BB0005CDF38 /* webview.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = webview.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/webview.h; sourceTree = "<absolute>"; };
+ EDBC4D262EAD4BB0005CDF38 /* webview.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = webview.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/webview.m; sourceTree = "<absolute>"; };
+ EDC315A32E9A736900403776 /* json.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = json.h; path = /Users/olaf/Projekte/toolkit/ucx/cx/json.h; sourceTree = "<absolute>"; };
+ EDC315A42E9A736900403776 /* properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = properties.h; path = /Users/olaf/Projekte/toolkit/ucx/cx/properties.h; sourceTree = "<absolute>"; };
+ EDC315A52E9A736900403776 /* streams.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = streams.h; path = /Users/olaf/Projekte/toolkit/ucx/cx/streams.h; sourceTree = "<absolute>"; };
+ EDC315A62E9A739300403776 /* json.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = json.c; path = /Users/olaf/Projekte/toolkit/ucx/json.c; sourceTree = "<absolute>"; };
+ EDC315A72E9A739300403776 /* properties.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = properties.c; path = /Users/olaf/Projekte/toolkit/ucx/properties.c; sourceTree = "<absolute>"; };
+ EDC315A82E9A739300403776 /* streams.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = streams.c; path = /Users/olaf/Projekte/toolkit/ucx/streams.c; sourceTree = "<absolute>"; };
+ EDCD22252E59EEF5000612AF /* list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = list.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/list.h; sourceTree = "<absolute>"; };
+ EDCD22262E59EEF5000612AF /* list.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = list.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/list.m; sourceTree = "<absolute>"; };
+ EDCD22332E59F3B1000612AF /* ListDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ListDataSource.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/ListDataSource.h; sourceTree = "<absolute>"; };
+ EDCD22342E59F3B1000612AF /* ListDataSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ListDataSource.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/ListDataSource.m; sourceTree = "<absolute>"; };
+ EDCD22362E5A160A000612AF /* ListDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ListDelegate.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/ListDelegate.h; sourceTree = "<absolute>"; };
+ EDCD22372E5A160A000612AF /* ListDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ListDelegate.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/ListDelegate.m; sourceTree = "<absolute>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+ ED6580AE2CFF122700F5402F /* toolkit */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ path = toolkit;
+ sourceTree = "<group>";
+ };
+/* End PBXFileSystemSynchronizedRootGroup section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ ED6580A92CFF122700F5402F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ ED6580A32CFF122700F5402F = {
+ isa = PBXGroup;
+ children = (
+ ED6581472CFF3C8300F5402F /* public */,
+ ED65812E2CFF1A7200F5402F /* cocoa */,
+ ED6580F82CFF1A1200F5402F /* ucx */,
+ ED6580D92CFF19DB00F5402F /* common */,
+ ED6580AE2CFF122700F5402F /* toolkit */,
+ ED6580AD2CFF122700F5402F /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ ED6580AD2CFF122700F5402F /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ ED6580AC2CFF122700F5402F /* toolkit.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ ED6580D92CFF19DB00F5402F /* common */ = {
+ isa = PBXGroup;
+ children = (
+ ED6FB0372E95466F006C6E8E /* args.h */,
+ ED6FB0382E95466F006C6E8E /* args.c */,
+ ED6FB0392E95466F006C6E8E /* container.h */,
+ ED6FB03A2E95466F006C6E8E /* container.c */,
+ ED6FB03B2E95466F006C6E8E /* wrapper.h */,
+ ED6FB03C2E95466F006C6E8E /* wrapper.c */,
+ ED6580DA2CFF19F900F5402F /* condvar.h */,
+ ED6580DB2CFF19F900F5402F /* condvar.c */,
+ ED6580DC2CFF19F900F5402F /* context.h */,
+ ED6580DD2CFF19F900F5402F /* context.c */,
+ ED6580DE2CFF19F900F5402F /* document.h */,
+ ED6580DF2CFF19F900F5402F /* document.c */,
+ ED6580E02CFF19F900F5402F /* menu.h */,
+ ED6580E12CFF19F900F5402F /* menu.c */,
+ ED6580E22CFF19F900F5402F /* object.h */,
+ ED6580E32CFF19F900F5402F /* object.c */,
+ ED6580E42CFF19F900F5402F /* properties.h */,
+ ED6580E52CFF19F900F5402F /* properties.c */,
+ ED6580E62CFF19F900F5402F /* threadpool.h */,
+ ED6580E72CFF19F900F5402F /* threadpool.c */,
+ ED6580E82CFF19F900F5402F /* toolbar.h */,
+ ED6580E92CFF19F900F5402F /* toolbar.c */,
+ ED6580EA2CFF19F900F5402F /* types.h */,
+ ED6580EB2CFF19F900F5402F /* types.c */,
+ ED6580EC2CFF19F900F5402F /* ucx_properties.h */,
+ ED6580ED2CFF19F900F5402F /* ucx_properties.c */,
+ );
+ path = common;
+ sourceTree = "<group>";
+ };
+ ED6580F82CFF1A1200F5402F /* ucx */ = {
+ isa = PBXGroup;
+ children = (
+ EDC315A62E9A739300403776 /* json.c */,
+ EDC315A72E9A739300403776 /* properties.c */,
+ EDC315A82E9A739300403776 /* streams.c */,
+ ED6580F92CFF1A3000F5402F /* allocator.c */,
+ ED6580FA2CFF1A3000F5402F /* array_list.c */,
+ ED6580FB2CFF1A3000F5402F /* buffer.c */,
+ ED6580FC2CFF1A3000F5402F /* compare.c */,
+ ED6581102CFF1A3000F5402F /* cx */,
+ ED6581112CFF1A3000F5402F /* hash_key.c */,
+ ED6581122CFF1A3000F5402F /* hash_map.c */,
+ ED6581132CFF1A3000F5402F /* iterator.c */,
+ ED6581142CFF1A3000F5402F /* linked_list.c */,
+ ED6581152CFF1A3000F5402F /* list.c */,
+ ED6581162CFF1A3000F5402F /* map.c */,
+ ED6581172CFF1A3000F5402F /* mempool.c */,
+ ED6581182CFF1A3000F5402F /* printf.c */,
+ ED6581192CFF1A3000F5402F /* string.c */,
+ ED65811B2CFF1A3000F5402F /* tree.c */,
+ );
+ path = ucx;
+ sourceTree = "<group>";
+ };
+ ED6581102CFF1A3000F5402F /* cx */ = {
+ isa = PBXGroup;
+ children = (
+ ED6580FD2CFF1A3000F5402F /* allocator.h */,
+ ED6580FE2CFF1A3000F5402F /* array_list.h */,
+ ED6580FF2CFF1A3000F5402F /* buffer.h */,
+ ED6581002CFF1A3000F5402F /* collection.h */,
+ ED6581012CFF1A3000F5402F /* common.h */,
+ ED6581032CFF1A3000F5402F /* compare.h */,
+ ED6581042CFF1A3000F5402F /* hash_key.h */,
+ ED6581052CFF1A3000F5402F /* hash_map.h */,
+ ED6581062CFF1A3000F5402F /* iterator.h */,
+ ED6581072CFF1A3000F5402F /* linked_list.h */,
+ ED6581082CFF1A3000F5402F /* list.h */,
+ ED6581092CFF1A3000F5402F /* map.h */,
+ ED65810A2CFF1A3000F5402F /* mempool.h */,
+ ED65810B2CFF1A3000F5402F /* printf.h */,
+ ED65810C2CFF1A3000F5402F /* string.h */,
+ ED65810D2CFF1A3000F5402F /* test.h */,
+ ED65810E2CFF1A3000F5402F /* tree.h */,
+ EDC315A32E9A736900403776 /* json.h */,
+ EDC315A42E9A736900403776 /* properties.h */,
+ EDC315A52E9A736900403776 /* streams.h */,
+ );
+ name = cx;
+ path = /Users/olaf/Projekte/toolkit/ucx/cx;
+ sourceTree = "<absolute>";
+ };
+ ED65812E2CFF1A7200F5402F /* cocoa */ = {
+ isa = PBXGroup;
+ children = (
+ EDBC4D252EAD4BB0005CDF38 /* webview.h */,
+ EDBC4D262EAD4BB0005CDF38 /* webview.m */,
+ ED895DA82EA0CACC00040078 /* TabView.h */,
+ ED895DA92EA0CACC00040078 /* TabView.m */,
+ ED83C2BD2E8EA49200054B22 /* BoxContainer.h */,
+ ED83C2BE2E8EA49200054B22 /* BoxContainer.m */,
+ ED18C9212E76CA5500B64EA5 /* entry.h */,
+ ED18C9222E76CA5500B64EA5 /* entry.m */,
+ ED99F0482E5CBD2E00A4CC97 /* widget.h */,
+ ED99F0492E5CBD2E00A4CC97 /* widget.m */,
+ ED679B0B2E5B2FB0001D4F71 /* UiThread.h */,
+ ED679B0C2E5B2FB0001D4F71 /* UiThread.m */,
+ ED679B082E5B266C001D4F71 /* label.h */,
+ ED679B092E5B266C001D4F71 /* label.m */,
+ EDCD22362E5A160A000612AF /* ListDelegate.h */,
+ EDCD22372E5A160A000612AF /* ListDelegate.m */,
+ EDCD22332E59F3B1000612AF /* ListDataSource.h */,
+ EDCD22342E59F3B1000612AF /* ListDataSource.m */,
+ EDCD22252E59EEF5000612AF /* list.h */,
+ EDCD22262E59EEF5000612AF /* list.m */,
+ ED2F55AC2E34FAD800A84793 /* Toolbar.h */,
+ ED2F55AD2E34FAD800A84793 /* Toolbar.m */,
+ EDB452C12E302C65006FB12D /* image.h */,
+ EDB452C22E302C65006FB12D /* image.m */,
+ ED8687E32D999CF3002F3EC2 /* menu.h */,
+ ED8687E42D999CF3002F3EC2 /* menu.m */,
+ ED52BFAE2D86FC5D00FD8BE5 /* text.h */,
+ ED52BFAF2D86FC5D00FD8BE5 /* text.m */,
+ ED65815D2CFF4BF200F5402F /* WindowManager.h */,
+ ED65815E2CFF4BF200F5402F /* WindowManager.m */,
+ ED65815A2CFF3EE900F5402F /* MainWindow.h */,
+ ED65815B2CFF3EE900F5402F /* MainWindow.m */,
+ ED65813B2CFF3BCE00F5402F /* button.h */,
+ ED65813C2CFF3BCE00F5402F /* button.m */,
+ ED65813D2CFF3BCE00F5402F /* Container.h */,
+ ED65813E2CFF3BCE00F5402F /* Container.m */,
+ ED65813F2CFF3BCE00F5402F /* GridLayout.h */,
+ ED6581402CFF3BCE00F5402F /* GridLayout.m */,
+ ED6581412CFF3BCE00F5402F /* window.h */,
+ ED6581422CFF3BCE00F5402F /* window.m */,
+ ED6581352CFF287300F5402F /* EventData.h */,
+ ED6581362CFF287300F5402F /* EventData.m */,
+ ED6581322CFF1F1900F5402F /* AppDelegate.h */,
+ ED6581332CFF1F1900F5402F /* AppDelegate.m */,
+ ED65812F2CFF1A8800F5402F /* toolkit.h */,
+ ED6581302CFF1A8800F5402F /* toolkit.m */,
+ );
+ path = cocoa;
+ sourceTree = "<group>";
+ };
+ ED6581472CFF3C8300F5402F /* public */ = {
+ isa = PBXGroup;
+ children = (
+ ED99F04B2E5CBE5000A4CC97 /* webview.h */,
+ ED99F04C2E5CBE5000A4CC97 /* widget.h */,
+ ED6581482CFF3CA000F5402F /* button.h */,
+ ED6581492CFF3CA000F5402F /* container.h */,
+ ED65814A2CFF3CA000F5402F /* display.h */,
+ ED65814B2CFF3CA000F5402F /* dnd.h */,
+ ED65814C2CFF3CA000F5402F /* entry.h */,
+ ED65814D2CFF3CA000F5402F /* graphics.h */,
+ ED65814E2CFF3CA000F5402F /* icons.h */,
+ ED65814F2CFF3CA000F5402F /* image.h */,
+ ED6581502CFF3CA000F5402F /* menu.h */,
+ ED6581512CFF3CA000F5402F /* properties.h */,
+ ED6581522CFF3CA000F5402F /* range.h */,
+ ED6581532CFF3CA000F5402F /* stock.h */,
+ ED6581542CFF3CA000F5402F /* text.h */,
+ ED6581552CFF3CA000F5402F /* toolbar.h */,
+ ED6581562CFF3CA000F5402F /* toolkit.h */,
+ ED6581572CFF3CA000F5402F /* tree.h */,
+ ED6581582CFF3CA000F5402F /* ui.h */,
+ ED6581592CFF3CA000F5402F /* window.h */,
+ );
+ path = public;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ ED6580AB2CFF122700F5402F /* toolkit */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = ED6580BC2CFF122800F5402F /* Build configuration list for PBXNativeTarget "toolkit" */;
+ buildPhases = (
+ ED6580A82CFF122700F5402F /* Sources */,
+ ED6580A92CFF122700F5402F /* Frameworks */,
+ ED6580AA2CFF122700F5402F /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ fileSystemSynchronizedGroups = (
+ ED6580AE2CFF122700F5402F /* toolkit */,
+ );
+ name = toolkit;
+ packageProductDependencies = (
+ );
+ productName = toolkit;
+ productReference = ED6580AC2CFF122700F5402F /* toolkit.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ ED6580A42CFF122700F5402F /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastUpgradeCheck = 1610;
+ TargetAttributes = {
+ ED6580AB2CFF122700F5402F = {
+ CreatedOnToolsVersion = 16.1;
+ };
+ };
+ };
+ buildConfigurationList = ED6580A72CFF122700F5402F /* Build configuration list for PBXProject "toolkit" */;
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = ED6580A32CFF122700F5402F;
+ minimizedProjectReferenceProxies = 1;
+ preferredProjectObjectVersion = 77;
+ productRefGroup = ED6580AD2CFF122700F5402F /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ ED6580AB2CFF122700F5402F /* toolkit */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ ED6580AA2CFF122700F5402F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ ED6580A82CFF122700F5402F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ ED679B0A2E5B266C001D4F71 /* label.m in Sources */,
+ ED6FB03D2E95466F006C6E8E /* args.c in Sources */,
+ ED6FB03E2E95466F006C6E8E /* container.c in Sources */,
+ ED6FB03F2E95466F006C6E8E /* wrapper.c in Sources */,
+ ED6580EE2CFF19F900F5402F /* context.c in Sources */,
+ ED6580EF2CFF19F900F5402F /* menu.c in Sources */,
+ EDCD22352E59F3B1000612AF /* ListDataSource.m in Sources */,
+ ED6580F02CFF19F900F5402F /* threadpool.c in Sources */,
+ EDB452C32E302C65006FB12D /* image.m in Sources */,
+ ED6580F12CFF19F900F5402F /* condvar.c in Sources */,
+ EDCD22272E59EEF5000612AF /* list.m in Sources */,
+ ED65815F2CFF4BF200F5402F /* WindowManager.m in Sources */,
+ ED6580F22CFF19F900F5402F /* document.c in Sources */,
+ ED65811D2CFF1A3000F5402F /* linked_list.c in Sources */,
+ ED2F55AE2E34FAD800A84793 /* Toolbar.m in Sources */,
+ ED65811E2CFF1A3000F5402F /* tree.c in Sources */,
+ ED6581202CFF1A3000F5402F /* mempool.c in Sources */,
+ EDC315A92E9A739300403776 /* json.c in Sources */,
+ EDC315AA2E9A739300403776 /* properties.c in Sources */,
+ EDC315AB2E9A739300403776 /* streams.c in Sources */,
+ ED6581212CFF1A3000F5402F /* map.c in Sources */,
+ ED6581222CFF1A3000F5402F /* hash_map.c in Sources */,
+ ED6581232CFF1A3000F5402F /* array_list.c in Sources */,
+ ED6581242CFF1A3000F5402F /* list.c in Sources */,
+ ED18C9232E76CA5500B64EA5 /* entry.m in Sources */,
+ ED65815C2CFF3EE900F5402F /* MainWindow.m in Sources */,
+ ED99F04A2E5CBD2E00A4CC97 /* widget.m in Sources */,
+ ED6581252CFF1A3000F5402F /* compare.c in Sources */,
+ ED6581262CFF1A3000F5402F /* hash_key.c in Sources */,
+ EDBC4D272EAD4BB0005CDF38 /* webview.m in Sources */,
+ ED6581272CFF1A3000F5402F /* iterator.c in Sources */,
+ ED6581282CFF1A3000F5402F /* string.c in Sources */,
+ ED6581312CFF1A8800F5402F /* toolkit.m in Sources */,
+ ED6581342CFF1F1900F5402F /* AppDelegate.m in Sources */,
+ ED679B0D2E5B2FB0001D4F71 /* UiThread.m in Sources */,
+ ED6581292CFF1A3000F5402F /* allocator.c in Sources */,
+ ED52BFB02D86FC5D00FD8BE5 /* text.m in Sources */,
+ ED6581432CFF3BCE00F5402F /* window.m in Sources */,
+ ED6581442CFF3BCE00F5402F /* button.m in Sources */,
+ ED6581452CFF3BCE00F5402F /* Container.m in Sources */,
+ ED6581462CFF3BCE00F5402F /* GridLayout.m in Sources */,
+ ED6581392CFF287300F5402F /* EventData.m in Sources */,
+ ED65812B2CFF1A3000F5402F /* buffer.c in Sources */,
+ ED895DAA2EA0CACC00040078 /* TabView.m in Sources */,
+ ED65812C2CFF1A3000F5402F /* printf.c in Sources */,
+ ED6580F32CFF19F900F5402F /* object.c in Sources */,
+ ED6580F42CFF19F900F5402F /* toolbar.c in Sources */,
+ ED6580F52CFF19F900F5402F /* types.c in Sources */,
+ ED8687E52D999CF3002F3EC2 /* menu.m in Sources */,
+ ED6580F62CFF19F900F5402F /* properties.c in Sources */,
+ ED83C2BF2E8EA49200054B22 /* BoxContainer.m in Sources */,
+ EDCD22382E5A160A000612AF /* ListDelegate.m in Sources */,
+ ED6580F72CFF19F900F5402F /* ucx_properties.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ ED6580BA2CFF122800F5402F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = ../../../ucx;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 15.1;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ ED6580BB2CFF122800F5402F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = ../../../ucx;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 15.1;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ ED6580BD2CFF122800F5402F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = toolkit/toolkit.entitlements;
+ CODE_SIGN_STYLE = Manual;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ HEADER_SEARCH_PATHS = (
+ ../../../ucx,
+ ../../../ui,
+ );
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INFOPLIST_KEY_NSMainNibFile = MainMenu;
+ INFOPLIST_KEY_NSPrincipalClass = NSApplication;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ OTHER_CFLAGS = "-DUI_COCOA";
+ PRODUCT_BUNDLE_IDENTIFIER = de.unixwork.toolkit;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ };
+ name = Debug;
+ };
+ ED6580BE2CFF122800F5402F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = toolkit/toolkit.entitlements;
+ CODE_SIGN_STYLE = Manual;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ HEADER_SEARCH_PATHS = (
+ ../../../ucx,
+ ../../../ui,
+ );
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INFOPLIST_KEY_NSMainNibFile = MainMenu;
+ INFOPLIST_KEY_NSPrincipalClass = NSApplication;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ OTHER_CFLAGS = "-DUI_COCOA";
+ PRODUCT_BUNDLE_IDENTIFIER = de.unixwork.toolkit;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ ED6580A72CFF122700F5402F /* Build configuration list for PBXProject "toolkit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ ED6580BA2CFF122800F5402F /* Debug */,
+ ED6580BB2CFF122800F5402F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ ED6580BC2CFF122800F5402F /* Build configuration list for PBXNativeTarget "toolkit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ ED6580BD2CFF122800F5402F /* Debug */,
+ ED6580BE2CFF122800F5402F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = ED6580A42CFF122700F5402F /* Project object */;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1610"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES"
+ buildArchitectures = "Automatic">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "ED6580AB2CFF122700F5402F"
+ BuildableName = "toolkit.app"
+ BlueprintName = "toolkit"
+ ReferencedContainer = "container:toolkit.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "ED6580AB2CFF122700F5402F"
+ BuildableName = "toolkit.app"
+ BlueprintName = "toolkit"
+ ReferencedContainer = "container:toolkit.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "ED6580AB2CFF122700F5402F"
+ BuildableName = "toolkit.app"
+ BlueprintName = "toolkit"
+ ReferencedContainer = "container:toolkit.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
--- /dev/null
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="toolkit" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="toolkit" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About toolkit" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide toolkit" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit toolkit" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="File" id="dMs-cI-mzQ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Format" id="jxT-CU-nIS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Help" id="wpr-3q-Mcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+ <items>
+ <menuItem title="toolkit Help" keyEquivalent="?" id="FKE-Sm-Kum">
+ <connections>
+ <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ <point key="canvasLocation" x="200" y="121"/>
+ </menu>
+ </objects>
+</document>
--- /dev/null
+/*
+ * 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.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#include <ui/ui.h>
+
+#include <ui/webview.h>
+
+typedef struct WindowData {
+ UiInteger *tbtoggle;
+} WindowData;
+
+typedef struct MyDocument {
+ UiInteger *tbtoggle;
+ UiDouble *number;
+ UiList *list1;
+ UiList *list2;
+ UiList *sidebar_list;
+ UiList *sidebar_list2;
+ UiString *link;
+ UiGeneric *webview;
+} MyDocument;
+
+MyDocument* create_doc(void) {
+ MyDocument *doc = ui_document_new(sizeof(MyDocument));
+ UiContext *ctx = ui_document_context(doc);
+ doc->tbtoggle = ui_int_new(ctx, "tbtoggle");
+ doc->number = ui_double_new(ctx, "number");
+ doc->list1 = ui_list_new(ctx, "list1");
+ ui_list_append(doc->list1, "Item 1");
+ ui_list_append(doc->list1, "Item 2");
+ ui_list_append(doc->list1, "Item 3");
+ ui_list_append(doc->list1, "Item 4");
+
+ doc->list2 = ui_list_new(ctx, "list2");
+ ui_list_append(doc->list2, "Option 1");
+ ui_list_append(doc->list2, "Option 2");
+ ui_list_append(doc->list2, "Option 3");
+ ui_list_append(doc->list2, "Option 4");
+ ui_list_append(doc->list2, "Option 5");
+ ui_list_append(doc->list2, "Option 6");
+
+ doc->sidebar_list = ui_list_new(ctx, "sidebar_list");
+ ui_list_append(doc->sidebar_list, "Item 1");
+ ui_list_append(doc->sidebar_list, "Item 2");
+ ui_list_append(doc->sidebar_list, "Item 3");
+ ui_list_append(doc->sidebar_list, "Item 4");
+ ui_list_append(doc->sidebar_list, "Item 5");
+ ui_list_append(doc->sidebar_list, "Item 6");
+
+ doc->sidebar_list2 = ui_list_new(ctx, "sidebar_list2");
+ ui_list_append(doc->sidebar_list2, "Item 1");
+ ui_list_append(doc->sidebar_list2, "Item 2");
+ ui_list_append(doc->sidebar_list2, "Item 3");
+ ui_list_append(doc->sidebar_list2, "Item 4");
+
+ doc->link = ui_string_new(ctx, "link");
+ doc->webview = ui_generic_new(ctx, "webview");
+
+ return doc;
+}
+
+static int clickcount = 0;
+static void action_button(UiEvent *event, void *userdata) {
+ printf("button click %d\n", clickcount++);
+}
+
+static void toolbar_action(UiEvent *event, void *userdata) {
+ printf("toolbar item\n");
+ ui_dialog(event->obj, .title = "Error", .content = "Error Message", .input = TRUE, .button1_label = "Add", .button2_label = "Remove", .closebutton_label = "Close");
+}
+
+static void toolbar_action2(UiEvent *event, void *userdata) {
+ printf("toolbar item 2\n");
+ UiObject *obj = ui_dialog_window(event->obj, .title = "Dialog Window", .lbutton1 = "Cancel", .rbutton4 = "OK", .default_button = 4);
+
+ ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) {
+ ui_rlabel(obj, .label = "Label 1");
+ ui_textfield(obj, .value = NULL);
+ ui_newline(obj);
+
+ ui_rlabel(obj, .label = "Label 2");
+ ui_textfield(obj, .value = NULL);
+ ui_newline(obj);
+
+ ui_textarea(obj, .colspan = 2, .hexpand = TRUE, .hfill = TRUE, .vfill = TRUE, .vexpand = TRUE, .width = 600, .height = 300);
+ }
+
+ ui_show(obj);
+}
+
+static void toolbar_toggle(UiEvent *event, void *userdata) {
+ MyDocument *doc = event->document;
+ int i = (int)(doc ? ui_get(doc->tbtoggle) : -1);
+ printf("toolbar toggle button %d %d\n", event->intval, i);
+}
+
+static void action_menuitem(UiEvent *event, void *userdata) {
+ printf("menuitem\n");
+}
+
+static void action_list_selection(UiEvent *event, void *userdata) {
+ printf("selection\n");
+}
+
+static void action_list_activate(UiEvent *event, void *userdata) {
+ printf("activate\n");
+}
+
+static void* table_getvalue(void *elm, int col) {
+ return elm;
+}
+
+
+
+void application_startup(UiEvent *event, void *data) {
+ UiObject *obj = ui_splitview_window("My Window", TRUE);
+ //WindowData *wdata = ui_malloc(obj->ctx, sizeof(WindowData));
+ //wdata->tbtoggle = ui_int_new(obj->ctx, "tbtoggle");
+ //obj->window = wdata;
+ MyDocument *doc = create_doc();
+ ui_attach_document(obj->ctx, doc);
+
+ UiSubList sublist[2];
+ sublist[0].header = "iCloud";
+ sublist[0].value = doc->sidebar_list;
+ sublist[0].separator = FALSE;
+ sublist[0].varname = NULL;
+ sublist[0].userdata = NULL;
+ sublist[1].header = "Tags";
+ sublist[1].value = doc->sidebar_list2;
+ sublist[1].separator = FALSE;
+ sublist[1].varname = NULL;
+ sublist[1].userdata = NULL;
+ ui_sidebar(obj) {
+ ui_sourcelist(obj, .fill = TRUE, .sublists = sublist, .numsublists = 2);
+ }
+
+
+ ui_left_panel0(obj) {
+ ui_grid(obj, .margin_left = 10, .margin_right = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) {
+ ui_button(obj, .label = "left", .hexpand = TRUE, .hfill = TRUE);
+ ui_newline(obj);
+ ui_linkbutton(obj, .varname = "link", .hexpand = TRUE, .hfill = TRUE);
+ ui_newline(obj);
+
+ ui_frame(obj, .label = "Test", .margin_bottom = 10, .hfill = TRUE, .vfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .subcontainer = UI_CONTAINER_VBOX, .spacing = 10) {
+ ui_button(obj, .label = "Test Button");
+ ui_button(obj, .label = "Test Button");
+ ui_button(obj, .label = "Test Button");
+ ui_button(obj, .label = "Test Button");
+ ui_button(obj, .label = "Test Button");
+ }
+ }
+ }
+
+ ui_right_panel0(obj) {
+ ui_tabview(obj, .padding = 20, .spacing = 10, .margin_left = 10, .margin_right = 10, .margin_bottom = 10, .fill = TRUE) {
+ ui_tab(obj, "Tab 1") {
+ ui_button(obj, .label = "Test");
+ ui_textarea(obj, .fill = TRUE);
+ }
+ ui_tab(obj, "Tab 2") {
+ //ui_button(obj, .label = "Tab 2 Content");
+ ui_webview(obj, .fill = TRUE, .value = doc->webview);
+ ui_webview_load_url(doc->webview, "https://unixwork.de/");
+ }
+ }
+
+ /*
+ ui_scrolledwindow(obj, .margin_left = 10, .margin_right = 10, .fill = TRUE, .subcontainer = UI_CONTAINER_NO_SUB) {
+ ui_vbox(obj, .margin = 0) {
+ for(int i=0;i<50;i++) {
+ char label[32];
+ snprintf(label, 32, "Button %d", i);
+ ui_button(obj, .label = label);
+ }
+ }
+ }
+ */
+ }
+
+ ui_linkbutton_value_set(doc->link, "unixwork", "https://unixwork.de/");
+
+
+ UiModel *model = ui_model(obj->ctx, UI_STRING, "Column 0", UI_STRING, "Column 1", UI_STRING, "Column 2", -1);
+ model->columnsize[1] = -1;
+ //ui_grid(obj, .columnspacing = 10, .rowspacing = 10) {
+ //ui_table(obj, .fill = UI_ON, .varname = "list1", .model = model, .getvalue = table_getvalue, .onactivate = action_list_activate, .onselection = action_list_selection, .multiselection = TRUE);
+
+ //}
+
+ /*
+ UiModel *model = ui_model(obj->ctx, UI_STRING, "Column 0", UI_STRING, "Column 1", UI_STRING, "Column 2", -1);
+ model->columnsize[1] = -1;
+ ui_grid(obj, .columnspacing = 10, .rowspacing = 10) {
+ ui_table(obj, .fill = UI_ON, .varname = "list1", .model = model, .getvalue = table_getvalue, .onactivate = action_list_activate, .onselection = action_list_selection, .multiselection = TRUE);
+ }
+ */
+
+ ui_show(obj);
+}
+
+int main(int argc, char * argv[]) {
+ ui_init("app1", argc, argv);
+ ui_onstartup(application_startup, NULL);
+
+ // menu
+ ui_menu("File") {
+ ui_menuitem(.label = "Test", .onclick = action_menuitem);
+ }
+
+ ui_menu("Edit") {
+ ui_menuitem(.label = "Undo");
+ ui_menu_toggleitem(.label = "Checkbox");
+ ui_menuseparator();
+ ui_menu_radioitem(.label = "Option 1", .varname = "menuoption");
+ ui_menu_radioitem(.label = "Option 2", .varname = "menuoption");
+ ui_menu_radioitem(.label = "Option 3", .varname = "menuoption");
+ ui_menu_radioitem(.label = "Option 4", .varname = "menuoption");
+ ui_menuseparator();
+ ui_menu("Submenu") {
+ ui_menuitem(.label = "Subitem");
+ }
+ }
+
+ ui_toolbar_item("itemz", .label = "Test Z", .icon = "NSImageNameActionTemplate");
+ ui_toolbar_item("item0", .label = "Test 0", .icon = "NSImageNameActionTemplate");
+ ui_toolbar_item("item1", .label = "Test 1", .icon = "NSImageNameGoBackTemplate", .onclick = toolbar_action);
+ ui_toolbar_item("item2", .label = "Test 2", .icon = "NSImageNameGoForwardTemplate", .onclick = toolbar_action);
+ ui_toolbar_toggleitem("item3", .label = "Toggle", .icon = "NSImageNameActionTemplate", .varname = "tbtoggle", .onchange = toolbar_toggle);
+ ui_toolbar_menu("item4", .label = "Add", .icon = "NSImageNameAddTemplate") {
+ ui_menuitem(.label = "Test1", .onclick = action_menuitem);
+ ui_menu_toggleitem(.label = "Check");
+ }
+ ui_toolbar_item("item5", .label = "Dialog Window", .icon = "NSImageNameAddTemplate", .onclick = toolbar_action2);
+ ui_toolbar_item("item6", .label = "Test", .icon = "NSImageNameGoBackTemplate");
+
+ ui_toolbar_add_default("itemz", UI_TOOLBAR_SIDEBAR_LEFT);
+ ui_toolbar_add_default("item0", UI_TOOLBAR_SIDEBAR_RIGHT);
+ ui_toolbar_add_default("item1", UI_TOOLBAR_LEFT);
+ ui_toolbar_add_default("item2", UI_TOOLBAR_LEFT);
+ ui_toolbar_add_default("item3", UI_TOOLBAR_CENTER);
+ ui_toolbar_add_default("item4", UI_TOOLBAR_RIGHT);
+ ui_toolbar_add_default("item5", UI_TOOLBAR_RIGHTPANEL_LEFT);
+ ui_toolbar_add_default("item6", UI_TOOLBAR_RIGHTPANEL_RIGHT);
+
+
+ ui_main();
+ return 0;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <false/>
+ <key>com.apple.security.files.user-selected.read-only</key>
+ <true/>
+</dict>
+</plist>
# POSSIBILITY OF SUCH DAMAGE.
#
-BUILD_ROOT = ../
include ../config.mk
# list of source files
SRC += compare.c
SRC += hash_key.c
SRC += hash_map.c
+SRC += iterator.c
SRC += linked_list.c
+SRC += kv_list.c
SRC += list.c
SRC += map.c
SRC += printf.c
SRC += string.c
-SRC += streams.c
SRC += tree.c
+SRC += streams.c
SRC += properties.c
SRC += json.c
-OBJ = $(SRC:%.c=../build/ucx/%.$(OBJ_EXT))
+OBJ = $(SRC:%.c=../build/ucx/%$(OBJ_EXT))
-UCX_LIB = ../build/lib/libucx.$(LIB_EXT)
+UCX_LIB = ../build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)ucx$(LIB_EXT)
+UCX_SHLIB = ../build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)ucx$(SHLIB_EXT)
-all: ../build/ucx $(UCX_LIB)
+all: $(UCX_LIB) $(UCX_SHLIB)
$(UCX_LIB): $(OBJ)
- $(AR) $(ARFLAGS) $(UCX_LIB) $(OBJ)
+ $(AR) $(ARFLAGS) $@ $(OBJ)
+
+$(UCX_SHLIB): $(OBJ)
+ $(CC) -o $@ $(LDFLAGS) $(SHLIB_LDFLAGS) $(OBJ)
../build/ucx:
mkdir -p ../build/ucx
-../build/ucx/%.$(OBJ_EXT): %.c
- $(CC) $(CFLAGS) -o $@ -c $<
+../build/ucx/%$(OBJ_EXT): %.c
+ $(CC) $(CFLAGS) $(SHLIB_CFLAGS) -o $@ -c $<
NULL
};
const CxAllocator * const cxStdlibAllocator = &cx_stdlib_allocator;
-const CxAllocator * cxDefaultAllocator = cxStdlibAllocator;
+const CxAllocator * cxDefaultAllocator = &cx_stdlib_allocator;
int cx_reallocate_(
void **mem,
}
CxArrayReallocator cx_array_default_reallocator_impl = {
- cx_array_default_realloc, NULL, NULL, 0, 0
+ cx_array_default_realloc, NULL, NULL
};
CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl;
}
// retrieve the pointer to the actual allocator
- const CxAllocator *al = alloc->ptr1;
+ const CxAllocator *al = alloc->allocator;
// check if the array is still located on the stack
void *newmem;
- if (array == alloc->ptr2) {
+ if (array == alloc->stack_ptr) {
newmem = cxMalloc(al, n);
if (newmem != NULL && array != NULL) {
memcpy(newmem, array, old_capacity*elem_size);
struct cx_array_reallocator_s cx_array_reallocator(
const struct cx_allocator_s *allocator,
- const void *stackmem
+ const void *stack_ptr
) {
if (allocator == NULL) {
allocator = cxDefaultAllocator;
}
return (struct cx_array_reallocator_s) {
cx_array_advanced_realloc,
- (void*) allocator, (void*) stackmem,
- 0, 0
+ allocator, stack_ptr,
};
}
*target, oldcap, newcap, elem_size, reallocator
);
if (newmem == NULL) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
// repair src pointer, if necessary
return 0;
}
-int cx_array_insert_sorted(
+static int cx_array_insert_sorted_impl(
void **target,
size_t *size,
size_t *capacity,
const void *sorted_data,
size_t elem_size,
size_t elem_count,
- CxArrayReallocator *reallocator
+ CxArrayReallocator *reallocator,
+ bool allow_duplicates
) {
// assert pointers
assert(target != NULL);
// store some counts
size_t old_size = *size;
size_t old_capacity = *capacity;
+ // the necessary capacity is the worst case assumption, including duplicates
size_t needed_capacity = old_size + elem_count;
// if we need more than we have, try a reallocation
bptr,
cmp_func
);
+ // binary search gives us the smallest index;
+ // we also want to include equal elements here
+ while (si + copy_len < elem_count
+ && cmp_func(bptr, src+copy_len*elem_size) == 0) {
+ copy_len++;
+ }
// copy the source elements
- bytes_copied = copy_len * elem_size;
- memcpy(dest, src, bytes_copied);
- dest += bytes_copied;
- src += bytes_copied;
- si += copy_len;
+ if (copy_len > 0) {
+ if (allow_duplicates) {
+ // we can copy the entire chunk
+ bytes_copied = copy_len * elem_size;
+ memcpy(dest, src, bytes_copied);
+ dest += bytes_copied;
+ src += bytes_copied;
+ si += copy_len;
+ di += copy_len;
+ } else {
+ // first, check the end of the source chunk
+ // for being a duplicate of the bptr
+ const char *end_of_src = src + (copy_len - 1) * elem_size;
+ size_t skip_len = 0;
+ while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) {
+ end_of_src -= elem_size;
+ skip_len++;
+ copy_len--;
+ }
+ char *last = dest == *target ? NULL : dest - elem_size;
+ // then iterate through the source chunk
+ // and skip all duplicates with the last element in the array
+ size_t more_skipped = 0;
+ for (unsigned j = 0; j < copy_len; j++) {
+ if (last != NULL && cmp_func(last, src) == 0) {
+ // duplicate - skip
+ src += elem_size;
+ si++;
+ more_skipped++;
+ } else {
+ memcpy(dest, src, elem_size);
+ src += elem_size;
+ last = dest;
+ dest += elem_size;
+ si++;
+ di++;
+ }
+ }
+ // skip the previously identified elements as well
+ src += skip_len * elem_size;
+ si += skip_len;
+ skip_len += more_skipped;
+ // reduce the actual size by the number of skipped elements
+ *size -= skip_len;
+ }
+ }
// when all source elements are in place, we are done
if (si >= elem_count) break;
memmove(dest, bptr, bytes_copied);
dest += bytes_copied;
bptr += bytes_copied;
+ di += copy_len;
bi += copy_len;
}
- // still source elements left? simply append them
+ // still source elements left?
if (si < elem_count) {
- memcpy(dest, src, elem_size * (elem_count - si));
+ if (allow_duplicates) {
+ // duplicates allowed or nothing inserted yet: simply copy everything
+ memcpy(dest, src, elem_size * (elem_count - si));
+ } else {
+ if (dest != *target) {
+ // skip all source elements that equal the last element
+ char *last = dest - elem_size;
+ while (si < elem_count) {
+ if (last != NULL && cmp_func(last, src) == 0) {
+ src += elem_size;
+ si++;
+ (*size)--;
+ } else {
+ break;
+ }
+ }
+ }
+ // we must check the elements in the chunk one by one
+ while (si < elem_count) {
+ // find a chain of elements that can be copied
+ size_t copy_len = 1, skip_len = 0;
+ {
+ const char *left_src = src;
+ while (si + copy_len < elem_count) {
+ const char *right_src = left_src + elem_size;
+ int d = cmp_func(left_src, right_src);
+ if (d < 0) {
+ if (skip_len > 0) {
+ // new larger element found;
+ // handle it in the next cycle
+ break;
+ }
+ left_src += elem_size;
+ copy_len++;
+ } else if (d == 0) {
+ left_src += elem_size;
+ skip_len++;
+ } else {
+ break;
+ }
+ }
+ }
+ size_t bytes_copied = copy_len * elem_size;
+ memcpy(dest, src, bytes_copied);
+ dest += bytes_copied;
+ src += bytes_copied + skip_len * elem_size;
+ si += copy_len + skip_len;
+ di += copy_len;
+ *size -= skip_len;
+ }
+ }
}
- // still buffer elements left?
- // don't worry, we already moved them to the correct place
+ // buffered elements need to be moved when we skipped duplicates
+ size_t total_skipped = new_size - *size;
+ if (bi < new_size && total_skipped > 0) {
+ // move the remaining buffer to the end of the array
+ memmove(dest, bptr, elem_size * (new_size - bi));
+ }
return 0;
}
+int cx_array_insert_sorted(
+ void **target,
+ size_t *size,
+ size_t *capacity,
+ cx_compare_func cmp_func,
+ const void *sorted_data,
+ size_t elem_size,
+ size_t elem_count,
+ CxArrayReallocator *reallocator
+) {
+ return cx_array_insert_sorted_impl(target, size, capacity,
+ cmp_func, sorted_data, elem_size, elem_count, reallocator, true);
+}
+
+int cx_array_insert_unique(
+ void **target,
+ size_t *size,
+ size_t *capacity,
+ cx_compare_func cmp_func,
+ const void *sorted_data,
+ size_t elem_size,
+ size_t elem_count,
+ CxArrayReallocator *reallocator
+) {
+ return cx_array_insert_sorted_impl(target, size, capacity,
+ cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
+}
+
size_t cx_array_binary_search_inf(
const void *arr,
size_t size,
result = cmp_func(elem, arr_elem);
if (result == 0) {
// found it!
+ // check previous elements;
+ // when they are equal, report the smallest index
+ arr_elem -= elem_size;
+ while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) {
+ pivot_index--;
+ arr_elem -= elem_size;
+ }
return pivot_index;
} else if (result < 0) {
// element is smaller than pivot, continue search left
}
}
+static size_t cx_arl_insert_unique(
+ struct cx_list_s *list,
+ const void *sorted_data,
+ size_t n
+) {
+ // get a correctly typed pointer to the list
+ cx_array_list *arl = (cx_array_list *) list;
+
+ if (cx_array_insert_unique(
+ &arl->data,
+ &list->collection.size,
+ &arl->capacity,
+ list->collection.cmpfunc,
+ sorted_data,
+ list->collection.elem_size,
+ n,
+ &arl->reallocator
+ )) {
+ // array list implementation is "all or nothing"
+ return 0;
+ } else {
+ return n;
+ }
+}
+
static void *cx_arl_insert_element(
struct cx_list_s *list,
size_t index,
const void *elem,
int prepend
) {
- struct cx_list_s *list = iter->src_handle.m;
+ struct cx_list_s *list = iter->src_handle;
if (iter->index < list->collection.size) {
if (cx_arl_insert_element(list,
iter->index + 1 - prepend, elem) == NULL) {
static bool cx_arl_iter_valid(const void *it) {
const struct cx_iterator_s *iter = it;
- const struct cx_list_s *list = iter->src_handle.c;
+ const struct cx_list_s *list = iter->src_handle;
return iter->index < list->collection.size;
}
struct cx_iterator_s *iter = it;
if (iter->base.remove) {
iter->base.remove = false;
- cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL);
+ cx_arl_remove(iter->src_handle, iter->index, 1, NULL);
+ iter->elem_count--;
} else {
iter->index++;
iter->elem_handle =
((char *) iter->elem_handle)
- + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size;
+ + ((const struct cx_list_s *) iter->src_handle)->collection.elem_size;
}
}
static void cx_arl_iter_prev(void *it) {
struct cx_iterator_s *iter = it;
- const cx_array_list *list = iter->src_handle.c;
if (iter->base.remove) {
iter->base.remove = false;
- cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL);
+ cx_arl_remove(iter->src_handle, iter->index, 1, NULL);
+ iter->elem_count--;
}
iter->index--;
+ cx_array_list *list = iter->src_handle;
if (iter->index < list->base.collection.size) {
iter->elem_handle = ((char *) list->data)
+ iter->index * list->base.collection.elem_size;
struct cx_iterator_s iter;
iter.index = index;
- iter.src_handle.c = list;
+ iter.src_handle = (void*)list;
iter.elem_handle = cx_arl_at(list, index);
iter.elem_size = list->collection.elem_size;
iter.elem_count = list->collection.size;
iter.base.current = cx_arl_iter_current;
iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
iter.base.remove = false;
- iter.base.mutating = false;
+ iter.base.allow_remove = true;
return iter;
}
cx_arl_insert_element,
cx_arl_insert_array,
cx_arl_insert_sorted,
+ cx_arl_insert_unique,
cx_arl_insert_iter,
cx_arl_remove,
cx_arl_clear,
return cx_vcmp_uint64(a, b);
}
+int cx_vcmp_size(size_t a, size_t b) {
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_size(const void *i1, const void *i2) {
+ size_t a = *((const size_t *) i1);
+ size_t b = *((const size_t *) i2);
+ return cx_vcmp_size(a, b);
+}
+
int cx_vcmp_float(float a, float b) {
if (fabsf(a - b) < 1e-6f) {
return 0;
/**
* The allocator's malloc() implementation.
*/
- void *(*malloc)(
- void *data,
- size_t n
- );
+ void *(*malloc)(void *data, size_t n);
/**
* The allocator's realloc() implementation.
*/
- void *(*realloc)(
- void *data,
- void *mem,
- size_t n
- );
+ void *(*realloc)(void *data, void *mem, size_t n);
/**
* The allocator's calloc() implementation.
*/
- void *(*calloc)(
- void *data,
- size_t nmemb,
- size_t size
- );
+ void *(*calloc)(void *data, size_t nmemb, size_t size);
/**
* The allocator's free() implementation.
*/
- void (*free)(
- void *data,
- void *mem
- );
+ void (*free)(void *data, void *mem);
} cx_allocator_class;
/**
/**
* A pre-defined allocator using standard library malloc() etc.
*/
-cx_attr_export
-extern const CxAllocator * const cxStdlibAllocator;
+CX_EXPORT extern const CxAllocator * const cxStdlibAllocator;
/**
* The default allocator that is used by UCX.
* Initialized with cxStdlibAllocator, but you may change it.
*/
-cx_attr_export
-extern const CxAllocator * cxDefaultAllocator;
+CX_EXPORT extern const CxAllocator * cxDefaultAllocator;
/**
* Function pointer type for destructor functions.
* @param data an optional pointer to custom data
* @param memory a pointer to the object to destruct
*/
-typedef void (*cx_destructor_func2)(
- void *data,
- void *memory
-);
+typedef void (*cx_destructor_func2)(void *data, void *memory);
+
+
+/**
+ * Function pointer type for clone functions.
+ *
+ * A clone function is supposed to create a deep copy of the memory pointed to
+ * by the @p source pointer.
+ * If the @p target pointer is non-null, the clone function is supposed to store
+ * the copy into that memory region.
+ * Otherwise, the clone function shall use the specified @p allocator to create
+ * a new object.
+ *
+ * The return value of a clone function is always a pointer to the target
+ * memory region, or @c NULL if any allocation failed.
+ * A clone function SHOULD NOT fail for any other reason than an allocation
+ * failure.
+ *
+ * @param target the target memory or @c NULL, if memory shall be allocated
+ * @param source the source memory
+ * @param allocator the allocator that shall be used
+ * @param data optional additional data
+ * @return either the specified @p target, a pointer to the allocated memory,
+ * or @c NULL, if any error occurred
+ */
+typedef void*(cx_clone_func)(void *target, const void *source,
+ const CxAllocator *allocator, void *data);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
* @retval non-zero failure
* @see cx_reallocatearray()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_reallocate_(
- void **mem,
- size_t n
-);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_reallocate_(void **mem, size_t n);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
* @retval non-zero failure
* @see cx_reallocate()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_reallocatearray_(
- void **mem,
- size_t nmemb,
- size_t size
-);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
* @param mem a pointer to the block to free
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-void cxFree(
- const CxAllocator *allocator,
- void *mem
-);
+CX_EXPORT void cxFree(const CxAllocator *allocator, void *mem);
/**
* Allocate @p n bytes of memory.
* @param n the number of bytes
* @return a pointer to the allocated memory
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_malloc
-cx_attr_dealloc_ucx
-cx_attr_allocsize(2)
-cx_attr_export
-void *cxMalloc(
- const CxAllocator *allocator,
- size_t n
-);
+cx_attr_nodiscard cx_attr_nonnull
+cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
+CX_EXPORT void *cxMalloc(const CxAllocator *allocator, size_t n);
/**
* Reallocate the previously allocated block in @p mem, making the new block
* @p n bytes long.
- * This function may return the same pointer that was passed to it, if moving
+ * This function may return the same pointer passed to it if moving
* the memory was not necessary.
*
* @note Re-allocating a block allocated by a different allocator is undefined.
* @param n the new size in bytes
* @return a pointer to the reallocated memory
*/
-cx_attr_nodiscard
-cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx
-cx_attr_allocsize(3)
-cx_attr_export
-void *cxRealloc(
- const CxAllocator *allocator,
- void *mem,
- size_t n
-);
+cx_attr_nodiscard cx_attr_nonnull_arg(1)
+cx_attr_dealloc_ucx cx_attr_allocsize(3)
+CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
/**
* Reallocate the previously allocated block in @p mem, making the new block
* @p n bytes long.
- * This function may return the same pointer that was passed to it, if moving
+ * This function may return the same pointer passed to it if moving
* the memory was not necessary.
*
* The size is calculated by multiplying @p nemb and @p size.
- * If that multiplication overflows, this function returns @c NULL and @c errno
+ * If that multiplication overflows, this function returns @c NULL, and @c errno
* will be set.
*
* @note Re-allocating a block allocated by a different allocator is undefined.
* @param size the size of each element
* @return a pointer to the reallocated memory
*/
-cx_attr_nodiscard
-cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx
-cx_attr_allocsize(3, 4)
-cx_attr_export
-void *cxReallocArray(
- const CxAllocator *allocator,
- void *mem,
- size_t nmemb,
- size_t size
-);
+cx_attr_nodiscard cx_attr_nonnull_arg(1)
+cx_attr_dealloc_ucx cx_attr_allocsize(3, 4)
+CX_EXPORT void *cxReallocArray(const CxAllocator *allocator,
+ void *mem, size_t nmemb, size_t size);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
* @note Re-allocating a block allocated by a different allocator is undefined.
*
* @par Error handling
- * @c errno will be set, if the underlying realloc function does so.
+ * @c errno will be set if the underlying realloc function does so.
*
* @param allocator the allocator
* @param mem pointer to the pointer to allocated block
* @retval zero success
* @retval non-zero failure
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_export
-int cxReallocate_(
- const CxAllocator *allocator,
- void **mem,
- size_t n
-);
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
* @note Re-allocating a block allocated by a different allocator is undefined.
*
* @par Error handling
- * @c errno will be set, if the underlying realloc function does so.
+ * @c errno will be set if the underlying realloc function does so.
*
* @param allocator (@c CxAllocator*) the allocator
* @param mem (@c void**) pointer to the pointer to allocated block
* @retval zero success
* @retval non-zero on failure
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_export
-int cxReallocateArray_(
- const CxAllocator *allocator,
- void **mem,
- size_t nmemb,
- size_t size
-);
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT int cxReallocateArray_(const CxAllocator *allocator,
+ void **mem, size_t nmemb, size_t size);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
* @param size the size of each element in bytes
* @return a pointer to the allocated memory
*/
-cx_attr_nonnull_arg(1)
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc_ucx
-cx_attr_allocsize(2, 3)
-cx_attr_export
-void *cxCalloc(
- const CxAllocator *allocator,
- size_t nmemb,
- size_t size
-);
+cx_attr_nonnull_arg(1) cx_attr_nodiscard
+cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2, 3)
+CX_EXPORT void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size);
/**
* Allocate @p n bytes of memory and sets every byte to zero.
* @param n the number of bytes
* @return a pointer to the allocated memory
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_malloc
-cx_attr_dealloc_ucx
-cx_attr_allocsize(2)
-cx_attr_export
-void *cxZalloc(
- const CxAllocator *allocator,
- size_t n
-);
+cx_attr_nodiscard cx_attr_nonnull
+cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
+CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n);
/**
* Convenience macro that invokes cxMalloc() with the cxDefaultAllocator.
#endif
/**
- * The maximum item size in an array list that fits into stack buffer
- * when swapped.
+ * The maximum item size in an array list that fits into
+ * a stack buffer when swapped.
*/
-cx_attr_export
-extern const unsigned cx_array_swap_sbo_size;
+CX_EXPORT extern const unsigned cx_array_swap_sbo_size;
/**
* Declares variables for an array that can be used with the convenience macros.
/**
* Declares variables for an array that can be used with the convenience macros.
*
- * The size and capacity variables will have @c size_t type.
+ * The size and capacity variables will have type @c size_t.
* Use #CX_ARRAY_DECLARE_SIZED() to specify a different type.
*
* @par Examples
* const CxAllocator *al = // ...
* cx_array_initialize_a(al, myarray, 128);
* // ...
- * cxFree(al, myarray); // don't forget to free with same allocator
+ * cxFree(al, myarray); // remember to free with the same allocator
* @endcode
*
* @param allocator (@c CxAllocator*) the allocator
* Reallocates space for the given array.
*
* Implementations are not required to free the original array.
- * This allows reallocation of static memory by allocating heap memory
- * and copying the array contents. The information in the custom fields of
- * the referenced allocator can be used to track the state of the memory
- * or to transport other additional data.
+ * This allows reallocation of static or stack memory by allocating heap memory
+ * and copying the array contents; namely when @c stack_ptr in this struct
+ * is not @c NULL and @p array equals @c stack_ptr.
*
* @param array the array to reallocate
* @param old_capacity the old number of elements
* @param alloc a reference to this allocator
* @return a pointer to the reallocated memory or @c NULL on failure
*/
- cx_attr_nodiscard
- cx_attr_nonnull_arg(5)
- cx_attr_allocsize(3, 4)
- void *(*realloc)(
- void *array,
- size_t old_capacity,
- size_t new_capacity,
- size_t elem_size,
- struct cx_array_reallocator_s *alloc
- );
+ void *(*realloc)( void *array, size_t old_capacity, size_t new_capacity,
+ size_t elem_size, struct cx_array_reallocator_s *alloc);
/**
- * Custom data pointer.
+ * The allocator that shall be used for the reallocations.
*/
- void *ptr1;
+ const CxAllocator *allocator;
/**
- * Custom data pointer.
+ * Optional pointer to stack memory
+ * if the array is originally located on the stack.
*/
- void *ptr2;
- /**
- * Custom data integer.
- */
- size_t int1;
- /**
- * Custom data integer.
- */
- size_t int2;
+ const void *stack_ptr;
};
/**
/**
* A default array reallocator that is based on the cxDefaultAllocator.
*/
-cx_attr_export
-extern CxArrayReallocator *cx_array_default_reallocator;
+CX_EXPORT extern CxArrayReallocator *cx_array_default_reallocator;
/**
* Creates a new array reallocator.
*
* When @p allocator is @c NULL, the cxDefaultAllocator will be used.
*
- * When @p stackmem is not @c NULL, the reallocator is supposed to be used
- * @em only for the specific array that is initially located at @p stackmem.
- * When reallocation is needed, the reallocator checks, if the array is
- * still located at @p stackmem and copies the contents to the heap.
+ * When @p stack_ptr is not @c NULL, the reallocator is supposed to be used
+ * @em only for the specific array initially located at @p stack_ptr.
+ * When reallocation is needed, the reallocator checks if the array is
+ * still located at @p stack_ptr and copies the contents to the heap.
*
- * @note Invoking this function with both arguments @c NULL will return a
+ * @note Invoking this function with both arguments being @c NULL will return a
* reallocator that behaves like #cx_array_default_reallocator.
*
* @param allocator the allocator this reallocator shall be based on
- * @param stackmem the address of the array when the array is initially located
- * on the stack or shall not reallocated in place
+ * @param stack_ptr the address of the array when the array is initially located
+ * on the stack or shall not reallocate in place
* @return an array reallocator
*/
-cx_attr_export
-CxArrayReallocator cx_array_reallocator(
- const struct cx_allocator_s *allocator,
- const void *stackmem
-);
+CX_EXPORT CxArrayReallocator cx_array_reallocator(
+ const struct cx_allocator_s *allocator, const void *stack_ptr);
/**
* Reserves memory for additional elements.
*
* The @p width in bytes refers to the size and capacity.
* Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64 bit
+ * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
* architecture. If set to zero, the native word width is used.
*
* @param array a pointer to the target array
* @see cx_array_reallocator()
*/
cx_attr_nonnull_arg(1, 2, 3)
-cx_attr_export
-int cx_array_reserve(
- void **array,
- void *size,
- void *capacity,
- unsigned width,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-);
+CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity,
+ unsigned width, size_t elem_size, size_t elem_count,
+ CxArrayReallocator *reallocator);
/**
* Copies elements from one array to another.
* The elements are copied to the @p target array at the specified @p index,
* overwriting possible elements. The @p index does not need to be in range of
* the current array @p size. If the new index plus the number of elements added
- * would extend the array's size, the remaining @p capacity is used.
+ * extends the array's size, the remaining @p capacity is used.
*
* If the @p capacity is also insufficient to hold the new data, a reallocation
* attempt is made with the specified @p reallocator.
*
* The @p width in bytes refers to the size and capacity.
* Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64 bit
+ * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
* architecture. If set to zero, the native word width is used.
*
* @param target a pointer to the target array
* @see cx_array_reallocator()
*/
cx_attr_nonnull_arg(1, 2, 3, 6)
-cx_attr_export
-int cx_array_copy(
- void **target,
- void *size,
- void *capacity,
- unsigned width,
- size_t index,
- const void *src,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-);
+CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width,
+ size_t index, const void *src, size_t elem_size, size_t elem_count,
+ CxArrayReallocator *reallocator);
/**
* Convenience macro that uses cx_array_copy() with a default layout and
* @retval non-zero failure
*/
cx_attr_nonnull_arg(1, 2, 3, 5)
-cx_attr_export
-int cx_array_insert_sorted(
- void **target,
- size_t *size,
- size_t *capacity,
- cx_compare_func cmp_func,
- const void *src,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-);
+CX_EXPORT int cx_array_insert_sorted(void **target, size_t *size, size_t *capacity,
+ cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count,
+ CxArrayReallocator *reallocator);
/**
* Inserts an element into a sorted array.
* If the target array is not already sorted with respect
* to the specified @p cmp_func, the behavior is undefined.
*
- * If the capacity is insufficient to hold the new data, a reallocation
+ * If the capacity is not enough to hold the new data, a reallocation
* attempt is made.
*
* The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
#define cx_array_simple_insert_sorted(array, src, n, cmp_func) \
cx_array_simple_insert_sorted_a(NULL, array, src, n, cmp_func)
+
+/**
+ * Inserts a sorted array into another sorted array, avoiding duplicates.
+ *
+ * If either the target or the source array is not already sorted with respect
+ * to the specified @p cmp_func, the behavior is undefined.
+ *
+ * If the capacity is insufficient to hold the new data, a reallocation
+ * attempt is made.
+ * You can create your own reallocator by hand, use #cx_array_default_reallocator,
+ * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ *
+ * @param target a pointer to the target array
+ * @param size a pointer to the size of the target array
+ * @param capacity a pointer to the capacity of the target array
+ * @param cmp_func the compare function for the elements
+ * @param src the source array
+ * @param elem_size the size of one element
+ * @param elem_count the number of elements to insert
+ * @param reallocator the array reallocator to use
+ * (@c NULL defaults to #cx_array_default_reallocator)
+ * @retval zero success
+ * @retval non-zero failure
+ */
+cx_attr_nonnull_arg(1, 2, 3, 5)
+CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capacity,
+ cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count,
+ CxArrayReallocator *reallocator);
+
+/**
+ * Inserts an element into a sorted array if it does not exist.
+ *
+ * If the target array is not already sorted with respect
+ * to the specified @p cmp_func, the behavior is undefined.
+ *
+ * If the capacity is insufficient to hold the new data, a reallocation
+ * attempt is made.
+ *
+ * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
+ * It is important, however, that @p size and @p capacity are pointers to
+ * variables of the same type.
+ *
+ * @param target (@c void**) a pointer to the target array
+ * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
+ * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
+ * @param elem_size (@c size_t) the size of one element
+ * @param elem (@c void*) a pointer to the element to add
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @retval zero success (also when the element was already present)
+ * @retval non-zero failure
+ */
+#define cx_array_add_unique(target, size, capacity, elem_size, elem, cmp_func, reallocator) \
+ cx_array_insert_unique((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator)
+
+/**
+ * Convenience macro for cx_array_add_unique() with a default
+ * layout and the specified reallocator.
+ *
+ * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_add_unique()
+ */
+#define cx_array_simple_add_unique_a(reallocator, array, elem, cmp_func) \
+ cx_array_add_unique(&array, &(array##_size), &(array##_capacity), \
+ sizeof((array)[0]), &(elem), cmp_func, reallocator)
+
+/**
+ * Convenience macro for cx_array_add_unique() with a default
+ * layout and the default reallocator.
+ *
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_add_unique_a()
+ */
+#define cx_array_simple_add_unique(array, elem, cmp_func) \
+ cx_array_simple_add_unique_a(NULL, array, elem, cmp_func)
+
+/**
+ * Convenience macro for cx_array_insert_unique() with a default
+ * layout and the specified reallocator.
+ *
+ * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param src (@c void*) pointer to the source array
+ * @param n (@c size_t) number of elements in the source array
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_insert_unique()
+ */
+#define cx_array_simple_insert_unique_a(reallocator, array, src, n, cmp_func) \
+ cx_array_insert_unique((void**)(&array), &(array##_size), &(array##_capacity), \
+ cmp_func, src, sizeof((array)[0]), n, reallocator)
+
+/**
+ * Convenience macro for cx_array_insert_unique() with a default
+ * layout and the default reallocator.
+ *
+ * @param array the name of the array (NOT a pointer or alias to the array)
+ * @param src (@c void*) pointer to the source array
+ * @param n (@c size_t) number of elements in the source array
+ * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @retval zero success
+ * @retval non-zero failure
+ * @see CX_ARRAY_DECLARE()
+ * @see cx_array_simple_insert_unique_a()
+ */
+#define cx_array_simple_insert_unique(array, src, n, cmp_func) \
+ cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func)
+
/**
* Searches the largest lower bound in a sorted array.
*
* @see cx_array_binary_search()
*/
cx_attr_nonnull
-cx_attr_export
-size_t cx_array_binary_search_inf(
- const void *arr,
- size_t size,
- size_t elem_size,
- const void *elem,
- cx_compare_func cmp_func
-);
+CX_EXPORT size_t cx_array_binary_search_inf(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func cmp_func);
/**
* Searches an item in a sorted array.
* @see cx_array_binary_search_sup()
*/
cx_attr_nonnull
-cx_attr_export
-size_t cx_array_binary_search(
- const void *arr,
- size_t size,
- size_t elem_size,
- const void *elem,
- cx_compare_func cmp_func
-);
+CX_EXPORT size_t cx_array_binary_search(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func cmp_func);
/**
* Searches the smallest upper bound in a sorted array.
* @see cx_array_binary_search()
*/
cx_attr_nonnull
-cx_attr_export
-size_t cx_array_binary_search_sup(
- const void *arr,
- size_t size,
- size_t elem_size,
- const void *elem,
- cx_compare_func cmp_func
-);
+CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func cmp_func);
/**
* Swaps two array elements.
*
* @param arr the array
* @param elem_size the element size
- * @param idx1 index of first element
- * @param idx2 index of second element
+ * @param idx1 index of the first element
+ * @param idx2 index of the second element
*/
cx_attr_nonnull
-cx_attr_export
-void cx_array_swap(
- void *arr,
- size_t elem_size,
- size_t idx1,
- size_t idx2
-);
+CX_EXPORT void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2);
/**
* Allocates an array list for storing elements with @p elem_size bytes each.
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements and the compare function will be automatically set
+ * copies of the added elements, and the compare function will be automatically set
* to cx_cmp_ptr(), if none is given.
*
* @param allocator the allocator for allocating the list memory
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxListFree, 1)
-cx_attr_export
-CxList *cxArrayListCreate(
- const CxAllocator *allocator,
- cx_compare_func comparator,
- size_t elem_size,
- size_t initial_capacity
-);
+CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator,
+ cx_compare_func comparator, size_t elem_size, size_t initial_capacity);
/**
* Allocates an array list for storing elements with @p elem_size bytes each.
* If this flag is enabled, the buffer will automatically free its contents when destroyed.
*
* Do NOT set this flag together with #CX_BUFFER_COPY_ON_WRITE. It will be automatically
- * set when the copy-on-write operations is performed.
+ * set when the copy-on-write operation is performed.
*/
#define CX_BUFFER_FREE_CONTENTS 0x01
/**
* If this flag is enabled, the buffer will allocate new memory when written to.
*
- * The current contents of the buffer will be copied to the new memory and the flag
+ * The current contents of the buffer will be copied to the new memory, and the flag
* will be cleared while the #CX_BUFFER_FREE_CONTENTS flag will be set automatically.
*/
#define CX_BUFFER_COPY_ON_WRITE 0x04
size_t blkmax;
/**
- * The target for write function.
+ * The target for the write function.
*/
void *target;
* you will need to cast the pointer, and you should set the
* #CX_BUFFER_COPY_ON_WRITE flag.
*
- * You need to set the size manually after initialization, if
+ * You need to set the size manually after initialization if
* you provide @p space which already contains data.
*
* When you specify stack memory as @p space and decide to use
* #CX_BUFFER_COPY_ON_EXTEND flag, instead of the
* #CX_BUFFER_AUTO_EXTEND flag.
*
- * @note You may provide @c NULL as argument for @p space.
+ * @note You may provide @c NULL as the argument for @p space.
* Then this function will allocate the space and enforce
* the #CX_BUFFER_FREE_CONTENTS flag. In that case, specifying
* copy-on-write should be avoided, because the allocated
* @return zero on success, non-zero if a required allocation failed
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-int cxBufferInit(
- CxBuffer *buffer,
- void *space,
- size_t capacity,
- const CxAllocator *allocator,
- int flags
-);
+CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity,
+ const CxAllocator *allocator, int flags);
/**
* Configures the buffer for flushing.
* @see cxBufferWrite()
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferEnableFlushing(
- CxBuffer *buffer,
- CxBufferFlushConfig config
-);
+CX_EXPORT int cxBufferEnableFlushing(CxBuffer *buffer, CxBufferFlushConfig config);
/**
* Destroys the buffer contents.
* @see cxBufferInit()
*/
cx_attr_nonnull
-cx_attr_export
-void cxBufferDestroy(CxBuffer *buffer);
+CX_EXPORT void cxBufferDestroy(CxBuffer *buffer);
/**
* Deallocates the buffer.
* If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys
* the contents. If you @em only want to destroy the contents, use cxBufferDestroy().
*
- * @remark As with all free() functions, this accepts @c NULL arguments in which
- * case it does nothing.
- *
* @param buffer the buffer to deallocate
* @see cxBufferCreate()
*/
-cx_attr_export
-void cxBufferFree(CxBuffer *buffer);
+CX_EXPORT void cxBufferFree(CxBuffer *buffer);
/**
* Allocates and initializes a fresh buffer.
* #CX_BUFFER_COPY_ON_EXTEND flag, instead of the
* #CX_BUFFER_AUTO_EXTEND flag.
*
- * @note You may provide @c NULL as argument for @p space.
+ * @note You may provide @c NULL as the argument for @p space.
* Then this function will allocate the space and enforce
* the #CX_BUFFER_FREE_CONTENTS flag.
*
* @param flags buffer features (see cx_buffer_s.flags)
* @return a pointer to the buffer on success, @c NULL if a required allocation failed
*/
-cx_attr_malloc
-cx_attr_dealloc(cxBufferFree, 1)
-cx_attr_nodiscard
-cx_attr_export
-CxBuffer *cxBufferCreate(
- void *space,
- size_t capacity,
- const CxAllocator *allocator,
- int flags
-);
+cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard
+CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity,
+ const CxAllocator *allocator, int flags);
/**
* Shifts the contents of the buffer by the given offset.
* If auto extension is enabled, the buffer grows, if necessary.
* In case the auto extension fails, this function returns a non-zero value and
* no contents are changed.
- * If auto extension is disabled, the contents that do not fit into the buffer
- * are discarded.
+ * When the auto extension is disabled, the contents that do not fit into the
+ * buffer are discarded.
*
* If the offset is negative, the contents are shifted to the left where the
* first @p shift bytes are discarded.
* If this value is larger than the buffer size, the buffer is emptied (but
* not cleared, see the security note below).
*
- * The buffer position gets shifted alongside with the content but is kept
+ * The buffer position gets shifted alongside the content but is kept
* within the boundaries of the buffer.
*
* @note For situations where @c off_t is not large enough, there are specialized cxBufferShiftLeft() and
- * cxBufferShiftRight() functions using a @c size_t as parameter type.
+ * cxBufferShiftRight() functions using a @c size_t as the parameter type.
*
* @attention
* Security Note: The shifting operation does @em not erase the previously occupied memory cells.
- * But you can easily do that manually, e.g. by calling
+ * But you can do that manually by calling
* <code>memset(buffer->bytes, 0, shift)</code> for a right shift or
* <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code>
* for a left shift.
* @see cxBufferShiftRight()
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferShift(
- CxBuffer *buffer,
- off_t shift
-);
+CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift);
/**
* Shifts the buffer to the right.
* @see cxBufferShift()
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferShiftRight(
- CxBuffer *buffer,
- size_t shift
-);
+CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
/**
* Shifts the buffer to the left.
* @see cxBufferShift()
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferShiftLeft(
- CxBuffer *buffer,
- size_t shift
-);
+CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
/**
*
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferSeek(
- CxBuffer *buffer,
- off_t offset,
- int whence
-);
+CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
/**
* Clears the buffer by resetting the position and deleting the data.
* @see cxBufferReset()
*/
cx_attr_nonnull
-cx_attr_export
-void cxBufferClear(CxBuffer *buffer);
+CX_EXPORT void cxBufferClear(CxBuffer *buffer);
/**
* Resets the buffer by resetting the position and size to zero.
* @see cxBufferClear()
*/
cx_attr_nonnull
-cx_attr_export
-void cxBufferReset(CxBuffer *buffer);
+CX_EXPORT void cxBufferReset(CxBuffer *buffer);
/**
* Tests, if the buffer position has exceeded the buffer size.
* byte of the buffer's contents
* @retval false otherwise
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-bool cxBufferEof(const CxBuffer *buffer);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
/**
* @see cxBufferShrink()
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferMinimumCapacity(
- CxBuffer *buffer,
- size_t capacity
-);
+CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
/**
* Shrinks the capacity of the buffer to fit its current size.
* @see cxBufferMinimumCapacity()
*/
cx_attr_nonnull
-cx_attr_export
-void cxBufferShrink(
- CxBuffer *buffer,
- size_t reserve
-);
+CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve);
/**
* Writes data to a CxBuffer.
*
* If automatic flushing is not enabled, the data is simply written into the
- * buffer at the current position and the position of the buffer is increased
+ * buffer at the current position, and the position of the buffer is increased
* by the number of bytes written.
*
* If flushing is enabled and the buffer needs to flush, the data is flushed to
* data in this buffer is shifted to the beginning of this buffer so that the
* newly available space can be used to append as much data as possible.
*
- * This function only stops writing more elements, when the flush target and this
+ * This function only stops writing more elements when the flush target and this
* buffer are both incapable of taking more data or all data has been written.
*
* If, after flushing, the number of items that shall be written still exceeds
* to the flush target, if possible.
*
* The number returned by this function is the number of elements from
- * @c ptr that could be written to either the flush target or the buffer
- * (so it does not include the number of items that had been already in the buffer
- * in were flushed during the process).
+ * @c ptr that could be written to either the flush target or the buffer.
+ * That means it does @em not include the number of items that were already in
+ * the buffer and were also flushed during the process.
*
* @attention
* When @p size is larger than one and the contents of the buffer are not aligned
* with @p size, flushing stops after all complete items have been flushed, leaving
- * the mis-aligned part in the buffer.
+ * the misaligned part in the buffer.
* Afterward, this function only writes as many items as possible to the buffer.
*
* @note The signature is compatible with the fwrite() family of functions.
* @see cxBufferRead()
*/
cx_attr_nonnull
-cx_attr_export
-size_t cxBufferWrite(
- const void *ptr,
- size_t size,
- size_t nitems,
- CxBuffer *buffer
-);
+CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size,
+ size_t nitems, CxBuffer *buffer);
/**
* Appends data to a CxBuffer.
* @see cxBufferRead()
*/
cx_attr_nonnull
-cx_attr_export
-size_t cxBufferAppend(
- const void *ptr,
- size_t size,
- size_t nitems,
- CxBuffer *buffer
-);
+CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size,
+ size_t nitems, CxBuffer *buffer);
/**
* Performs a single flush-run on the specified buffer.
* at position 200. The flush configuration is
* @c blkmax=4 and @c blksize=64 .
* Assume that the entire flush operation is successful.
- * All 200 bytes on the left hand-side from the current
+ * All 200 bytes on the left-hand-side from the current
* position are written.
- * That means, the size of the buffer is now 140 and the
+ * That means the size of the buffer is now 140 and the
* position is zero.
*
* @par Example 2
* Same as Example 1, but now the @c blkmax is 1.
- * The size of the buffer is now 276 and the position is 136.
+ * The size of the buffer is now 276, and the position is 136.
*
* @par Example 3
* Same as Example 1, but now assume the flush target
* only accepts 100 bytes before returning zero.
- * That means, the flush operations manages to flush
+ * That means the flush operation manages to flush
* one complete block and one partial block, ending
* up with a buffer with size 240 and position 100.
*
* @remark When the buffer uses copy-on-write, the memory
* is copied first, before attempting any flush.
* This is, however, considered an erroneous use of the
- * buffer, because it does not make much sense to put
- * readonly data into an UCX buffer for flushing, instead
+ * buffer because it makes little sense to put
+ * readonly data into an UCX buffer for flushing instead
* of writing it directly to the target.
*
* @param buffer the buffer
* @see cxBufferEnableFlushing()
*/
cx_attr_nonnull
-cx_attr_export
-size_t cxBufferFlush(CxBuffer *buffer);
+CX_EXPORT size_t cxBufferFlush(CxBuffer *buffer);
/**
* Reads data from a CxBuffer.
* @see cxBufferAppend()
*/
cx_attr_nonnull
-cx_attr_export
-size_t cxBufferRead(
- void *ptr,
- size_t size,
- size_t nitems,
- CxBuffer *buffer
-);
+CX_EXPORT size_t cxBufferRead(void *ptr, size_t size,
+ size_t nitems, CxBuffer *buffer);
/**
* Writes a character to a buffer.
* The least significant byte of the argument is written to the buffer. If the
* end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled,
* the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature
- * is disabled or buffer extension fails, @c EOF is returned.
+ * is disabled or the buffer extension fails, @c EOF is returned.
*
- * On successful write, the position of the buffer is increased.
+ * On successful writing, the position of the buffer is increased.
*
* If you just want to write a null-terminator at the current position, you
* should use cxBufferTerminate() instead.
* @param buffer the buffer to write to
* @param c the character to write
* @return the byte that has been written or @c EOF when the end of the stream is
- * reached and automatic extension is not enabled or not possible
+ * reached, and automatic extension is not enabled or not possible
* @see cxBufferTerminate()
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferPut(
- CxBuffer *buffer,
- int c
-);
+CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c);
/**
* Writes a terminating zero to a buffer at the current position.
* @return zero, if the terminator could be written, non-zero otherwise
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferTerminate(CxBuffer *buffer);
+CX_EXPORT int cxBufferTerminate(CxBuffer *buffer);
/**
* Writes a string to a buffer.
* @param str the zero-terminated string
* @return the number of bytes written
*/
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-cx_attr_export
-size_t cxBufferPutString(
- CxBuffer *buffer,
- const char *str
-);
+cx_attr_nonnull cx_attr_cstr_arg(2)
+CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str);
/**
* Gets a character from a buffer.
* @return the character or @c EOF, if the end of the buffer is reached
*/
cx_attr_nonnull
-cx_attr_export
-int cxBufferGet(CxBuffer *buffer);
+CX_EXPORT int cxBufferGet(CxBuffer *buffer);
#ifdef __cplusplus
}
/**
* Indicates whether the collection can guarantee that the stored elements are currently sorted.
*
- * This may return false even when the elements are sorted.
- * It is totally up to the implementation of the collection whether it keeps track of the order of its elements.
+ * This may return @c false even when the elements are sorted.
+ * It is totally up to the implementation of the collection when to check if the elements are sorted.
+ * It is usually a good practice to establish this property as an invariant that does not need
+ * to be re-checked on certain operations.
*
* @param c a pointer to a struct that contains #CX_COLLECTION_BASE
* @retval true if the elements are currently sorted wrt. the collection's compare function
* @retval false if the order of elements is unknown
*/
-#define cxCollectionSorted(c) ((c)->collection.sorted)
+#define cxCollectionSorted(c) ((c)->collection.sorted || (c)->collection.size == 0)
/**
* Sets a simple destructor function for this collection.
* <a href="https://uap-core.de/hg/ucx">https://uap-core.de/hg/ucx</a>
* </p>
*
- * <h2>LICENCE</h2>
+ * <h2>LICENSE</h2>
*
* Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
*
#define __attribute__(x)
#endif
+/**
+ * Inform the compiler that falling through a switch case is intentional.
+ */
+#define cx_attr_fallthrough __attribute__((__fallthrough__))
+
/**
* All pointer arguments must be non-NULL.
*/
*/
#define cx_attr_cstr_arg(idx)
/**
- * No support for access attribute in clang.
+ * No support for the access attribute in clang.
*/
#define cx_attr_access(mode, ...)
#else
#define _Thread_local __declspec(thread)
#endif // _MSC_VER
+// ---------------------------------------------------------------------------
+// Exported and inlined functions
+// ---------------------------------------------------------------------------
+
#if defined(CX_WINDLL_EXPORT)
-#define cx_attr_export __declspec(dllexport)
+#define CX_EXPORT __declspec(dllexport)
#elif defined(CX_WINDLL)
-#define cx_attr_export __declspec(dllimport)
+#define CX_EXPORT __declspec(dllimport)
#else
/** Only used for building Windows DLLs. */
-#define cx_attr_export
+#define CX_EXPORT
#endif // CX_WINDLL / CX_WINDLL_EXPORT
+#ifdef __GNUC__
+/**
+ * Declares a function to be inlined.
+ */
+#define CX_INLINE __attribute__((always_inline)) static inline
+#else
+#define CX_INLINE static inline
+#endif
+/**
+ * Declares a compatibility function for C++ builds.
+ */
+#define CX_CPPDECL static inline
+
// ---------------------------------------------------------------------------
// Useful function pointers
// ---------------------------------------------------------------------------
/**
* Function pointer compatible with fwrite-like functions.
*/
-typedef size_t (*cx_write_func)(
- const void *,
- size_t,
- size_t,
- void *
-);
+typedef size_t (*cx_write_func)(const void*, size_t, size_t, void*);
/**
* Function pointer compatible with fread-like functions.
*/
-typedef size_t (*cx_read_func)(
- void *,
- size_t,
- size_t,
- void *
-);
+typedef size_t (*cx_read_func)(void*, size_t, size_t, void*);
// ---------------------------------------------------------------------------
// Utility macros
#if __cplusplus
extern "C"
#endif
-cx_attr_export int cx_szmul_impl(size_t a, size_t b, size_t *result);
+CX_EXPORT int cx_szmul_impl(size_t a, size_t b, size_t *result);
#endif // cx_szmul
-
-
#endif // UCX_COMMON_H
*
* All functions from compare.h with the cx_cmp prefix are
* compatible with this signature and can be used as
- * compare function for collections, or other implementations
+ * compare function for collections or other implementations
* that need to be type-agnostic.
*
* For simple comparisons the cx_vcmp family of functions
* can be used, but they are NOT compatible with this function
* pointer.
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-typedef int (*cx_compare_func)(
- const void *left,
- const void *right
-);
+typedef int (*cx_compare_func)(const void *left, const void *right);
/**
* Compares two integers of type int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_int(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_int(const void *i1, const void *i2);
/**
* Compares two integers of type int.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_int(int i1, int i2);
+CX_EXPORT int cx_vcmp_int(int i1, int i2);
/**
* Compares two integers of type long int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_longint(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_longint(const void *i1, const void *i2);
/**
* Compares two integers of type long int.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_longint(long int i1, long int i2);
+CX_EXPORT int cx_vcmp_longint(long int i1, long int i2);
/**
* Compares two integers of type long long.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_longlong(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_longlong(const void *i1, const void *i2);
/**
* Compares two integers of type long long.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_longlong(long long int i1, long long int i2);
+CX_EXPORT int cx_vcmp_longlong(long long int i1, long long int i2);
/**
* Compares two integers of type int16_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_int16(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_int16(const void *i1, const void *i2);
/**
* Compares two integers of type int16_t.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_int16(int16_t i1, int16_t i2);
+CX_EXPORT int cx_vcmp_int16(int16_t i1, int16_t i2);
/**
* Compares two integers of type int32_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_int32(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_int32(const void *i1, const void *i2);
/**
* Compares two integers of type int32_t.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_int32(int32_t i1, int32_t i2);
+CX_EXPORT int cx_vcmp_int32(int32_t i1, int32_t i2);
/**
* Compares two integers of type int64_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_int64(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_int64(const void *i1, const void *i2);
/**
* Compares two integers of type int64_t.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_int64(int64_t i1, int64_t i2);
+CX_EXPORT int cx_vcmp_int64(int64_t i1, int64_t i2);
/**
* Compares two integers of type unsigned int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_uint(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_uint(const void *i1, const void *i2);
/**
* Compares two integers of type unsigned int.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_uint(unsigned int i1, unsigned int i2);
+CX_EXPORT int cx_vcmp_uint(unsigned int i1, unsigned int i2);
/**
* Compares two integers of type unsigned long int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_ulongint(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_ulongint(const void *i1, const void *i2);
/**
* Compares two integers of type unsigned long int.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
+CX_EXPORT int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
/**
* Compares two integers of type unsigned long long.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_ulonglong(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_ulonglong(const void *i1, const void *i2);
/**
* Compares two integers of type unsigned long long.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
+CX_EXPORT int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
/**
* Compares two integers of type uint16_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_uint16(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_uint16(const void *i1, const void *i2);
/**
* Compares two integers of type uint16_t.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
+CX_EXPORT int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
/**
* Compares two integers of type uint32_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_uint32(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_uint32(const void *i1, const void *i2);
/**
* Compares two integers of type uint32_t.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
+CX_EXPORT int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
/**
* Compares two integers of type uint64_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_uint64(const void *i1, const void *i2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_uint64(const void *i1, const void *i2);
/**
* Compares two integers of type uint64_t.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
+CX_EXPORT int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
+
+/**
+ * Compares two integers of type size_t.
+ *
+ * @note the parameters deliberately have type @c void* to be
+ * compatible with #cx_compare_func without the need of a cast.
+ *
+ * @param i1 pointer to size_t one
+ * @param i2 pointer to size_t two
+ * @retval -1 if the left argument is less than the right argument
+ * @retval 0 if both arguments are equal
+ * @retval 1 if the left argument is greater than the right argument
+ */
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_size(const void *i1, const void *i2);
+
+/**
+ * Compares two integers of type size_t.
+ *
+ * @param i1 size_t one
+ * @param i2 size_t two
+ * @retval -1 if the left argument is less than the right argument
+ * @retval 0 if both arguments are equal
+ * @retval 1 if the left argument is greater than the right argument
+ */
+cx_attr_nodiscard
+CX_EXPORT int cx_vcmp_size(size_t i1, size_t i2);
/**
* Compares two real numbers of type float with precision 1e-6f.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_float(const void *f1, const void *f2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_float(const void *f1, const void *f2);
/**
* Compares two real numbers of type float with precision 1e-6f.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_float(float f1, float f2);
+CX_EXPORT int cx_vcmp_float(float f1, float f2);
/**
* Compares two real numbers of type double with precision 1e-14.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_double(const void *d1, const void *d2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_double(const void *d1, const void *d2);
/**
* Compares two real numbers of type double with precision 1e-14.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_double(double d1, double d2);
+CX_EXPORT int cx_vcmp_double(double d1, double d2);
/**
* Compares the integer representation of two pointers.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_intptr(const void *ptr1, const void *ptr2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_intptr(const void *ptr1, const void *ptr2);
/**
* Compares the integer representation of two pointers.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
+CX_EXPORT int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
/**
* Compares the unsigned integer representation of two pointers.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
/**
* Compares the unsigned integer representation of two pointers.
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
+CX_EXPORT int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
/**
* Compares the pointers specified in the arguments without dereferencing.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cx_cmp_ptr(const void *ptr1, const void *ptr2);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2);
#ifdef __cplusplus
} // extern "C"
/** Internal structure for a key within a hash map. */
struct cx_hash_key_s {
- /** The key data. */
+ /**
+ * The key data.
+ * May be NULL when the hash is collision-free.
+ */
const void *data;
/**
* The key data length.
*/
size_t len;
/** The hash value of the key data. */
- unsigned hash;
+ uint64_t hash;
};
/**
* @see cx_hash_key()
*/
cx_attr_nonnull
-cx_attr_export
-void cx_hash_murmur(CxHashKey *key);
+CX_EXPORT void cx_hash_murmur(CxHashKey *key);
+
+/**
+ * Mixes up a 32-bit integer to be used as a hash.
+ *
+ * This function produces no collisions and has a good statistical distribution.
+ *
+ * @param x the integer
+ * @return the hash
+ */
+CX_EXPORT uint32_t cx_hash_u32(uint32_t x);
+
+/**
+ * Mixes up a 64-bit integer to be used as a hash.
+ *
+ * This function produces no collisions and has a good statistical distribution.
+ *
+ * @param x the integer
+ * @return the hash
+ */
+CX_EXPORT uint64_t cx_hash_u64(uint64_t x);
+
+/**
+ * Computes a hash key from a 32-bit integer.
+ *
+ * @param x the integer
+ * @return the hash key
+ */
+cx_attr_nodiscard
+CX_EXPORT CxHashKey cx_hash_key_u32(uint32_t x);
+
+/**
+ * Computes a hash key from a 64-bit integer.
+ *
+ * @param x the integer
+ * @return the hash key
+ */
+cx_attr_nodiscard
+CX_EXPORT CxHashKey cx_hash_key_u64(uint64_t x);
/**
* Computes a hash key from a string.
* @param str the string
* @return the hash key
*/
-cx_attr_nodiscard
-cx_attr_cstr_arg(1)
-cx_attr_export
-CxHashKey cx_hash_key_str(const char *str);
+cx_attr_nodiscard cx_attr_cstr_arg(1)
+CX_EXPORT CxHashKey cx_hash_key_str(const char *str);
+
+/**
+ * Computes a hash key from a string.
+ *
+ * Use this function when the string is represented
+ * as an unsigned char array.
+ *
+ * The string needs to be zero-terminated.
+ *
+ * @param str the string
+ * @return the hash key
+ */
+cx_attr_nodiscard cx_attr_cstr_arg(1)
+CX_EXPORT CxHashKey cx_hash_key_ustr(const unsigned char *str);
/**
* Computes a hash key from a byte array.
* @param len the length
* @return the hash key
*/
-cx_attr_nodiscard
-cx_attr_access_r(1, 2)
-cx_attr_export
-CxHashKey cx_hash_key_bytes(
- const unsigned char *bytes,
- size_t len
-);
+cx_attr_nodiscard cx_attr_access_r(1, 2)
+CX_EXPORT CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len);
/**
* Computes a hash key for an arbitrary object.
* used for data exchange with different machines.
*
* @param obj a pointer to an arbitrary object
- * @param len the length of object in memory
+ * @param len the length of the object in memory
* @return the hash key
*/
cx_attr_nodiscard
cx_attr_access_r(1, 2)
-cx_attr_export
-CxHashKey cx_hash_key(
- const void *obj,
- size_t len
-);
+CX_EXPORT CxHashKey cx_hash_key(const void *obj, size_t len);
/**
* Computes a hash key from a UCX string.
* @return the hash key
*/
cx_attr_nodiscard
-static inline CxHashKey cx_hash_key_cxstr(cxstring str) {
- return cx_hash_key(str.ptr, str.length);
-}
+CX_EXPORT CxHashKey cx_hash_key_cxstr(cxstring str);
/**
* Computes a hash key from a UCX string.
*
- * @param str (@c cxstring or @c cxmutstr) the string
- * @return (@c CxHashKey) the hash key
+ * @param str the string
+ * @return the hash key
+ */
+cx_attr_nodiscard
+CX_EXPORT CxHashKey cx_hash_key_mutstr(cxmutstr str);
+
+/**
+ * The identity function for the CX_HASH_KEY() macro.
+ * You should never need to use this manually.
+ *
+ * @param key the key
+ * @return a copy of the key
+ */
+cx_attr_nodiscard
+CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) {
+ return key;
+}
+
+#ifndef __cplusplus
+/**
+ * Creates a hash key from any of the supported types with implicit length.
+ *
+ * Does nothing when passing a CxHashkey.
+ *
+ * Supported types are UCX strings, zero-terminated C strings,
+ * and 32-bit or 64-bit unsigned integers.
+ *
+ * @param key the key data
+ * @returns the @c CxHashKey
+ */
+#define CX_HASH_KEY(key) _Generic((key), \
+ CxHashKey: cx_hash_key_identity, \
+ cxstring: cx_hash_key_cxstr, \
+ cxmutstr: cx_hash_key_mutstr, \
+ char*: cx_hash_key_str, \
+ const char*: cx_hash_key_str, \
+ unsigned char*: cx_hash_key_ustr, \
+ const unsigned char*: cx_hash_key_ustr, \
+ uint32_t: cx_hash_key_u32, \
+ uint64_t: cx_hash_key_u64) \
+ (key)
+#endif // __cplusplus
+
+/**
+ * Compare function for hash keys.
+ *
+ * @param left the first key
+ * @param right the second key
+ * @return zero when the keys equal, non-zero when they differ
*/
-#define cx_hash_key_cxstr(str) cx_hash_key_cxstr(cx_strcast(str))
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right);
#ifdef __cplusplus
} // extern "C"
+
+// ----------------------------------------------------------
+// Overloads of CX_HASH_KEY (the C++ version of a _Generic)
+// ----------------------------------------------------------
+
+CX_CPPDECL CxHashKey CX_HASH_KEY(CxHashKey key) {
+ return key;
+}
+
+CX_CPPDECL CxHashKey CX_HASH_KEY(cxstring str) {
+ return cx_hash_key_cxstr(str);
+}
+
+CX_CPPDECL CxHashKey CX_HASH_KEY(cxmutstr str) {
+ return cx_hash_key_mutstr(str);
+}
+
+CX_CPPDECL CxHashKey CX_HASH_KEY(const char *str) {
+ return cx_hash_key_str(str);
+}
+
+CX_CPPDECL CxHashKey CX_HASH_KEY(const unsigned char *str) {
+ return cx_hash_key_ustr(str);
+}
+
+CX_CPPDECL CxHashKey CX_HASH_KEY(uint32_t key) {
+ return cx_hash_key_u32(key);
+}
+
+CX_CPPDECL CxHashKey CX_HASH_KEY(uint64_t key) {
+ return cx_hash_key_u64(key);
+}
#endif
#endif // UCX_HASH_KEY_H
* copies of the added elements.
*
* @note Iterators provided by this hash map implementation provide the remove operation.
- * The index value of an iterator is incremented when the iterator advanced without removal.
+ * The index value of an iterator is incremented when the iterator advanced without
+ * removing an entry.
* In other words, when the iterator is finished, @c index==size .
*
* @param allocator the allocator to use
* @param buckets the initial number of buckets in this hash map
* @return a pointer to the new hash map
*/
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc(cxMapFree, 1)
-cx_attr_export
-CxMap *cxHashMapCreate(
- const CxAllocator *allocator,
- size_t itemsize,
- size_t buckets
-);
+cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
+CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator,
+ size_t itemsize, size_t buckets);
/**
* Creates a new hash map with a default number of buckets.
* copies of the added elements.
*
* @note Iterators provided by this hash map implementation provide the remove operation.
- * The index value of an iterator is incremented when the iterator advanced without removal.
+ * The index value of an iterator is incremented when the iterator advanced without
+ * removing an entry.
* In other words, when the iterator is finished, @c index==size .
*
* @param itemsize (@c size_t) the size of one element
* Increases the number of buckets, if necessary.
*
* The load threshold is @c 0.75*buckets. If the element count exceeds the load
- * threshold, the map will be rehashed. Otherwise, no action is performed and
+ * threshold, the map will be rehashed. Otherwise, no action is performed, and
* this function simply returns 0.
*
- * The rehashing process ensures, that the number of buckets is at least
+ * The rehashing process ensures that the number of buckets is at least
* 2.5 times the element count. So there is enough room for additional
* elements without the need of another soon rehashing.
*
* @retval non-zero if a memory allocation error occurred
*/
cx_attr_nonnull
-cx_attr_export
-int cxMapRehash(CxMap *map);
+CX_EXPORT int cxMapRehash(CxMap *map);
#ifdef __cplusplus
* True if the iterator points to valid data.
*/
bool (*valid)(const void *);
-
+ /**
+ * Original implementation in case the function needs to be wrapped.
+ */
+ bool (*valid_impl)(const void *);
/**
* Returns a pointer to the current element.
*
* Original implementation in case the function needs to be wrapped.
*/
void *(*current_impl)(const void *);
-
/**
* Advances the iterator.
*
* When valid returns false, the behavior of this function is undefined.
*/
void (*next)(void *);
+ /**
+ * Original implementation in case the function needs to be wrapped.
+ */
+ void (*next_impl)(void *);
/**
* Indicates whether this iterator may remove elements.
*/
- bool mutating;
+ bool allow_remove;
/**
* Internal flag for removing the current element when advancing.
*/
/**
* Handle for the source collection, if any.
*/
- union {
- /**
- * Access for mutating iterators.
- */
- void *m;
- /**
- * Access for normal iterators.
- */
- const void *c;
- } src_handle;
+ void *src_handle;
/**
* If the iterator is position-aware, contains the index of the element in the underlying collection.
* Iterator type.
*
* An iterator points to a certain element in a (possibly unbounded) chain of elements.
- * Iterators that are based on collections (which have a defined "first" element), are supposed
+ * Iterators that are based on collections (which have a defined "first" element) are supposed
* to be "position-aware", which means that they keep track of the current index within the collection.
*
* @note Objects that are pointed to by an iterator are always mutable through that iterator. However,
- * any concurrent mutation of the collection other than by this iterator makes this iterator invalid,
+ * any concurrent mutation of the collection other than by this iterator makes this iterator obsolete,
* and it must not be used anymore.
*/
typedef struct cx_iterator_s CxIterator;
#define cxIteratorNext(iter) (iter).base.next(&iter)
/**
- * Flags the current element for removal, if this iterator is mutating.
- *
- * Does nothing for non-mutating iterators.
+ * Flags the current element for removal if the iterator allows it.
*
* @param iter the iterator
+ * @return @c true if removal is allowed, @c false otherwise
*/
-#define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating
+#define cxIteratorFlagRemoval(iter) ((iter).base.remove = (iter).base.allow_remove)
/**
* Obtains a reference to an arbitrary iterator.
/**
* Creates an iterator for the specified plain array.
*
- * The @p array can be @c NULL in which case the iterator will be immediately
+ * The @p array can be @c NULL, in which case the iterator will be immediately
* initialized such that #cxIteratorValid() returns @c false.
*
* This iterator yields the addresses of the array elements.
* use cxIteratorPtr() to create an iterator which directly
* yields the stored pointers.
*
- * @param array a pointer to the array (can be @c NULL)
- * @param elem_size the size of one array element
- * @param elem_count the number of elements in the array
- * @return an iterator for the specified array
- * @see cxIteratorPtr()
- */
-cx_attr_nodiscard
-cx_attr_export
-CxIterator cxIterator(
- const void *array,
- size_t elem_size,
- size_t elem_count
-);
-
-/**
- * Creates a mutating iterator for the specified plain array.
- *
* While the iterator is in use, the array may only be altered by removing
* elements through #cxIteratorFlagRemoval(). Every other change to the array
* will bring this iterator to an undefined state.
* moving all subsequent elements by one. Usually, when the order of elements is
* not important, this parameter should be set to @c false.
*
- * The @p array can be @c NULL in which case the iterator will be immediately
- * initialized such that #cxIteratorValid() returns @c false.
- *
- *
* @param array a pointer to the array (can be @c NULL)
* @param elem_size the size of one array element
* @param elem_count the number of elements in the array
* @param remove_keeps_order @c true if the order of elements must be preserved
* when removing an element
* @return an iterator for the specified array
+ * @see cxIteratorPtr()
*/
cx_attr_nodiscard
-cx_attr_export
-CxIterator cxMutIterator(
- void *array,
- size_t elem_size,
- size_t elem_count,
- bool remove_keeps_order
-);
+CX_EXPORT CxIterator cxIterator(const void *array,
+ size_t elem_size, size_t elem_count, bool remove_keeps_order);
/**
* Creates an iterator for the specified plain pointer array.
*
* This iterator assumes that every element in the array is a pointer
- * and yields exactly those pointers during iteration (while in contrast
- * an iterator created with cxIterator() would return the addresses
- * of those pointers within the array).
+ * and yields exactly those pointers during iteration (on the other
+ * hand, an iterator created with cxIterator() would return the
+ * addresses of those pointers within the array).
*
- * @param array a pointer to the array (can be @c NULL)
- * @param elem_count the number of elements in the array
- * @return an iterator for the specified array
- * @see cxIterator()
- */
-cx_attr_nodiscard
-cx_attr_export
-CxIterator cxIteratorPtr(
- const void *array,
- size_t elem_count
-);
-
-/**
- * Creates a mutating iterator for the specified plain pointer array.
+ * While the iterator is in use, the array may only be altered by removing
+ * elements through #cxIteratorFlagRemoval(). Every other change to the array
+ * will bring this iterator to an undefined state.
*
- * This is the mutating variant of cxIteratorPtr(). See also
- * cxMutIterator().
+ * When @p remove_keeps_order is set to @c false, removing an element will only
+ * move the last element to the position of the removed element, instead of
+ * moving all subsequent elements by one. Usually, when the order of elements is
+ * not important, this parameter should be set to @c false.
*
* @param array a pointer to the array (can be @c NULL)
* @param elem_count the number of elements in the array
* @param remove_keeps_order @c true if the order of elements must be preserved
* when removing an element
* @return an iterator for the specified array
- * @see cxMutIterator()
- * @see cxIteratorPtr()
+ * @see cxIterator()
*/
cx_attr_nodiscard
-cx_attr_export
-CxIterator cxMutIteratorPtr(
- void *array,
- size_t elem_count,
- bool remove_keeps_order
-);
+CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count,
+ bool remove_keeps_order);
#ifdef __cplusplus
} // extern "C"
*/
CX_JSON_TOKEN_STRING,
/**
- * A number token that can be represented as integer.
+ * A number token that can be represented as an integer.
*/
CX_JSON_TOKEN_INTEGER,
/**
- * A number token that cannot be represented as integer.
+ * A number token that cannot be represented as an integer.
*/
CX_JSON_TOKEN_NUMBER,
/**
*/
typedef struct cx_mutstr_s CxJsonString;
/**
- * Type alias for a number that can be represented as 64-bit signed integer.
+ * Type alias for a number that can be represented as a 64-bit signed integer.
*/
typedef int64_t CxJsonInteger;
/**
- * Type alias for number that is not an integer.
+ * Type alias for a number that is not an integer.
*/
typedef double CxJsonNumber;
/**
*/
union {
/**
- * The array data if type is #CX_JSON_ARRAY.
+ * The array data if the type is #CX_JSON_ARRAY.
*/
CxJsonArray array;
/**
- * The object data if type is #CX_JSON_OBJECT.
+ * The object data if the type is #CX_JSON_OBJECT.
*/
CxJsonObject object;
/**
- * The string data if type is #CX_JSON_STRING.
+ * The string data if the type is #CX_JSON_STRING.
*/
CxJsonString string;
/**
- * The integer if type is #CX_JSON_INTEGER.
+ * The integer if the type is #CX_JSON_INTEGER.
*/
CxJsonInteger integer;
/**
- * The number if type is #CX_JSON_NUMBER.
+ * The number if the type is #CX_JSON_NUMBER.
*/
CxJsonNumber number;
/**
- * The literal type if type is #CX_JSON_LITERAL.
+ * The literal type if the type is #CX_JSON_LITERAL.
*/
CxJsonLiteral literal;
} value;
};
/**
- * Status codes for the json interface.
+ * Status codes for the JSON interface.
*/
enum cx_json_status {
/**
/**
* The input ends unexpectedly.
*
- * Refill the buffer with cxJsonFill() to complete the json data.
+ * Refill the buffer with cxJsonFill() to complete the JSON data.
*/
CX_JSON_INCOMPLETE_DATA,
/**
* You can use this enumerator to check for all "good" status results
* by checking if the status is less than @c CX_JSON_OK.
*
- * A "good" status means, that you can refill data and continue parsing.
+ * A "good" status means that you can refill data and continue parsing.
*/
CX_JSON_OK,
/**
*/
CX_JSON_BUFFER_ALLOC_FAILED,
/**
- * Allocating memory for a json value failed.
+ * Allocating memory for a JSON value failed.
*/
CX_JSON_VALUE_ALLOC_FAILED,
/**
};
/**
- * Typedef for the json status enum.
+ * Typedef for the JSON status enum.
*/
typedef enum cx_json_status CxJsonStatus;
/**
* The maximum number of fractional digits in a number value.
* The default value is 6 and values larger than 15 are reduced to 15.
- * Note, that the actual number of digits may be lower, depending on the concrete number.
+ * Note that the actual number of digits may be lower, depending on the concrete number.
*/
uint8_t frac_max_digits;
/**
};
/**
- * Typedef for the json writer.
+ * Typedef for the JSON writer.
*/
typedef struct cx_json_writer_s CxJsonWriter;
* @return new JSON writer settings
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonWriter cxJsonWriterCompact(void);
+CX_EXPORT CxJsonWriter cxJsonWriterCompact(void);
/**
* Creates a default writer configuration for pretty output.
* @return new JSON writer settings
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonWriter cxJsonWriterPretty(bool use_spaces);
+CX_EXPORT CxJsonWriter cxJsonWriterPretty(bool use_spaces);
/**
* Writes a JSON value to a buffer or stream.
* that the data is only partially written when an error occurs with no
* way to indicate how much data was written.
* To avoid this problem, you can use a CxBuffer as @p target which is
- * unlikely to fail a write operation and either use the buffer's flush
- * feature to relay the data or use the data in the buffer manually to
- * write it to the actual target.
+ * unlikely to fail a write operation. You can, for example, use the buffer's flush
+ * feature to relay the data.
*
* @param target the buffer or stream where to write to
* @param value the value that shall be written
* @retval non-zero when no or not all data could be written
*/
cx_attr_nonnull_arg(1, 2, 3)
-cx_attr_export
-int cxJsonWrite(
- void* target,
- const CxJsonValue* value,
- cx_write_func wfunc,
- const CxJsonWriter* settings
-);
+CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value,
+ cx_write_func wfunc, const CxJsonWriter* settings);
/**
- * Initializes the json interface.
+ * Initializes the JSON interface.
*
- * @param json the json interface
+ * @param json the JSON interface
* @param allocator the allocator that shall be used for the produced values
* @see cxJsonDestroy()
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-void cxJsonInit(CxJson *json, const CxAllocator *allocator);
+CX_EXPORT void cxJsonInit(CxJson *json, const CxAllocator *allocator);
/**
- * Destroys the json interface.
+ * Destroys the JSON interface.
*
- * @param json the json interface
+ * @param json the JSON interface
* @see cxJsonInit()
*/
cx_attr_nonnull
-cx_attr_export
-void cxJsonDestroy(CxJson *json);
+CX_EXPORT void cxJsonDestroy(CxJson *json);
/**
- * Destroys and re-initializes the json interface.
+ * Destroys and re-initializes the JSON interface.
*
- * You might want to use this, to reset the parser after
+ * You might want to use this to reset the parser after
* encountering a syntax error.
*
- * @param json the json interface
+ * @param json the JSON interface
*/
cx_attr_nonnull
-static inline void cxJsonReset(CxJson *json) {
- const CxAllocator *allocator = json->allocator;
- cxJsonDestroy(json);
- cxJsonInit(json, allocator);
-}
+CX_EXPORT void cxJsonReset(CxJson *json);
/**
* Fills the input buffer.
* the additional data is appended - inevitably leading to
* an allocation of a new buffer and copying the previous contents.
*
- * @param json the json interface
+ * @param json the JSON interface
* @param buf the source buffer
* @param len the length of the source buffer
* @retval zero success
* @retval non-zero internal allocation error
* @see cxJsonFill()
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxJsonFilln(CxJson *json, const char *buf, size_t len);
-
-#ifdef __cplusplus
-} // extern "C"
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len);
-cx_attr_nonnull
-static inline int cxJsonFill(
- CxJson *json,
- cxstring str
-) {
- return cxJsonFilln(json, str.ptr, str.length);
-}
+/**
+ * Internal function, do not use.
+ *
+ * @param json the JSON interface
+ * @param str the string
+ * @retval zero success
+ * @retval non-zero internal allocation error
+ */
cx_attr_nonnull
-static inline int cxJsonFill(
- CxJson *json,
- cxmutstr str
-) {
+CX_INLINE int cx_json_fill(CxJson *json, cxstring str) {
return cxJsonFilln(json, str.ptr, str.length);
}
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxJsonFill(
- CxJson *json,
- const char *str
-) {
- return cxJsonFilln(json, str, strlen(str));
-}
-
-extern "C" {
-#else // __cplusplus
/**
* Fills the input buffer.
*
* the additional data is appended - inevitably leading to
* an allocation of a new buffer and copying the previous contents.
*
- * @param json the json interface
+ * @param json the JSON interface
* @param str the source string
* @retval zero success
* @retval non-zero internal allocation error
* @see cxJsonFilln()
*/
-#define cxJsonFill(json, str) _Generic((str), \
- cxstring: cx_json_fill_cxstr, \
- cxmutstr: cx_json_fill_mutstr, \
- char*: cx_json_fill_str, \
- const char*: cx_json_fill_str) \
- (json, str)
-
-/**
- * @copydoc cxJsonFill()
- */
-cx_attr_nonnull
-static inline int cx_json_fill_cxstr(
- CxJson *json,
- cxstring str
-) {
- return cxJsonFilln(json, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxJsonFill()
- */
-cx_attr_nonnull
-static inline int cx_json_fill_mutstr(
- CxJson *json,
- cxmutstr str
-) {
- return cxJsonFilln(json, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxJsonFill()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_json_fill_str(
- CxJson *json,
- const char *str
-) {
- return cxJsonFilln(json, str, strlen(str));
-}
-#endif
+#define cxJsonFill(json, str) cx_json_fill(json, cx_strcast(str))
/**
* Creates a new (empty) JSON object.
* @see cxJsonArrAddValues()
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
+CX_EXPORT CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
/**
* Creates a new (empty) JSON array.
* @see cxJsonArrAddValues()
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
+CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
/**
* Creates a new JSON number value.
* @see cxJsonArrAddNumbers()
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
+CX_EXPORT CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
/**
* Creates a new JSON number value based on an integer.
* @see cxJsonArrAddIntegers()
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
+CX_EXPORT CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
/**
* Creates a new JSON string.
* @see cxJsonObjPutString()
* @see cxJsonArrAddStrings()
*/
-cx_attr_nodiscard
-cx_attr_nonnull_arg(2)
-cx_attr_cstr_arg(2)
-cx_attr_export
-CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str);
+cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2)
+CX_EXPORT CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str);
/**
* Creates a new JSON string.
* @see cxJsonArrAddCxStrings()
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str);
+CX_EXPORT CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str);
/**
* Creates a new JSON literal.
* @see cxJsonArrAddLiterals()
*/
cx_attr_nodiscard
-cx_attr_export
-CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
+CX_EXPORT CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
/**
* Adds number values to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
/**
* Adds number values, of which all are integers, to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
/**
* Adds strings to a JSON array.
* @retval non-zero allocation failure
* @see cxJsonArrAddCxStrings()
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
/**
* Adds strings to a JSON array.
* @retval non-zero allocation failure
* @see cxJsonArrAddStrings()
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
/**
* Adds literals to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
/**
* Add arbitrary values to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
/**
* Adds or replaces a value within a JSON object.
* @retval non-zero allocation failure
*/
cx_attr_nonnull
-cx_attr_export
-int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+CX_EXPORT int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
/**
* Creates a new JSON object and adds it to an existing object.
* @see cxJsonCreateObj()
*/
cx_attr_nonnull
-cx_attr_export
-CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
+CX_EXPORT CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
/**
* Creates a new JSON array and adds it to an object.
* @see cxJsonCreateArr()
*/
cx_attr_nonnull
-cx_attr_export
-CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
+CX_EXPORT CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
/**
* Creates a new JSON number and adds it to an object.
* @see cxJsonCreateNumber()
*/
cx_attr_nonnull
-cx_attr_export
-CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num);
+CX_EXPORT CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num);
/**
* Creates a new JSON number, based on an integer, and adds it to an object.
* @see cxJsonCreateInteger()
*/
cx_attr_nonnull
-cx_attr_export
-CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num);
+CX_EXPORT CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num);
/**
* Creates a new JSON string and adds it to an object.
* @see cxJsonObjPut()
* @see cxJsonCreateString()
*/
-cx_attr_nonnull
-cx_attr_cstr_arg(3)
-cx_attr_export
-CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str);
+cx_attr_nonnull cx_attr_cstr_arg(3)
+CX_EXPORT CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str);
/**
* Creates a new JSON string and adds it to an object.
* @see cxJsonCreateCxString()
*/
cx_attr_nonnull
-cx_attr_export
-CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str);
+CX_EXPORT CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str);
/**
* Creates a new JSON literal and adds it to an object.
* @see cxJsonCreateLiteral()
*/
cx_attr_nonnull
-cx_attr_export
-CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+CX_EXPORT CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
/**
* Recursively deallocates the memory of a JSON value.
*
* @remark The type of each deallocated value will be changed
- * to #CX_JSON_NOTHING and values of such type will be skipped
- * by the de-allocation. That means, this function protects
+ * to #CX_JSON_NOTHING, and values of such a type will be skipped
+ * by the deallocation. That means this function protects
* you from double-frees when you are accidentally freeing
* a nested value and then the parent value (or vice versa).
*
* @param value the value
*/
-cx_attr_export
-void cxJsonValueFree(CxJsonValue *value);
+CX_EXPORT void cxJsonValueFree(CxJsonValue *value);
/**
* Tries to obtain the next JSON value.
* add the missing data with another invocation of cxJsonFill()
* and then repeat the call to cxJsonNext().
*
- * @param json the json interface
+ * @param json the JSON interface
* @param value a pointer where the next value shall be stored
* @retval CX_JSON_NO_ERROR successfully retrieve the @p value
* @retval CX_JSON_NO_DATA there is no (more) data in the buffer to read from
* @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number
* @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error
*/
-cx_attr_nonnull
-cx_attr_access_w(2)
-cx_attr_export
-CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
+cx_attr_nonnull cx_attr_access_w(2)
+CX_EXPORT CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
/**
* Checks if the specified value is a JSON object.
* @retval false otherwise
*/
cx_attr_nonnull
-static inline bool cxJsonIsObject(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsObject(const CxJsonValue *value) {
return value->type == CX_JSON_OBJECT;
}
* @retval false otherwise
*/
cx_attr_nonnull
-static inline bool cxJsonIsArray(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsArray(const CxJsonValue *value) {
return value->type == CX_JSON_ARRAY;
}
* @retval false otherwise
*/
cx_attr_nonnull
-static inline bool cxJsonIsString(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsString(const CxJsonValue *value) {
return value->type == CX_JSON_STRING;
}
/**
* Checks if the specified value is a JSON number.
*
- * This function will return true for both floating point and
+ * This function will return true for both floating-point and
* integer numbers.
*
* @param value a pointer to the value
* @see cxJsonIsInteger()
*/
cx_attr_nonnull
-static inline bool cxJsonIsNumber(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsNumber(const CxJsonValue *value) {
return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER;
}
* @see cxJsonIsNumber()
*/
cx_attr_nonnull
-static inline bool cxJsonIsInteger(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsInteger(const CxJsonValue *value) {
return value->type == CX_JSON_INTEGER;
}
* @see cxJsonIsNull()
*/
cx_attr_nonnull
-static inline bool cxJsonIsLiteral(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) {
return value->type == CX_JSON_LITERAL;
}
* @see cxJsonIsFalse()
*/
cx_attr_nonnull
-static inline bool cxJsonIsBool(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL;
}
/**
* Checks if the specified value is @c true.
*
- * @remark Be advised, that this is not the same as
+ * @remark Be advised that this is different from
* testing @c !cxJsonIsFalse(v).
*
* @param value a pointer to the value
* @see cxJsonIsFalse()
*/
cx_attr_nonnull
-static inline bool cxJsonIsTrue(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE;
}
/**
* Checks if the specified value is @c false.
*
- * @remark Be advised, that this is not the same as
+ * @remark Be advised that this is different from
* testing @c !cxJsonIsTrue(v).
*
* @param value a pointer to the value
* @see cxJsonIsTrue()
*/
cx_attr_nonnull
-static inline bool cxJsonIsFalse(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE;
}
* @see cxJsonIsLiteral()
*/
cx_attr_nonnull
-static inline bool cxJsonIsNull(const CxJsonValue *value) {
+CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) {
return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL;
}
* @return the value represented as C string
* @see cxJsonIsString()
*/
-cx_attr_nonnull
-cx_attr_returns_nonnull
-static inline char *cxJsonAsString(const CxJsonValue *value) {
- return value->value.string.ptr;
-}
+cx_attr_nonnull cx_attr_returns_nonnull
+CX_EXPORT char *cxJsonAsString(const CxJsonValue *value);
/**
* Obtains a UCX string from the given JSON value.
* @see cxJsonIsString()
*/
cx_attr_nonnull
-static inline cxstring cxJsonAsCxString(const CxJsonValue *value) {
- return cx_strcast(value->value.string);
-}
+CX_EXPORT cxstring cxJsonAsCxString(const CxJsonValue *value);
/**
* Obtains a mutable UCX string from the given JSON value.
* @see cxJsonIsString()
*/
cx_attr_nonnull
-static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
- return value->value.string;
-}
+CX_EXPORT cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
/**
- * Obtains a double-precision floating point value from the given JSON value.
+ * Obtains a double-precision floating-point value from the given JSON value.
*
* If the @p value is not a JSON number, the behavior is undefined.
*
* @see cxJsonIsNumber()
*/
cx_attr_nonnull
-static inline double cxJsonAsDouble(const CxJsonValue *value) {
- if (value->type == CX_JSON_INTEGER) {
- return (double) value->value.integer;
- } else {
- return value->value.number;
- }
-}
+CX_EXPORT double cxJsonAsDouble(const CxJsonValue *value);
/**
* Obtains a 64-bit signed integer from the given JSON value.
* @see cxJsonIsInteger()
*/
cx_attr_nonnull
-static inline int64_t cxJsonAsInteger(const CxJsonValue *value) {
- if (value->type == CX_JSON_INTEGER) {
- return value->value.integer;
- } else {
- return (int64_t) value->value.number;
- }
-}
+CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value);
/**
* Obtains a Boolean value from the given JSON value.
* @see cxJsonIsLiteral()
*/
cx_attr_nonnull
-static inline bool cxJsonAsBool(const CxJsonValue *value) {
+CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
return value->value.literal == CX_JSON_TRUE;
}
* @see cxJsonIsArray()
*/
cx_attr_nonnull
-static inline size_t cxJsonArrSize(const CxJsonValue *value) {
+CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
return value->value.array.array_size;
}
* @return the value at the specified index
* @see cxJsonIsArray()
*/
+cx_attr_nonnull cx_attr_returns_nonnull
+CX_EXPORT CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
+
+/**
+ * Removes an element from a JSON array.
+ *
+ * If the @p value is not a JSON array, the behavior is undefined.
+ *
+ * This function, in contrast to cxJsonArrayGet(), returns @c NULL
+ * when the index is out of bounds.
+ *
+ * @param value the JSON value
+ * @param index the index in the array
+ * @return the removed value from the specified index or @c NULL when the index was out of bounds
+ * @see cxJsonIsArray()
+ */
cx_attr_nonnull
-cx_attr_returns_nonnull
-cx_attr_export
-CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
+CX_EXPORT CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
/**
* Returns an iterator over the JSON array elements.
* @return an iterator over the array elements
* @see cxJsonIsArray()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxIterator cxJsonArrIter(const CxJsonValue *value);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
/**
* Returns an iterator over the JSON object members.
* @return an iterator over the object members
* @see cxJsonIsObject()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxIterator cxJsonObjIter(const CxJsonValue *value);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT CxIterator cxJsonObjIter(const CxJsonValue *value);
/**
- * @copydoc cxJsonObjGet()
+ * Internal function, do not use.
+ * @param value the JSON object
+ * @param name the key to look up
+ * @return the value corresponding to the key
*/
-cx_attr_nonnull
-cx_attr_returns_nonnull
-cx_attr_export
-CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name);
+cx_attr_nonnull cx_attr_returns_nonnull
+CX_EXPORT CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
-#ifdef __cplusplus
-} // extern "C"
-
-static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) {
- return cx_json_obj_get_cxstr(value, name);
-}
-
-static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) {
- return cx_json_obj_get_cxstr(value, cx_strcast(name));
-}
-
-static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) {
- return cx_json_obj_get_cxstr(value, cx_str(name));
-}
-
-extern "C" {
-#else
/**
* Returns a value corresponding to a key in a JSON object.
*
* @return the value corresponding to the key
* @see cxJsonIsObject()
*/
-#define cxJsonObjGet(value, name) _Generic((name), \
- cxstring: cx_json_obj_get_cxstr, \
- cxmutstr: cx_json_obj_get_mutstr, \
- char*: cx_json_obj_get_str, \
- const char*: cx_json_obj_get_str) \
- (value, name)
+#define cxJsonObjGet(value, name) cx_json_obj_get(value, cx_strcast(name))
/**
- * @copydoc cxJsonObjGet()
+ * Internal function, do not use.
+ * @param value the JSON object
+ * @param name the key to look up
+ * @return the value corresponding to the key or @c NULL when the key is not part of the object
*/
cx_attr_nonnull
-cx_attr_returns_nonnull
-static inline CxJsonValue *cx_json_obj_get_mutstr(const CxJsonValue *value, cxmutstr name) {
- return cx_json_obj_get_cxstr(value, cx_strcast(name));
-}
+CX_EXPORT CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
/**
- * @copydoc cxJsonObjGet()
+ * Removes and returns a value corresponding to a key in a JSON object.
+ *
+ * If the @p value is not a JSON object, the behavior is undefined.
+ *
+ * This function, in contrast to cxJsonObjGet() returns @c NULL when the
+ * object does not contain @p name.
+ *
+ * @param value the JSON object
+ * @param name the key to look up
+ * @return the value corresponding to the key or @c NULL when the key is not part of the object
+ * @see cxJsonIsObject()
*/
-cx_attr_nonnull
-cx_attr_returns_nonnull
-cx_attr_cstr_arg(2)
-static inline CxJsonValue *cx_json_obj_get_str(const CxJsonValue *value, const char *name) {
- return cx_json_obj_get_cxstr(value, cx_str(name));
-}
-#endif
+#define cxJsonObjRemove(value, name) cx_json_obj_remove(value, cx_strcast(name))
#ifdef __cplusplus
}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file kv_list.h
+ * @brief Linked list implementation with key/value-lookup.
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ * @copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_KV_LIST_H
+#define UCX_KV_LIST_H
+
+#include "common.h"
+#include "list.h"
+#include "map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr() if none is given.
+ *
+ * After creating the list, it can also be used as a map after converting the pointer
+ * to a CxMap pointer with cxKvListAsMap().
+ * When you want to use the list interface again, you can also convert the map pointer back
+ * with cxKvListAsList().
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * (if @c NULL, the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if @c NULL, and the list is not storing pointers, sort and find
+ * functions will not work)
+ * @param elem_size the size of each element in bytes
+ * @return the created list
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
+CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator,
+ cx_compare_func comparator, size_t elem_size);
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr() if none is given.
+ *
+ * This function creates the list with cxKvListCreate() and immediately applies
+ * cxKvListAsMap(). If you want to use the returned object as a list, you can call
+ * cxKvListAsList() later.
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * (if @c NULL, the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if @c NULL, and the list is not storing pointers, sort and find
+ * functions will not work)
+ * @param elem_size the size of each element in bytes
+ * @return the created list wrapped into the CxMap interface
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
+CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
+ cx_compare_func comparator, size_t elem_size);
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * The list will use cxDefaultAllocator and no comparator function. If you want
+ * to call functions that need a comparator, you must either set one immediately
+ * after list creation or use cxKvListCreate().
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr().
+ *
+ * After creating the list, it can also be used as a map after converting the pointer
+ * to a CxMap pointer with cxKvListAsMap().
+ * When you want to use the list interface again, you can also convert the map pointer back
+ * with cxKvListAsList().
+ *
+ * @param elem_size (@c size_t) the size of each element in bytes
+ * @return (@c CxList*) the created list
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+#define cxKvListCreateSimple(elem_size) cxKvListCreate(NULL, NULL, elem_size)
+
+/**
+ * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
+ *
+ * The list will use cxDefaultAllocator and no comparator function. If you want
+ * to call functions that need a comparator, you must either set one immediately
+ * after list creation or use cxKvListCreate().
+ *
+ * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr().
+ *
+ * This macro behaves as if the list was created with cxKvListCreateSimple() and
+ * immediately followed up by cxKvListAsMap().
+ * If you want to use the returned object as a list, you can call cxKvListAsList() later.
+ *
+ * @param elem_size (@c size_t) the size of each element in bytes
+ * @return (@c CxMap*) the created list wrapped into the CxMap interface
+ * @see cxKvListAsMap()
+ * @see cxKvListAsList()
+ */
+#define cxKvListCreateAsMapSimple(elem_size) cxKvListCreateAsMap(NULL, NULL, elem_size)
+
+/**
+ * Converts a map pointer belonging to a key-value-List back to the original list pointer.
+ *
+ * @param map a map pointer that was returned by a call to cxKvListAsMap()
+ * @return the original list pointer
+ */
+cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
+CX_EXPORT CxList *cxKvListAsList(CxMap *map);
+
+/**
+ * Converts a map pointer belonging to a key-value-List back to the original list pointer.
+ *
+ * @param list a list created by cxKvListCreate() or cxKvListCreateSimple()
+ * @return a map pointer that lets you use the list as if it was a map
+ */
+cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
+CX_EXPORT CxMap *cxKvListAsMap(CxList *list);
+
+/**
+ * Sets or updates the key of a list item.
+ *
+ * This is, for example, useful when you have inserted an element using the CxList interface,
+ * and now you want to associate this element with a key.
+ *
+ * @param list the list
+ * @param index the index of the element in the list
+ * @param key the key
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see cxKvListSetKey()
+ */
+cx_attr_nonnull
+CX_EXPORT int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
+
+/**
+ * Inserts an item into the list at the specified index and associates it with the specified key.
+ *
+ * @param list the list
+ * @param index the index the inserted element shall have
+ * @param key the key
+ * @param value the value
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see cxKvListInsert()
+ */
+cx_attr_nonnull
+CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
+
+/**
+ * Sets or updates the key of a list item.
+ *
+ * This is, for example, useful when you have inserted an element using the CxList interface,
+ * and now you want to associate this element with a key.
+ *
+ * @param list (@c CxList*) the list
+ * @param index (@c size_t) the index of the element in the list
+ * @param key (any supported key type) the key
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see CX_HASH_KEY()
+ */
+#define cxKvListSetKey(list, index, key) cx_kv_list_set_key(list, index, CX_HASH_KEY(key))
+
+/**
+ * Inserts an item into the list at the specified index and associates it with the specified key.
+ *
+ * @param list (@c CxList*) the list
+ * @param index (@c size_t) the index the inserted element shall have
+ * @param key (any supported key type) the key
+ * @param value (@c void*) the value
+ * @retval zero success
+ * @retval non-zero memory allocation failure or the index is out of bounds
+ * @see CX_HASH_KEY()
+ */
+#define cxKvListInsert(list, index, key, value) cx_kv_list_insert(list, index, CX_HASH_KEY(key), value)
+
+
+/**
+ * Removes the key of a list item.
+ *
+ * This can be useful if you want to explicitly remove an item from the lookup map.
+ *
+ * If no key is associated with the item, nothing happens, and this function returns zero.
+ *
+ * @param list the list
+ * @param index the index of the element in the list
+ * @retval zero success
+ * @retval non-zero the index is out of bounds
+ */
+cx_attr_nonnull
+CX_EXPORT int cxKvListRemoveKey(CxList *list, size_t index);
+
+/**
+ * Returns the key of a list item.
+ *
+ * @param list the list
+ * @param index the index of the element in the list
+ * @return a pointer to the key or @c NULL when the index is out of bounds or the item does not have a key
+ */
+cx_attr_nonnull
+CX_EXPORT const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
+
+/**
+ * Adds an item into the list and associates it with the specified key.
+ *
+ * @param list (@c CxList*) the list
+ * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param value (@c void*) the value
+ * @retval zero success
+ * @retval non-zero memory allocation failure
+ */
+#define cxKvListAdd(list, key, value) cxKvListInsert(list, (list)->collection.size, key, value)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_KV_LIST_H
extern "C" {
#endif
+/**
+ * Metadata for a linked list.
+ */
+typedef struct cx_linked_list_s {
+ /** Base members. */
+ struct cx_list_s base;
+ /**
+ * Location of the prev pointer (mandatory).
+ */
+ off_t loc_prev;
+ /**
+ * Location of the next pointer (mandatory).
+ */
+ off_t loc_next;
+ /**
+ * Location of the payload (mandatory).
+ */
+ off_t loc_data;
+ /**
+ * Additional bytes to allocate @em behind the payload (e.g. for metadata).
+ */
+ size_t extra_data_len;
+ /**
+ * Pointer to the first node.
+ */
+ void *begin;
+ /**
+ * Pointer to the last node.
+ */
+ void *end;
+} cx_linked_list;
+
/**
* Allocates a linked list for storing elements with @p elem_size bytes each.
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * copies of the added elements, and the compare function will be automatically set
+ * to cx_cmp_ptr() if none is given.
*
* @param allocator the allocator for allocating the list nodes
* (if @c NULL, the cxDefaultAllocator will be used)
* @param elem_size the size of each element in bytes
* @return the created list
*/
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc(cxListFree, 1)
-cx_attr_export
-CxList *cxLinkedListCreate(
- const CxAllocator *allocator,
- cx_compare_func comparator,
- size_t elem_size
-);
+cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
+CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator,
+ cx_compare_func comparator, size_t elem_size);
/**
* Allocates a linked list for storing elements with @p elem_size bytes each.
* after list creation or use cxLinkedListCreate().
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements and the compare function will be automatically set
+ * copies of the added elements, and the compare function will be automatically set
* to cx_cmp_ptr().
*
* @param elem_size (@c size_t) the size of each element in bytes
* @return (@c CxList*) the created list
*/
#define cxLinkedListCreateSimple(elem_size) \
- cxLinkedListCreate(NULL, NULL, elem_size)
+ cxLinkedListCreate(NULL, NULL, elem_size)
/**
* Finds the node at a certain index.
*
* This function can be used to start at an arbitrary position within the list.
- * If the search index is large than the start index, @p loc_advance must denote
- * the location of some sort of @c next pointer (i.e. a pointer to the next node).
+ * If the search index is larger than the start index, @p loc_advance must denote
+ * the location of a @c next pointer (i.e., a pointer to the next node).
* But it is also possible that the search index is smaller than the start index
- * (e.g. in cases where traversing a list backwards is faster) in which case
- * @p loc_advance must denote the location of some sort of @c prev pointer
- * (i.e. a pointer to the previous node).
+ * (e.g., in cases where traversing a list backwards is faster).
+ * In that case @p loc_advance must denote the location of a @c prev pointer
+ * (i.e., a pointer to the previous node).
*
* @param start a pointer to the start node
* @param start_index the start index
* @param index the search index
* @return the node found at the specified index
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-void *cx_linked_list_at(
- const void *start,
- size_t start_index,
- ptrdiff_t loc_advance,
- size_t index
-);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT void *cx_linked_list_at(const void *start,size_t start_index,
+ ptrdiff_t loc_advance, size_t index);
/**
* Finds the node containing an element within a linked list.
* @param elem a pointer to the element to find
* @param found_index an optional pointer where the index of the found node
* (given that @p start has index 0) is stored
- * @return the index of the element, if found - unspecified if not found
+ * @return a pointer to the found node or @c NULL if no matching node was found
*/
cx_attr_nonnull_arg(1, 4, 5)
-cx_attr_export
-void *cx_linked_list_find(
- const void *start,
- ptrdiff_t loc_advance,
- ptrdiff_t loc_data,
- cx_compare_func cmp_func,
- const void *elem,
- size_t *found_index
-);
+CX_EXPORT void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance,
+ ptrdiff_t loc_data, cx_compare_func cmp_func, const void *elem,
+ size_t *found_index);
/**
* Finds the first node in a linked list.
* @param loc_prev the location of the @c prev pointer
* @return a pointer to the first node
*/
-cx_attr_nonnull
-cx_attr_returns_nonnull
-cx_attr_export
-void *cx_linked_list_first(
- const void *node,
- ptrdiff_t loc_prev
-);
+cx_attr_nonnull cx_attr_returns_nonnull
+CX_EXPORT void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
/**
* Finds the last node in a linked list.
* @param loc_next the location of the @c next pointer
* @return a pointer to the last node
*/
-cx_attr_nonnull
-cx_attr_returns_nonnull
-cx_attr_export
-void *cx_linked_list_last(
- const void *node,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull cx_attr_returns_nonnull
+CX_EXPORT void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
/**
* Finds the predecessor of a node in case it is not linked.
* @return the node or @c NULL if @p node has no predecessor
*/
cx_attr_nonnull
-cx_attr_export
-void *cx_linked_list_prev(
- const void *begin,
- ptrdiff_t loc_next,
- const void *node
-);
+CX_EXPORT void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node);
/**
* Adds a new node to a linked list.
- * The node must not be part of any list already.
+ * The node must not be part of any list yet.
*
* @remark One of the pointers @p begin or @p end may be @c NULL, but not both.
*
* @param new_node a pointer to the node that shall be appended
*/
cx_attr_nonnull_arg(5)
-cx_attr_export
-void cx_linked_list_add(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *new_node
-);
+CX_EXPORT void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
/**
* Prepends a new node to a linked list.
- * The node must not be part of any list already.
+ * The node must not be part of any list yet.
*
* @remark One of the pointers @p begin or @p end may be @c NULL, but not both.
*
* @param new_node a pointer to the node that shall be prepended
*/
cx_attr_nonnull_arg(5)
-cx_attr_export
-void cx_linked_list_prepend(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *new_node
-);
+CX_EXPORT void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
/**
* Links two nodes.
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
cx_attr_nonnull
-cx_attr_export
-void cx_linked_list_link(
- void *left,
- void *right,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+CX_EXPORT void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Unlinks two nodes.
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
cx_attr_nonnull
-cx_attr_export
-void cx_linked_list_unlink(
- void *left,
- void *right,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+CX_EXPORT void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Inserts a new node after a given node of a linked list.
- * The new node must not be part of any list already.
+ * The new node must not be part of any list yet.
*
* @note If you specify @c NULL as the @p node to insert after, this function needs either the @p begin or
* the @p end pointer to determine the start of the list. Then the new node will be prepended to the list.
* @param new_node a pointer to the node that shall be inserted
*/
cx_attr_nonnull_arg(6)
-cx_attr_export
-void cx_linked_list_insert(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *node,
- void *new_node
-);
+CX_EXPORT void cx_linked_list_insert(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *new_node);
/**
* Inserts a chain of nodes after a given node of a linked list.
- * The chain must not be part of any list already.
+ * The chain must not be part of any list yet.
*
* If you do not explicitly specify the end of the chain, it will be determined by traversing
* the @c next pointer.
* @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined)
*/
cx_attr_nonnull_arg(6)
-cx_attr_export
-void cx_linked_list_insert_chain(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *node,
- void *insert_begin,
- void *insert_end
-);
+CX_EXPORT void cx_linked_list_insert_chain(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *insert_begin, void *insert_end);
/**
* Inserts a node into a sorted linked list.
- * The new node must not be part of any list already.
+ * The new node must not be part of any list yet.
*
* If the list starting with the node pointed to by @p begin is not sorted
* already, the behavior is undefined.
* @param cmp_func a compare function that will receive the node pointers
*/
cx_attr_nonnull_arg(1, 5, 6)
-cx_attr_export
-void cx_linked_list_insert_sorted(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *new_node,
- cx_compare_func cmp_func
-);
+CX_EXPORT void cx_linked_list_insert_sorted(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func);
/**
* Inserts a chain of nodes into a sorted linked list.
- * The chain must not be part of any list already.
+ * The chain must not be part of any list yet.
*
* If either the list starting with the node pointed to by @p begin or the list
* starting with @p insert_begin is not sorted, the behavior is undefined.
*
* @attention In contrast to cx_linked_list_insert_chain(), the source chain
* will be broken and inserted into the target list so that the resulting list
- * will be sorted according to @p cmp_func. That means, each node in the source
+ * will be sorted according to @p cmp_func. That means each node in the source
* chain may be re-linked with nodes from the target list.
*
* @param begin a pointer to the beginning node pointer (required)
* @param cmp_func a compare function that will receive the node pointers
*/
cx_attr_nonnull_arg(1, 5, 6)
-cx_attr_export
-void cx_linked_list_insert_sorted_chain(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *insert_begin,
- cx_compare_func cmp_func
-);
+CX_EXPORT void cx_linked_list_insert_sorted_chain(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
+
+/**
+ * Inserts a node into a sorted linked list if no other node with the same value already exists.
+ * The new node must not be part of any list yet.
+ *
+ * If the list starting with the node pointed to by @p begin is not sorted
+ * already, the behavior is undefined.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @retval zero when the node was inserted
+ * @retval non-zero when a node with the same value already exists
+ */
+cx_attr_nonnull_arg(1, 5, 6)
+CX_EXPORT int cx_linked_list_insert_unique(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func);
+
+/**
+ * Inserts a chain of nodes into a sorted linked list, avoiding duplicates.
+ * The chain must not be part of any list yet.
+ *
+ * If either the list starting with the node pointed to by @p begin or the list
+ * starting with @p insert_begin is not sorted, the behavior is undefined.
+ *
+ * @attention In contrast to cx_linked_list_insert_sorted(), not all nodes of the
+ * chain might be added. This function returns a new chain consisting of all the duplicates.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates)
+ */
+cx_attr_nonnull_arg(1, 5, 6) cx_attr_nodiscard
+CX_EXPORT void *cx_linked_list_insert_unique_chain(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
/**
* Removes a chain of nodes from the linked list.
*
- * If one of the nodes to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end)
+ * If one of the nodes to remove is the beginning (resp. end) node of the list, and if @p begin (resp. @p end)
* addresses are provided, the pointers are adjusted accordingly.
*
* The following combinations of arguments are valid (more arguments are optional):
* @return the actual number of nodes that were removed (can be less when the list did not have enough nodes)
*/
cx_attr_nonnull_arg(5)
-cx_attr_export
-size_t cx_linked_list_remove_chain(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *node,
- size_t num
-);
+CX_EXPORT size_t cx_linked_list_remove_chain(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, size_t num);
/**
* Removes a node from the linked list.
*
- * If the node to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end)
+ * If the node to remove is the beginning (resp. end) node of the list, and if @p begin (resp. @p end)
* addresses are provided, the pointers are adjusted accordingly.
*
* The following combinations of arguments are valid (more arguments are optional):
* @param node the node to remove
*/
cx_attr_nonnull_arg(5)
-static inline void cx_linked_list_remove(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *node
-) {
- cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node, 1);
-}
+CX_EXPORT void cx_linked_list_remove(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node);
/**
* Determines the size of a linked list starting with @p node.
* @return the size of the list or zero if @p node is @c NULL
*/
cx_attr_nodiscard
-cx_attr_export
-size_t cx_linked_list_size(
- const void *node,
- ptrdiff_t loc_next
-);
+CX_EXPORT size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
/**
* Sorts a linked list based on a comparison function.
* @param cmp_func the compare function defining the sort order
*/
cx_attr_nonnull_arg(1, 6)
-cx_attr_export
-void cx_linked_list_sort(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- ptrdiff_t loc_data,
- cx_compare_func cmp_func
-);
+CX_EXPORT void cx_linked_list_sort(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func cmp_func);
/**
* Compares two lists element wise.
*
- * @attention Both list must have the same structure.
+ * @attention Both lists must have the same structure.
*
* @param begin_left the beginning of the left list (@c NULL denotes an empty list)
* @param begin_right the beginning of the right list (@c NULL denotes an empty list)
* right list, positive if the left list is larger than the right list, zero if both lists are equal.
*/
cx_attr_nonnull_arg(5)
-cx_attr_export
-int cx_linked_list_compare(
- const void *begin_left,
- const void *begin_right,
- ptrdiff_t loc_advance,
- ptrdiff_t loc_data,
- cx_compare_func cmp_func
-);
+CX_EXPORT int cx_linked_list_compare(const void *begin_left, const void *begin_right,
+ ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func);
/**
* Reverses the order of the nodes in a linked list.
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-void cx_linked_list_reverse(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+CX_EXPORT void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next);
#ifdef __cplusplus
} // extern "C"
/**
* Member function for inserting a single element.
- * The data pointer may be @c NULL in which case the function shall only allocate memory.
- * Returns a pointer to the data of the inserted element.
+ * The data pointer may be @c NULL, in which case the function shall only allocate memory.
+ * Returns a pointer to the allocated memory or @c NULL if allocation fails.
*/
- void *(*insert_element)(
- struct cx_list_s *list,
- size_t index,
- const void *data
- );
+ void *(*insert_element)(struct cx_list_s *list, size_t index, const void *data);
/**
* Member function for inserting multiple elements.
*
+ * The data pointer may be @c NULL, in which case the function shall only allocate memory.
+ * Returns the number of successfully inserted or allocated elements.
+ *
* @see cx_list_default_insert_array()
*/
- size_t (*insert_array)(
- struct cx_list_s *list,
- size_t index,
- const void *data,
- size_t n
- );
+ size_t (*insert_array)(struct cx_list_s *list, size_t index, const void *data, size_t n);
/**
* Member function for inserting sorted elements into a sorted list.
+ * Returns the number of successfully inserted elements.
*
* @see cx_list_default_insert_sorted()
*/
- size_t (*insert_sorted)(
- struct cx_list_s *list,
- const void *sorted_data,
- size_t n
- );
+ size_t (*insert_sorted)(struct cx_list_s *list, const void *sorted_data, size_t n);
+
+ /**
+ * Member function for inserting multiple elements if they do not exist.
+ * Implementations shall return the number of successfully processed elements
+ * (including those which were not added because they are already contained).
+ * @see cx_list_default_insert_unique()
+ */
+ size_t (*insert_unique)(struct cx_list_s *list, const void *sorted_data, size_t n);
/**
* Member function for inserting an element relative to an iterator position.
*/
- int (*insert_iter)(
- struct cx_iterator_s *iter,
- const void *elem,
- int prepend
- );
+ int (*insert_iter)(struct cx_iterator_s *iter, const void *elem, int prepend);
/**
* Member function for removing elements.
* The function SHALL return the actual number of elements removed, which
* might be lower than @p num when going out of bounds.
*/
- size_t (*remove)(
- struct cx_list_s *list,
- size_t index,
- size_t num,
- void *targetbuf
- );
+ size_t (*remove)(struct cx_list_s *list, size_t index, size_t num, void *targetbuf);
/**
* Member function for removing all elements.
*
* @see cx_list_default_swap()
*/
- int (*swap)(
- struct cx_list_s *list,
- size_t i,
- size_t j
- );
+ int (*swap)(struct cx_list_s *list, size_t i, size_t j);
/**
* Member function for element lookup.
*/
- void *(*at)(
- const struct cx_list_s *list,
- size_t index
- );
+ void *(*at)(const struct cx_list_s *list, size_t index);
/**
* Member function for finding and optionally removing an element.
*/
- size_t (*find_remove)(
- struct cx_list_s *list,
- const void *elem,
- bool remove
- );
+ size_t (*find_remove)(struct cx_list_s *list, const void *elem, bool remove);
/**
* Member function for sorting the list.
/**
* Optional member function for comparing this list
* to another list of the same type.
- * If set to @c NULL, comparison won't be optimized.
+ * If set to @c NULL, the comparison won't be optimized.
*/
- cx_attr_nonnull
- int (*compare)(
- const struct cx_list_s *list,
- const struct cx_list_s *other
- );
+ int (*compare)(const struct cx_list_s *list, const struct cx_list_s *other);
/**
* Member function for reversing the order of the items.
/**
* Member function for returning an iterator pointing to the specified index.
*/
- struct cx_iterator_s (*iterator)(
- const struct cx_list_s *list,
- size_t index,
- bool backward
- );
+ struct cx_iterator_s (*iterator)(const struct cx_list_s *list, size_t index, bool backward);
};
/**
*
* Writing to that list is not allowed.
*
- * You can use this is a placeholder for initializing CxList pointers
+ * You can use this as a placeholder for initializing CxList pointers
* for which you do not want to reserve memory right from the beginning.
*/
-cx_attr_export
-extern CxList *const cxEmptyList;
+CX_EXPORT extern CxList *const cxEmptyList;
/**
* Default implementation of an array insert.
* @return the number of elements actually inserted
*/
cx_attr_nonnull
-cx_attr_export
-size_t cx_list_default_insert_array(
- struct cx_list_s *list,
- size_t index,
- const void *data,
- size_t n
-);
+CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list,
+ size_t index, const void *data, size_t n);
/**
* Default implementation of a sorted insert.
* @return the number of elements actually inserted
*/
cx_attr_nonnull
-cx_attr_export
-size_t cx_list_default_insert_sorted(
- struct cx_list_s *list,
- const void *sorted_data,
- size_t n
-);
+CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list,
+ const void *sorted_data, size_t n);
+
+/**
+ * Default implementation of an array insert where only elements are inserted when they don't exist in the list.
+ *
+ * This function is similar to cx_list_default_insert_sorted(), except it skips elements that are already in the list.
+ *
+ * @note The return value of this function denotes the number of elements from the @p sorted_data that are definitely
+ * contained in the list after completing the call. It is @em not the number of elements that were newly inserted.
+ * That means, when no error occurred, the return value should be @p n.
+ *
+ * Use this in your own list class if you do not want to implement an optimized version for your list.
+ *
+ * @param list the list
+ * @param sorted_data a pointer to the array of pre-sorted data to insert
+ * @param n the number of elements to insert
+ * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list,
+ const void *sorted_data, size_t n);
/**
* Default unoptimized sort implementation.
* @param list the list that shall be sorted
*/
cx_attr_nonnull
-cx_attr_export
-void cx_list_default_sort(struct cx_list_s *list);
+CX_EXPORT void cx_list_default_sort(struct cx_list_s *list);
/**
* Default unoptimized swap implementation.
* allocation for the temporary buffer fails
*/
cx_attr_nonnull
-cx_attr_export
-int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
+CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
/**
* Initializes a list struct.
*
* Only use this function if you are creating your own list implementation.
* The purpose of this function is to be called in the initialization code
- * of your list, to set certain members correctly.
+ * of your list to set certain members correctly.
*
* This is particularly important when you want your list to support
* #CX_STORE_POINTERS as @p elem_size. This function will wrap the list
* class accordingly and make sure that you can implement your list as if
- * it was only storing objects and the wrapper will automatically enable
+ * it was only storing objects, and the wrapper will automatically enable
* the feature of storing pointers.
*
* @par Example
* @param elem_size the size of one element
*/
cx_attr_nonnull_arg(1, 2, 3)
-cx_attr_export
-void cx_list_init(
- struct cx_list_s *list,
- struct cx_list_class_s *cl,
- const struct cx_allocator_s *allocator,
- cx_compare_func comparator,
- size_t elem_size
-);
+CX_EXPORT void cx_list_init(struct cx_list_s *list,
+ struct cx_list_class_s *cl, const struct cx_allocator_s *allocator,
+ cx_compare_func comparator, size_t elem_size);
/**
* Returns the number of elements currently stored in the list.
* @return the number of currently stored elements
*/
cx_attr_nonnull
-static inline size_t cxListSize(const CxList *list) {
- return list->collection.size;
-}
+CX_EXPORT size_t cxListSize(const CxList *list);
/**
* Adds an item to the end of the list.
* @see cxListEmplace()
*/
cx_attr_nonnull
-static inline int cxListAdd(
- CxList *list,
- const void *elem
-) {
- list->collection.sorted = false;
- return list->cl->insert_element(list, list->collection.size, elem) == NULL;
-}
+CX_EXPORT int cxListAdd(CxList *list, const void *elem);
/**
* Adds multiple items to the end of the list.
* If there is not enough memory to add all elements, the returned value is
* less than @p n.
*
- * If this list is storing pointers instead of objects @p array is expected to
+ * If this list is storing pointers instead of objects, @p array is expected to
* be an array of pointers.
*
* @param list the list
* @param array a pointer to the elements to add
* @param n the number of elements to add
* @return the number of added elements
+ * @see cxListEmplaceArray()
*/
cx_attr_nonnull
-static inline size_t cxListAddArray(
- CxList *list,
- const void *array,
- size_t n
-) {
- list->collection.sorted = false;
- return list->cl->insert_array(list, list->collection.size, array, n);
-}
+CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n);
/**
* Inserts an item at the specified index.
*
- * If @p index equals the list @c size, this is effectively cxListAdd().
+ * If the @p index equals the list @c size, this is effectively cxListAdd().
*
* @param list the list
* @param index the index the element shall have
* @see cxListEmplaceAt()
*/
cx_attr_nonnull
-static inline int cxListInsert(
- CxList *list,
- size_t index,
- const void *elem
-) {
- list->collection.sorted = false;
- return list->cl->insert_element(list, index, elem) == NULL;
-}
+CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem);
/**
* Allocates memory for an element at the specified index and returns a pointer to that memory.
* @param index the index where to emplace the element
* @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds
* @see cxListEmplace()
+ * @see cxListEmplaceArrayAt()
* @see cxListInsert()
*/
cx_attr_nonnull
-static inline void *cxListEmplaceAt(CxList *list, size_t index) {
- list->collection.sorted = false;
- return list->cl->insert_element(list, index, NULL);
-}
-
+CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index);
/**
* Allocates memory for an element at the end of the list and returns a pointer to that memory.
* @see cxListAdd()
*/
cx_attr_nonnull
-static inline void *cxListEmplace(CxList *list) {
- list->collection.sorted = false;
- return list->cl->insert_element(list, list->collection.size, NULL);
-}
+CX_EXPORT void *cxListEmplace(CxList *list);
+
+/**
+ * Allocates memory for multiple elements and returns an iterator.
+ *
+ * The iterator will only iterate over the successfully allocated elements.
+ * The @c elem_count attribute is set to that number, and the @c index attribute
+ * will range from zero to @c elem_count minus one.
+ *
+ * @remark When the list is storing pointers, the iterator will iterate over
+ * the @c void** elements.
+ *
+ * @param list the list
+ * @param index the index where to insert the new data
+ * @param n the number of elements for which to allocate the memory
+ * @return an iterator, iterating over the new memory
+ * @see cxListEmplaceAt()
+ * @see cxListInsertArray()
+ */
+cx_attr_nonnull
+CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
+
+/**
+ * Allocates memory for multiple elements and returns an iterator.
+ *
+ * The iterator will only iterate over the successfully allocated elements.
+ * The @c elem_count attribute is set to that number, and the @c index attribute
+ * will range from zero to @c elem_count minus one.
+ *
+ * @remark When the list is storing pointers, the iterator will iterate over
+ * the @c void** elements.
+ *
+ * @param list the list
+ * @param n the number of elements for which to allocate the memory
+ * @return an iterator, iterating over the new memory
+ * @see cxListEmplace()
+ * @see cxListAddArray()
+ */
+cx_attr_nonnull
+CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n);
/**
* Inserts an item into a sorted list.
* @retval non-zero memory allocation failure
*/
cx_attr_nonnull
-static inline int cxListInsertSorted(
- CxList *list,
- const void *elem
-) {
- list->collection.sorted = true; // guaranteed by definition
- const void *data = list->collection.store_pointer ? &elem : elem;
- return list->cl->insert_sorted(list, data, 1) == 0;
-}
+CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem);
+
+/**
+ * Inserts an item into a list if it does not exist.
+ *
+ * If the list is not sorted already, this function will check all elements
+ * and append the new element when it was not found.
+ * It is strongly recommended to use this function only on sorted lists, where
+ * the element, if it is not contained, is inserted at the correct position.
+ *
+ * @param list the list
+ * @param elem a pointer to the element to add
+ * @retval zero success (also when the element was already in the list)
+ * @retval non-zero memory allocation failure
+ */
+cx_attr_nonnull
+CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem);
/**
* Inserts multiple items to the list at the specified index.
- * If @p index equals the list size, this is effectively cxListAddArray().
+ * If the @p index equals the list size, this is effectively cxListAddArray().
*
* This method is usually more efficient than invoking cxListInsert()
* multiple times.
* If there is not enough memory to add all elements, the returned value is
* less than @p n.
*
- * If this list is storing pointers instead of objects @p array is expected to
+ * If this list is storing pointers instead of objects, @p array is expected to
* be an array of pointers.
*
* @param list the list
* @param array a pointer to the elements to add
* @param n the number of elements to add
* @return the number of added elements
+ * @see cxListEmplaceArrayAt()
*/
cx_attr_nonnull
-static inline size_t cxListInsertArray(
- CxList *list,
- size_t index,
- const void *array,
- size_t n
-) {
- list->collection.sorted = false;
- return list->cl->insert_array(list, index, array, n);
-}
+CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
/**
* Inserts a sorted array into a sorted list.
*
- * This method is usually more efficient than inserting each element separately,
+ * This method is usually more efficient than inserting each element separately
* because consecutive chunks of sorted data are inserted in one pass.
*
* If there is not enough memory to add all elements, the returned value is
* less than @p n.
*
- * If this list is storing pointers instead of objects @p array is expected to
+ * If this list is storing pointers instead of objects, @p array is expected to
* be an array of pointers.
*
* If the list is not sorted already, the behavior is undefined.
* @return the number of added elements
*/
cx_attr_nonnull
-static inline size_t cxListInsertSortedArray(
- CxList *list,
- const void *array,
- size_t n
-) {
- list->collection.sorted = true; // guaranteed by definition
- return list->cl->insert_sorted(list, array, n);
-}
+CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n);
+
+/**
+ * Inserts an array into a list, skipping duplicates.
+ *
+ * The @p list does not need to be sorted (in contrast to cxListInsertSortedArray()).
+ * But it is strongly recommended to use this function only on sorted lists,
+ * because otherwise it will fall back to an inefficient algorithm which inserts
+ * all elements one by one.
+ * If the @p list is not sorted, the @p array also does not need to be sorted.
+ * But when the @p list is sorted, the @p array must also be sorted.
+ *
+ * This method is usually more efficient than inserting each element separately
+ * because consecutive chunks of sorted data are inserted in one pass.
+ *
+ * If there is not enough memory to add all elements, the returned value is
+ * less than @p n.
+ *
+ * @note The return value of this function denotes the number of elements
+ * from the @p sorted_data that are definitely contained in the list after
+ * completing the call. It is @em not the number of elements that were newly
+ * inserted. That means, when no error occurred, the return value should
+ * be @p n.
+ *
+ * If this list is storing pointers instead of objects @p array is expected to
+ * be an array of pointers.
+ *
+ * @param list the list
+ * @param array a pointer to the elements to add
+ * @param n the number of elements to add
+ * @return the number of added elements
+ *
+ * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n);
/**
* Inserts an element after the current location of the specified iterator.
* @see cxListInsertBefore()
*/
cx_attr_nonnull
-static inline int cxListInsertAfter(
- CxIterator *iter,
- const void *elem
-) {
- CxList* list = (CxList*)iter->src_handle.m;
- list->collection.sorted = false;
- return list->cl->insert_iter(iter, elem, 0);
-}
+CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem);
/**
* Inserts an element before the current location of the specified iterator.
* @see cxListInsertAfter()
*/
cx_attr_nonnull
-static inline int cxListInsertBefore(
- CxIterator *iter,
- const void *elem
-) {
- CxList* list = (CxList*)iter->src_handle.m;
- list->collection.sorted = false;
- return list->cl->insert_iter(iter, elem, 1);
-}
+CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem);
/**
* Removes the element at the specified index.
* @retval non-zero index out of bounds
*/
cx_attr_nonnull
-static inline int cxListRemove(
- CxList *list,
- size_t index
-) {
- return list->cl->remove(list, index, 1, NULL) == 0;
-}
+CX_EXPORT int cxListRemove(CxList *list, size_t index);
/**
* Removes and returns the element at the specified index.
* @retval zero success
* @retval non-zero index out of bounds
*/
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cxListRemoveAndGet(
- CxList *list,
- size_t index,
- void *targetbuf
-) {
- return list->cl->remove(list, index, 1, targetbuf) == 0;
-}
+cx_attr_nonnull cx_attr_access_w(3)
+CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
/**
* Removes and returns the first element of the list.
* @param list the list
* @param targetbuf a buffer where to copy the element
* @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
* @see cxListPopFront()
* @see cxListRemoveAndGetLast()
*/
-cx_attr_nonnull
-cx_attr_access_w(2)
-static inline int cxListRemoveAndGetFirst(
- CxList *list,
- void *targetbuf
-) {
- return list->cl->remove(list, 0, 1, targetbuf) == 0;
-}
+cx_attr_nonnull cx_attr_access_w(2)
+CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
/**
* Removes and returns the first element of the list.
* @param list (@c CxList*) the list
* @param targetbuf (@c void*) a buffer where to copy the element
* @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
* @see cxListRemoveAndGetFirst()
* @see cxListPop()
*/
* @param list the list
* @param targetbuf a buffer where to copy the element
* @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
*/
-cx_attr_nonnull
-cx_attr_access_w(2)
-static inline int cxListRemoveAndGetLast(
- CxList *list,
- void *targetbuf
-) {
- // note: index may wrap - member function will catch that
- return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0;
-}
+cx_attr_nonnull cx_attr_access_w(2)
+CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
/**
* Removes and returns the last element of the list.
* @param list (@c CxList*) the list
* @param targetbuf (@c void*) a buffer where to copy the element
* @retval zero success
- * @retval non-zero list is empty
+ * @retval non-zero the list is empty
* @see cxListRemoveAndGetLast()
* @see cxListPopFront()
*/
* @return the actual number of removed elements
*/
cx_attr_nonnull
-static inline size_t cxListRemoveArray(
- CxList *list,
- size_t index,
- size_t num
-) {
- return list->cl->remove(list, index, num, NULL);
-}
+CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
/**
* Removes and returns multiple elements starting at the specified index.
* @param targetbuf a buffer where to copy the elements
* @return the actual number of removed elements
*/
-cx_attr_nonnull
-cx_attr_access_w(4)
-static inline size_t cxListRemoveArrayAndGet(
- CxList *list,
- size_t index,
- size_t num,
- void *targetbuf
-) {
- return list->cl->remove(list, index, num, targetbuf);
-}
+cx_attr_nonnull cx_attr_access_w(4)
+CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf);
/**
* Removes all elements from this list.
* @param list the list
*/
cx_attr_nonnull
-static inline void cxListClear(CxList *list) {
- list->collection.sorted = true; // empty lists are always sorted
- list->cl->clear(list);
-}
+CX_EXPORT void cxListClear(CxList *list);
/**
* Swaps two items in the list.
* or the swap needed extra memory, but allocation failed
*/
cx_attr_nonnull
-static inline int cxListSwap(
- CxList *list,
- size_t i,
- size_t j
-) {
- list->collection.sorted = false;
- return list->cl->swap(list, i, j);
-}
+CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j);
/**
* Returns a pointer to the element at the specified index.
* @return a pointer to the element or @c NULL if the index is out of bounds
*/
cx_attr_nonnull
-static inline void *cxListAt(
- const CxList *list,
- size_t index
-) {
- return list->cl->at(list, index);
-}
+CX_EXPORT void *cxListAt(const CxList *list, size_t index);
/**
* Returns a pointer to the first element.
* @return a pointer to the first element or @c NULL if the list is empty
*/
cx_attr_nonnull
-static inline void *cxListFirst(const CxList *list) {
- return list->cl->at(list, 0);
-}
+CX_EXPORT void *cxListFirst(const CxList *list);
/**
* Returns a pointer to the last element.
* @return a pointer to the last element or @c NULL if the list is empty
*/
cx_attr_nonnull
-static inline void *cxListLast(const CxList *list) {
- return list->cl->at(list, list->collection.size - 1);
-}
+CX_EXPORT void *cxListLast(const CxList *list);
/**
- * Sets the element at the specified index in the list
+ * Sets the element at the specified index in the list.
+ *
+ * This overwrites the element in-place without calling any destructor
+ * on the overwritten element.
*
* @param list the list to set the element in
* @param index the index to set the element at
* @retval non-zero when index is out of bounds
*/
cx_attr_nonnull
-cx_attr_export
-int cxListSet(
- CxList *list,
- size_t index,
- const void *elem
-);
+CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem);
/**
* Returns an iterator pointing to the item at the specified index.
*
* The returned iterator is position-aware.
*
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
-static inline CxIterator cxListIteratorAt(
- const CxList *list,
- size_t index
-) {
- return list->cl->iterator(list, index, false);
-}
+CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index);
/**
* Returns a backwards iterator pointing to the item at the specified index.
*
* The returned iterator is position-aware.
*
- * If the index is out of range, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @param index the index where the iterator shall point at
- * @return a new iterator
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline CxIterator cxListBackwardsIteratorAt(
- const CxList *list,
- size_t index
-) {
- return list->cl->iterator(list, index, true);
-}
-
-/**
- * Returns a mutating iterator pointing to the item at the specified index.
- *
- * The returned iterator is position-aware.
- *
- * If the index is out of range, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @param index the index where the iterator shall point at
- * @return a new iterator
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxIterator cxListMutIteratorAt(
- CxList *list,
- size_t index
-);
-
-/**
- * Returns a mutating backwards iterator pointing to the item at the
- * specified index.
- *
- * The returned iterator is position-aware.
- *
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
-cx_attr_export
-CxIterator cxListMutBackwardsIteratorAt(
- CxList *list,
- size_t index
-);
+CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
/**
* Returns an iterator pointing to the first item of the list.
* @return a new iterator
*/
cx_attr_nodiscard
-static inline CxIterator cxListIterator(const CxList *list) {
- if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, 0, false);
-}
-
-/**
- * Returns a mutating iterator pointing to the first item of the list.
- *
- * The returned iterator is position-aware.
- *
- * If the list is empty or @c NULL, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @return a new iterator
- */
-cx_attr_nodiscard
-static inline CxIterator cxListMutIterator(CxList *list) {
- if (list == NULL) list = cxEmptyList;
- return cxListMutIteratorAt(list, 0);
-}
-
+CX_EXPORT CxIterator cxListIterator(const CxList *list);
/**
* Returns a backwards iterator pointing to the last item of the list.
* @return a new iterator
*/
cx_attr_nodiscard
-static inline CxIterator cxListBackwardsIterator(const CxList *list) {
- if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, list->collection.size - 1, true);
-}
-
-/**
- * Returns a mutating backwards iterator pointing to the last item of the list.
- *
- * The returned iterator is position-aware.
- *
- * If the list is empty or @c NULL, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @return a new iterator
- */
-cx_attr_nodiscard
-static inline CxIterator cxListMutBackwardsIterator(CxList *list) {
- if (list == NULL) list = cxEmptyList;
- return cxListMutBackwardsIteratorAt(list, list->collection.size - 1);
-}
+CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list);
/**
* Returns the index of the first element that equals @p elem.
* @see cxListIndexValid()
* @see cxListContains()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline size_t cxListFind(
- const CxList *list,
- const void *elem
-) {
- return list->cl->find_remove((CxList*)list, elem, false);
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT size_t cxListFind(const CxList *list, const void *elem);
/**
- * Checks, if the list contains the specified element.
+ * Checks if the list contains the specified element.
*
* The elements are compared with the list's comparator function.
*
* @retval false if the element is not contained
* @see cxListFind()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline bool cxListContains(
- const CxList* list,
- const void* elem
-) {
- return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size;
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT bool cxListContains(const CxList* list, const void* elem);
/**
* Checks if the specified index is within bounds.
* @retval true if the index is within bounds
* @retval false if the index is out of bounds
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline bool cxListIndexValid(const CxList *list, size_t index) {
- return index < list->collection.size;
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index);
/**
* Removes and returns the index of the first element that equals @p elem.
* @see cxListIndexValid()
*/
cx_attr_nonnull
-static inline size_t cxListFindRemove(
- CxList *list,
- const void *elem
-) {
- return list->cl->find_remove(list, elem, true);
-}
+CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem);
/**
* Sorts the list.
* @param list the list
*/
cx_attr_nonnull
-static inline void cxListSort(CxList *list) {
- if (list->collection.sorted) return;
- list->cl->sort(list);
- list->collection.sorted = true;
-}
+CX_EXPORT void cxListSort(CxList *list);
/**
* Reverses the order of the items.
* @param list the list
*/
cx_attr_nonnull
-static inline void cxListReverse(CxList *list) {
- // still sorted, but not according to the cmp_func
- list->collection.sorted = false;
- list->cl->reverse(list);
-}
+CX_EXPORT void cxListReverse(CxList *list);
/**
* Compares a list to another list of the same type.
* @param list the list
* @param other the list to compare to
* @retval zero both lists are equal element wise
- * @retval negative the first list is smaller
+ * @retval negative the first list is smaller,
* or the first non-equal element in the first list is smaller
* @retval positive the first list is larger
* or the first non-equal element in the first list is larger
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-int cxListCompare(
- const CxList *list,
- const CxList *other
-);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cxListCompare(const CxList *list, const CxList *other);
/**
* Deallocates the memory of the specified list structure.
*
* Also calls the content destructor functions for each element, if specified.
*
- * @param list the list which shall be freed
+ * @param list the list that shall be freed
*/
-cx_attr_export
-void cxListFree(CxList *list);
+CX_EXPORT void cxListFree(CxList *list);
#ifdef __cplusplus
/**
* Handle for the source map.
*/
- union {
- /**
- * Access for mutating iterators.
- */
- CxMap *m;
- /**
- * Access for normal iterators.
- */
- const CxMap *c;
- } map;
+ CxMap *map;
/**
* Handle for the current element.
/**
* Add or overwrite an element.
+ * If the @p value is @c NULL, the implementation
+ * shall only allocate memory instead of adding an existing value to the map.
+ * Returns a pointer to the allocated memory or @c NULL if allocation fails.
*/
- int (*put)(
- CxMap *map,
- CxHashKey key,
- void *value
- );
+ void *(*put)(CxMap *map, CxHashKey key, void *value);
/**
* Returns an element.
*/
- void *(*get)(
- const CxMap *map,
- CxHashKey key
- );
+ void *(*get)(const CxMap *map, CxHashKey key);
/**
* Removes an element.
* The function SHALL return zero when the @p key was found and
* non-zero, otherwise.
*/
- int (*remove)(
- CxMap *map,
- CxHashKey key,
- void *targetbuf
- );
+ int (*remove)(CxMap *map, CxHashKey key, void *targetbuf);
/**
* Creates an iterator for this map.
*
* Writing to that map is not allowed.
*
- * You can use this is a placeholder for initializing CxMap pointers
+ * You can use this as a placeholder for initializing CxMap pointers
* for which you do not want to reserve memory right from the beginning.
*/
-cx_attr_export
-extern CxMap *const cxEmptyMap;
+CX_EXPORT extern CxMap *const cxEmptyMap;
/**
* Deallocates the memory of the specified map.
*
* @param map the map to be freed
*/
-cx_attr_export
-void cxMapFree(CxMap *map);
+CX_EXPORT void cxMapFree(CxMap *map);
/**
* @param map the map to be cleared
*/
cx_attr_nonnull
-static inline void cxMapClear(CxMap *map) {
- map->cl->clear(map);
-}
+CX_EXPORT void cxMapClear(CxMap *map);
/**
* Returns the number of elements in this map.
* @return the number of stored elements
*/
cx_attr_nonnull
-static inline size_t cxMapSize(const CxMap *map) {
- return map->collection.size;
-}
+CX_EXPORT size_t cxMapSize(const CxMap *map);
/**
* Creates a value iterator for a map.
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored values
*/
-cx_attr_nonnull
cx_attr_nodiscard
-static inline CxMapIterator cxMapIteratorValues(const CxMap *map) {
- return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
-}
+CX_EXPORT CxMapIterator cxMapIteratorValues(const CxMap *map);
/**
* Creates a key iterator for a map.
*
- * The elements of the iterator are keys of type CxHashKey and the pointer returned
+ * The elements of the iterator are keys of type CxHashKey, and the pointer returned
* during iterator shall be treated as @c const @c CxHashKey* .
*
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored keys
*/
-cx_attr_nonnull
cx_attr_nodiscard
-static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) {
- return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
-}
+CX_EXPORT CxMapIterator cxMapIteratorKeys(const CxMap *map);
/**
* Creates an iterator for a map.
*
- * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
+ * The elements of the iterator are key/value pairs of type CxMapEntry, and the pointer returned
* during iterator shall be treated as @c const @c CxMapEntry* .
*
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored entries
* @see cxMapIteratorKeys()
* @see cxMapIteratorValues()
*/
-cx_attr_nonnull
cx_attr_nodiscard
-static inline CxMapIterator cxMapIterator(const CxMap *map) {
- return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
-}
-
+CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map);
/**
- * Creates a mutating iterator over the values of a map.
- *
- * When the map is storing pointers, those pointers are returned.
- * Otherwise, the iterator iterates over pointers to the memory within the map where the
- * respective elements are stored.
- *
- * @note An iterator iterates over all elements successively. Therefore, the order
- * highly depends on the map implementation and may change arbitrarily when the contents change.
- *
- * @param map the map to create the iterator for
- * @return an iterator for the currently stored values
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxMapIterator cxMapMutIteratorValues(CxMap *map);
-
-/**
- * Creates a mutating iterator over the keys of a map.
- *
- * The elements of the iterator are keys of type CxHashKey and the pointer returned
- * during iterator shall be treated as @c const @c CxHashKey* .
- *
- * @note An iterator iterates over all elements successively. Therefore, the order
- * highly depends on the map implementation and may change arbitrarily when the contents change.
+ * Puts a key/value-pair into the map.
*
- * @param map the map to create the iterator for
- * @return an iterator for the currently stored keys
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxMapIterator cxMapMutIteratorKeys(CxMap *map);
-
-/**
- * Creates a mutating iterator for a map.
+ * A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
*
- * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
- * during iterator shall be treated as @c const @c CxMapEntry* .
+ * If this map is storing pointers, the @p value pointer is written
+ * to the map. Otherwise, the memory is copied from @p value with
+ * memcpy().
*
- * @note An iterator iterates over all elements successively. Therefore, the order
- * highly depends on the map implementation and may change arbitrarily when the contents change.
+ * The @p key is always copied.
*
- * @param map the map to create the iterator for
- * @return an iterator for the currently stored entries
- * @see cxMapMutIteratorKeys()
- * @see cxMapMutIteratorValues()
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxMapIterator cxMapMutIterator(CxMap *map);
-
-#ifdef __cplusplus
-} // end the extern "C" block here, because we want to start overloading
-cx_attr_nonnull
-static inline int cxMapPut(
- CxMap *map,
- CxHashKey const &key,
- void *value
-) {
- return map->cl->put(map, key, value);
-}
-
-cx_attr_nonnull
-static inline int cxMapPut(
- CxMap *map,
- cxstring const &key,
- void *value
-) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-cx_attr_nonnull
-static inline int cxMapPut(
- CxMap *map,
- cxmutstr const &key,
- void *value
-) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxMapPut(
- CxMap *map,
- const char *key,
- void *value
-) {
- return map->cl->put(map, cx_hash_key_str(key), value);
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapGet(
- const CxMap *map,
- CxHashKey const &key
-) {
- return map->cl->get(map, key);
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapGet(
- const CxMap *map,
- cxstring const &key
-) {
- return map->cl->get(map, cx_hash_key_cxstr(key));
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxMapGet(
- const CxMap *map,
- cxmutstr const &key
-) {
- return map->cl->get(map, cx_hash_key_cxstr(key));
-}
-
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_cstr_arg(2)
-static inline void *cxMapGet(
- const CxMap *map,
- const char *key
-) {
- return map->cl->get(map, cx_hash_key_str(key));
-}
-
-cx_attr_nonnull
-static inline int cxMapRemove(
- CxMap *map,
- CxHashKey const &key
-) {
- return map->cl->remove(map, key, nullptr);
-}
-
-cx_attr_nonnull
-static inline int cxMapRemove(
- CxMap *map,
- cxstring const &key
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
-}
-
-cx_attr_nonnull
-static inline int cxMapRemove(
- CxMap *map,
- cxmutstr const &key
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
-}
-
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxMapRemove(
- CxMap *map,
- const char *key
-) {
- return map->cl->remove(map, cx_hash_key_str(key), nullptr);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cxMapRemoveAndGet(
- CxMap *map,
- CxHashKey key,
- void *targetbuf
-) {
- return map->cl->remove(map, key, targetbuf);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cxMapRemoveAndGet(
- CxMap *map,
- cxstring key,
- void *targetbuf
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cxMapRemoveAndGet(
- CxMap *map,
- cxmutstr key,
- void *targetbuf
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-cx_attr_nonnull
-cx_attr_access_w(3)
-cx_attr_cstr_arg(2)
-static inline int cxMapRemoveAndGet(
- CxMap *map,
- const char *key,
- void *targetbuf
-) {
- return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
-}
-
-#else // __cplusplus
-
-/**
- * @copydoc cxMapPut()
- */
-cx_attr_nonnull
-static inline int cx_map_put(
- CxMap *map,
- CxHashKey key,
- void *value
-) {
- return map->cl->put(map, key, value);
-}
-
-/**
- * @copydoc cxMapPut()
- */
-cx_attr_nonnull
-static inline int cx_map_put_cxstr(
- CxMap *map,
- cxstring key,
- void *value
-) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-/**
- * @copydoc cxMapPut()
- */
-cx_attr_nonnull
-static inline int cx_map_put_mustr(
- CxMap *map,
- cxmutstr key,
- void *value
-) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
-}
-
-/**
- * @copydoc cxMapPut()
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @retval zero success
+ * @retval non-zero value on memory allocation failure
+ * @see cxMapPut()
*/
cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_map_put_str(
- CxMap *map,
- const char *key,
- void *value
-) {
- return map->cl->put(map, cx_hash_key_str(key), value);
-}
+CX_EXPORT int cx_map_put(CxMap *map, CxHashKey key, void *value);
/**
* Puts a key/value-pair into the map.
* The @p key is always copied.
*
* @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param key (any supported key type) the key
* @param value (@c void*) the value
* @retval zero success
* @retval non-zero value on memory allocation failure
+ * @see CX_HASH_KEY()
*/
-#define cxMapPut(map, key, value) _Generic((key), \
- CxHashKey: cx_map_put, \
- cxstring: cx_map_put_cxstr, \
- cxmutstr: cx_map_put_mustr, \
- char*: cx_map_put_str, \
- const char*: cx_map_put_str) \
- (map, key, value)
-
-/**
- * @copydoc cxMapGet()
- */
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_get(
- const CxMap *map,
- CxHashKey key
-) {
- return map->cl->get(map, key);
-}
+#define cxMapPut(map, key, value) cx_map_put(map, CX_HASH_KEY(key), value)
/**
- * @copydoc cxMapGet()
+ * Allocates memory for a value in the map associated with the specified key.
+ *
+ * A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
+ *
+ * If the map is storing pointers, this function returns a @c void** pointer,
+ * meaning a pointer to that pointer.
+ *
+ * The @p key is always copied.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the pointer to the allocated memory or @c NULL if allocation fails
+ * @retval zero success
+ * @retval non-zero value on memory allocation failure
+ * @see cxMapEmplace()
*/
cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_get_cxstr(
- const CxMap *map,
- cxstring key
-) {
- return map->cl->get(map, cx_hash_key_cxstr(key));
-}
+CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key);
/**
- * @copydoc cxMapGet()
+ * Allocates memory for a value in the map associated with the specified key.
+ *
+ * A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
+ *
+ * If the map is storing pointers, this function returns a @c void** pointer,
+ * meaning a pointer to that pointer.
+ *
+ * The @p key is always copied.
+ *
+ * @param map (@c CxMap*) the map
+ * @param key (any supported key type) the key
+ * @return the pointer to the allocated memory or @c NULL if allocation fails
+ * @retval zero success
+ * @retval non-zero value on memory allocation failure
+ * @see CX_HASH_KEY()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cx_map_get_mustr(
- const CxMap *map,
- cxmutstr key
-) {
- return map->cl->get(map, cx_hash_key_cxstr(key));
-}
+#define cxMapEmplace(map, key) cx_map_emplace(map, CX_HASH_KEY(key))
/**
- * @copydoc cxMapGet()
+ * Retrieves a value by using a key.
+ *
+ * If this map is storing pointers, the stored pointer is returned.
+ * Otherwise, a pointer to the element within the map's memory
+ * is returned (which is valid as long as the element stays in the map).
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see cxMapGet()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_cstr_arg(2)
-static inline void *cx_map_get_str(
- const CxMap *map,
- const char *key
-) {
- return map->cl->get(map, cx_hash_key_str(key));
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key);
/**
* Retrieves a value by using a key.
* is returned (which is valid as long as the element stays in the map).
*
* @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param key (any supported key type) the key
* @return (@c void*) the value
+ * @see CX_HASH_KEY()
*/
-#define cxMapGet(map, key) _Generic((key), \
- CxHashKey: cx_map_get, \
- cxstring: cx_map_get_cxstr, \
- cxmutstr: cx_map_get_mustr, \
- char*: cx_map_get_str, \
- const char*: cx_map_get_str) \
- (map, key)
-
-/**
- * @copydoc cxMapRemove()
- */
-cx_attr_nonnull
-static inline int cx_map_remove(
- CxMap *map,
- CxHashKey key
-) {
- return map->cl->remove(map, key, NULL);
-}
-
-/**
- * @copydoc cxMapRemove()
- */
-cx_attr_nonnull
-static inline int cx_map_remove_cxstr(
- CxMap *map,
- cxstring key
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
-}
-
-/**
- * @copydoc cxMapRemove()
- */
-cx_attr_nonnull
-static inline int cx_map_remove_mustr(
- CxMap *map,
- cxmutstr key
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
-}
+#define cxMapGet(map, key) cx_map_get(map, CX_HASH_KEY(key))
/**
- * @copydoc cxMapRemove()
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Invokes the destructor functions, if any, on the removed element if and only if the
+ * @p targetbuf is @c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @param targetbuf the optional buffer where the removed element shall be copied to
+ * @retval zero success
+ * @retval non-zero the key was not found
+ *
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
*/
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_map_remove_str(
- CxMap *map,
- const char *key
-) {
- return map->cl->remove(map, cx_hash_key_str(key), NULL);
-}
+cx_attr_nonnull_arg(1)
+CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
/**
* Removes a key/value-pair from the map by using the key.
*
- * Always invokes the destructors functions, if any, on the removed element.
+ * Always invokes the destructor functions, if any, on the removed element.
*
* @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param key (any supported key type) the key
* @retval zero success
* @retval non-zero the key was not found
*
* @see cxMapRemoveAndGet()
+ * @see CX_HASH_KEY()
*/
-#define cxMapRemove(map, key) _Generic((key), \
- CxHashKey: cx_map_remove, \
- cxstring: cx_map_remove_cxstr, \
- cxmutstr: cx_map_remove_mustr, \
- char*: cx_map_remove_str, \
- const char*: cx_map_remove_str) \
- (map, key)
-
-/**
- * @copydoc cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cx_map_remove_and_get(
- CxMap *map,
- CxHashKey key,
- void *targetbuf
-) {
- return map->cl->remove(map, key, targetbuf);
-}
-
-/**
- * @copydoc cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cx_map_remove_and_get_cxstr(
- CxMap *map,
- cxstring key,
- void *targetbuf
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-/**
- * @copydoc cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_access_w(3)
-static inline int cx_map_remove_and_get_mustr(
- CxMap *map,
- cxmutstr key,
- void *targetbuf
-) {
- return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
-}
-
-/**
- * @copydoc cxMapRemoveAndGet()
- */
-cx_attr_nonnull
-cx_attr_access_w(3)
-cx_attr_cstr_arg(2)
-static inline int cx_map_remove_and_get_str(
- CxMap *map,
- const char *key,
- void *targetbuf
-) {
- return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
-}
+#define cxMapRemove(map, key) cx_map_remove(map, CX_HASH_KEY(key), NULL)
/**
* Removes a key/value-pair from the map by using the key.
* and not the object it points to.
*
* @param map (@c CxMap*) the map
- * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @param key (any supported key type) the key
* @param targetbuf (@c void*) the buffer where the element shall be copied to
* @retval zero success
* @retval non-zero the key was not found
*
* @see cxMapRemove()
+ * @see CX_HASH_KEY()
*/
-#define cxMapRemoveAndGet(map, key, targetbuf) _Generic((key), \
- CxHashKey: cx_map_remove_and_get, \
- cxstring: cx_map_remove_and_get_cxstr, \
- cxmutstr: cx_map_remove_and_get_mustr, \
- char*: cx_map_remove_and_get_str, \
- const char*: cx_map_remove_and_get_str) \
- (map, key, targetbuf)
-
-#endif // __cplusplus
+#define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
#endif // UCX_MAP_H
*
* @param pool the memory pool to free
*/
-cx_attr_export
-void cxMempoolFree(CxMempool *pool);
+CX_EXPORT void cxMempoolFree(CxMempool *pool);
/**
* Creates an array-based memory pool.
* @param type the type of memory pool
* @return the created memory pool or @c NULL if allocation failed
*/
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc(cxMempoolFree, 1)
-cx_attr_export
-CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
+cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1)
+CX_EXPORT CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
/**
* Creates a basic array-based memory pool.
* @param fnc the destructor that shall be applied to all memory blocks
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
+CX_EXPORT void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
/**
* Sets the global destructor for all memory blocks within the specified pool.
* @param data additional data for the destructor function
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
+CX_EXPORT void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
/**
* Sets the destructor function for a specific allocated memory object.
* @param fnc the destructor function
*/
cx_attr_nonnull
-cx_attr_export
-void cxMempoolSetDestructor(
- void *memory,
- cx_destructor_func fnc
-);
+CX_EXPORT void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
/**
* Sets the destructor function for a specific allocated memory object.
* @param data additional data for the destructor function
*/
cx_attr_nonnull
-cx_attr_export
-void cxMempoolSetDestructor2(
- void *memory,
- cx_destructor_func2 fnc,
- void *data
-);
+CX_EXPORT void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data);
/**
* Removes the destructor function for a specific allocated memory object.
* @param memory the object allocated in the pool
*/
cx_attr_nonnull
-cx_attr_export
-void cxMempoolRemoveDestructor(void *memory);
+CX_EXPORT void cxMempoolRemoveDestructor(void *memory);
/**
* Removes the destructor function for a specific allocated memory object.
* @param memory the object allocated in the pool
*/
cx_attr_nonnull
-cx_attr_export
-void cxMempoolRemoveDestructor2(void *memory);
+CX_EXPORT void cxMempoolRemoveDestructor2(void *memory);
/**
* Registers foreign memory with this pool.
* @retval non-zero failure
*/
cx_attr_nonnull
-cx_attr_export
-int cxMempoolRegister(
- CxMempool *pool,
- void *memory,
- cx_destructor_func destr
-);
+CX_EXPORT int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr);
/**
* @retval non-zero failure
*/
cx_attr_nonnull
-cx_attr_export
-int cxMempoolRegister2(
- CxMempool *pool,
- void *memory,
- cx_destructor_func2 destr,
- void *data
-);
+CX_EXPORT int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data);
/**
* Transfers all the memory managed by one pool to another.
* @retval non-zero allocation failure or incompatible pools
*/
cx_attr_nonnull
-cx_attr_export
-int cxMempoolTransfer(
- CxMempool *source,
- CxMempool *dest
-);
+CX_EXPORT int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
/**
* Transfers an object from one pool to another.
* @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible
*/
cx_attr_nonnull
-cx_attr_export
-int cxMempoolTransferObject(
- CxMempool *source,
- CxMempool *dest,
- const void *obj
-);
+CX_EXPORT int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj);
#ifdef __cplusplus
} // extern "C"
*/
/**
* @file printf.h
- * @brief Wrapper for write functions with a printf-like interface.
+ * @brief Wrapper for write-functions with a printf-like interface.
* @author Mike Becker
* @author Olaf Wintermann
* @copyright 2-Clause BSD License
/**
* The maximum string length that fits into stack memory.
*/
-cx_attr_export
-extern const unsigned cx_printf_sbo_size;
+CX_EXPORT extern const unsigned cx_printf_sbo_size;
/**
* A @c fprintf like function which writes the output to a stream by
* @param ... additional arguments
* @return the total number of bytes written or an error code from stdlib printf implementation
*/
-cx_attr_nonnull_arg(1, 2, 3)
-cx_attr_printf(3, 4)
-cx_attr_cstr_arg(3)
-cx_attr_export
-int cx_fprintf(
- void *stream,
- cx_write_func wfc,
- const char *fmt,
- ...
-);
+cx_attr_nonnull_arg(1, 2, 3) cx_attr_printf(3, 4) cx_attr_cstr_arg(3)
+CX_EXPORT int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
/**
* A @c vfprintf like function which writes the output to a stream by
* @return the total number of bytes written or an error code from stdlib printf implementation
* @see cx_fprintf()
*/
-cx_attr_nonnull
-cx_attr_cstr_arg(3)
-cx_attr_export
-int cx_vfprintf(
- void *stream,
- cx_write_func wfc,
- const char *fmt,
- va_list ap
-);
+cx_attr_nonnull cx_attr_cstr_arg(3)
+CX_EXPORT int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap);
/**
- * A @c asprintf like function which allocates space for a string
+ * An @c asprintf like function which allocates space for a string
* the result is written to.
*
* @note The resulting string is guaranteed to be zero-terminated,
* @return the formatted string
* @see cx_strfree_a()
*/
-cx_attr_nonnull_arg(1, 2)
-cx_attr_printf(2, 3)
-cx_attr_cstr_arg(2)
-cx_attr_export
-cxmutstr cx_asprintf_a(
- const CxAllocator *allocator,
- const char *fmt,
- ...
-);
+cx_attr_nonnull_arg(1, 2) cx_attr_printf(2, 3) cx_attr_cstr_arg(2)
+CX_EXPORT cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...);
/**
- * A @c asprintf like function which allocates space for a string
+ * An @c asprintf like function which allocates space for a string
* the result is written to.
*
* @note The resulting string is guaranteed to be zero-terminated,
* @return (@c cxmutstr) the formatted string
* @see cx_strfree()
*/
-#define cx_asprintf(fmt, ...) \
- cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__)
+#define cx_asprintf(fmt, ...) cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__)
/**
* A @c vasprintf like function which allocates space for a string
* @return the formatted string
* @see cx_asprintf_a()
*/
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-cx_attr_export
-cxmutstr cx_vasprintf_a(
- const CxAllocator *allocator,
- const char *fmt,
- va_list ap
-);
+cx_attr_nonnull cx_attr_cstr_arg(2)
+CX_EXPORT cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap);
/**
* A @c vasprintf like function which allocates space for a string
* the result is written to.
*
* @note The resulting string is guaranteed to be zero-terminated,
- * unless there was in error, in which case the string's pointer
+ * unless there was an error, in which case the string's pointer
* will be @c NULL.
*
* @param fmt (@c char*) format string
* @see cx_fprintf()
* @see cxBufferWrite()
*/
-#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, \
- cxBufferWriteFunc, fmt, __VA_ARGS__)
+#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, cxBufferWriteFunc, fmt, __VA_ARGS__)
/**
* @param len (@c size_t*) a pointer to the length of the buffer
* @param fmt (@c char*) the format string
* @param ... additional arguments
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
*/
#define cx_sprintf(str, len, fmt, ...) cx_sprintf_a(cxDefaultAllocator, str, len, fmt, __VA_ARGS__)
* @param len a pointer to the length of the buffer
* @param fmt the format string
* @param ... additional arguments
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull_arg(1, 2, 3, 4)
-cx_attr_printf(4, 5)
-cx_attr_cstr_arg(4)
-cx_attr_export
-int cx_sprintf_a(
- const CxAllocator *alloc,
- char **str,
- size_t *len,
- const char *fmt,
- ...
-);
+cx_attr_nonnull_arg(1, 2, 3, 4) cx_attr_printf(4, 5) cx_attr_cstr_arg(4)
+CX_EXPORT int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...);
/**
* @param len (@c size_t*) a pointer to the length of the buffer
* @param fmt (@c char*) the format string
* @param ap (@c va_list) argument list
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
*/
#define cx_vsprintf(str, len, fmt, ap) cx_vsprintf_a(cxDefaultAllocator, str, len, fmt, ap)
* @param len a pointer to the length of the buffer
* @param fmt the format string
* @param ap argument list
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull
-cx_attr_cstr_arg(4)
-cx_attr_access_rw(2)
-cx_attr_access_rw(3)
-cx_attr_export
-int cx_vsprintf_a(
- const CxAllocator *alloc,
- char **str,
- size_t *len,
- const char *fmt,
- va_list ap
-);
+cx_attr_nonnull cx_attr_cstr_arg(4) cx_attr_access_rw(2) cx_attr_access_rw(3)
+CX_EXPORT int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap);
/**
* @param str (@c char**) a pointer where the location of the result shall be stored
* @param fmt (@c char*) the format string
* @param ... additional arguments
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
*/
#define cx_sprintf_s(buf, len, str, fmt, ...) cx_sprintf_sa(cxDefaultAllocator, buf, len, str, fmt, __VA_ARGS__)
* @param str a pointer where the location of the result shall be stored
* @param fmt the format string
* @param ... additional arguments
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull_arg(1, 2, 4, 5)
-cx_attr_printf(5, 6)
-cx_attr_cstr_arg(5)
-cx_attr_access_rw(2)
-cx_attr_access_rw(3)
-cx_attr_access_rw(4)
-cx_attr_export
-int cx_sprintf_sa(
- const CxAllocator *alloc,
- char *buf,
- size_t *len,
- char **str,
- const char *fmt,
- ...
-);
+cx_attr_nonnull_arg(1, 2, 4, 5) cx_attr_printf(5, 6) cx_attr_cstr_arg(5)
+cx_attr_access_rw(2) cx_attr_access_rw(3) cx_attr_access_rw(4)
+CX_EXPORT int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...);
/**
* An @c sprintf like function which allocates a new string when the buffer is not large enough.
* @param str (@c char**) a pointer where the location of the result shall be stored
* @param fmt (@c char*) the format string
* @param ap (@c va_list) argument list
- * @return (@c int) the length of produced string or an error code from stdlib printf implementation
+ * @return (@c int) the length of the produced string or an error code from stdlib printf implementation
*/
#define cx_vsprintf_s(buf, len, str, fmt, ap) cx_vsprintf_sa(cxDefaultAllocator, buf, len, str, fmt, ap)
* @param str a pointer where the location of the result shall be stored
* @param fmt the format string
* @param ap argument list
- * @return the length of produced string or an error code from stdlib printf implementation
+ * @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull
-cx_attr_cstr_arg(5)
-cx_attr_export
-int cx_vsprintf_sa(
- const CxAllocator *alloc,
- char *buf,
- size_t *len,
- char **str,
- const char *fmt,
- va_list ap
-);
+cx_attr_nonnull cx_attr_cstr_arg(5)
+CX_EXPORT int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap);
#ifdef __cplusplus
/**
* Default properties configuration.
*/
-cx_attr_export
-extern const CxPropertiesConfig cx_properties_config_default;
+CX_EXPORT extern const CxPropertiesConfig cx_properties_config_default;
/**
* Status codes for the properties interface.
* You can use this enumerator to check for all "good" status results
* by checking if the status is less than @c CX_PROPERTIES_OK.
*
- * A "good" status means, that you can refill data and continue parsing.
+ * A "good" status means that you can refill data and continue parsing.
*/
CX_PROPERTIES_OK,
/**
*/
CX_PROPERTIES_NULL_INPUT,
/**
- * The line contains a delimiter, but no key.
+ * The line contains a delimiter but no key.
*/
CX_PROPERTIES_INVALID_EMPTY_KEY,
/**
- * The line contains data, but no delimiter.
+ * The line contains data but no delimiter.
*/
CX_PROPERTIES_INVALID_MISSING_DELIMITER,
/**
/**
* A function that consumes a k/v-pair in a sink.
*
- * The sink could be e.g. a map and the sink function would be calling
+ * The sink could be a map, and the sink function would be calling
* a map function to store the k/v-pair.
*
* @param prop the properties interface that wants to sink a k/v-pair
* @retval zero success
* @retval non-zero sinking the k/v-pair failed
*/
-cx_attr_nonnull
typedef int(*cx_properties_sink_func)(
CxProperties *prop,
CxPropertiesSink *sink,
* @retval zero success
* @retval non-zero reading the data failed
*/
-cx_attr_nonnull
typedef int(*cx_properties_read_func)(
CxProperties *prop,
CxPropertiesSource *src,
* @retval zero initialization was successful
* @retval non-zero otherwise
*/
-cx_attr_nonnull
typedef int(*cx_properties_read_init_func)(
CxProperties *prop,
CxPropertiesSource *src
* @param prop the properties interface that wants to read from the source
* @param src the source
*/
-cx_attr_nonnull
typedef void(*cx_properties_read_clean_func)(
CxProperties *prop,
CxPropertiesSource *src
/**
* The source object.
*
- * For example a file stream or a string.
+ * For example, a file stream or a string.
*/
void *src;
/**
* @see cxPropertiesInitDefault()
*/
cx_attr_nonnull
-cx_attr_export
-void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
+CX_EXPORT void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
/**
* Destroys the properties interface.
*
* @note Even when you are certain that you did not use the interface in a
* way that caused a memory allocation, you should call this function anyway.
- * Future versions of the library might add features that need additional memory
- * and you really don't want to search the entire code where you might need
- * add call to this function.
+ * Future versions of the library might add features that need additional memory,
+ * and you really don't want to search the entire code where you might need to
+ * add a call to this function.
*
* @param prop the properties interface
*/
cx_attr_nonnull
-cx_attr_export
-void cxPropertiesDestroy(CxProperties *prop);
+CX_EXPORT void cxPropertiesDestroy(CxProperties *prop);
/**
* Destroys and re-initializes the properties interface.
*
- * You might want to use this, to reset the parser after
+ * You might want to use this to reset the parser after
* encountering a syntax error.
*
* @param prop the properties interface
*/
cx_attr_nonnull
-static inline void cxPropertiesReset(CxProperties *prop) {
- CxPropertiesConfig config = prop->config;
- cxPropertiesDestroy(prop);
- cxPropertiesInit(prop, config);
-}
+CX_EXPORT void cxPropertiesReset(CxProperties *prop);
/**
* Initialize a properties parser with the default configuration.
* @see cxPropertiesInit()
*/
#define cxPropertiesInitDefault(prop) \
- cxPropertiesInit(prop, cx_properties_config_default)
+ cxPropertiesInit(prop, cx_properties_config_default)
/**
* Fills the input buffer with data.
* @retval non-zero a memory allocation was necessary but failed
* @see cxPropertiesFill()
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-int cxPropertiesFilln(
- CxProperties *prop,
- const char *buf,
- size_t len
-);
-
-#ifdef __cplusplus
-} // extern "C"
-cx_attr_nonnull
-static inline int cxPropertiesFill(
- CxProperties *prop,
- cxstring str
-) {
- return cxPropertiesFilln(prop, str.ptr, str.length);
-}
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len);
+/**
+ * Internal function, do not use.
+ *
+ * @param prop the properties interface
+ * @param str the text to fill in
+ * @retval zero success
+ * @retval non-zero a memory allocation was necessary but failed
+ */
cx_attr_nonnull
-static inline int cxPropertiesFill(
- CxProperties *prop,
- cxmutstr str
-) {
+CX_INLINE int cx_properties_fill(CxProperties *prop, cxstring str) {
return cxPropertiesFilln(prop, str.ptr, str.length);
}
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cxPropertiesFill(
- CxProperties *prop,
- const char *str
-) {
- return cxPropertiesFilln(prop, str, strlen(str));
-}
-
-extern "C" {
-#else // __cplusplus
/**
* Fills the input buffer with data.
*
* @retval non-zero a memory allocation was necessary but failed
* @see cxPropertiesFilln()
*/
-#define cxPropertiesFill(prop, str) _Generic((str), \
- cxstring: cx_properties_fill_cxstr, \
- cxmutstr: cx_properties_fill_mutstr, \
- char*: cx_properties_fill_str, \
- const char*: cx_properties_fill_str) \
- (prop, str)
+#define cxPropertiesFill(prop, str) cx_properties_fill(prop, cx_strcast(str))
/**
- * @copydoc cxPropertiesFill()
- */
-cx_attr_nonnull
-static inline int cx_properties_fill_cxstr(
- CxProperties *prop,
- cxstring str
-) {
- return cxPropertiesFilln(prop, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxPropertiesFill()
- */
-cx_attr_nonnull
-static inline int cx_properties_fill_mutstr(
- CxProperties *prop,
- cxmutstr str
-) {
- return cxPropertiesFilln(prop, str.ptr, str.length);
-}
-
-/**
- * @copydoc cxPropertiesFill()
- */
-cx_attr_nonnull
-cx_attr_cstr_arg(2)
-static inline int cx_properties_fill_str(
- CxProperties *prop,
- const char *str
-) {
- return cxPropertiesFilln(prop, str, strlen(str));
-}
-#endif
-
-/**
- * Specifies stack memory that shall be used as internal buffer.
+ * Specifies stack memory that shall be used as an internal buffer.
*
* @param prop the properties interface
* @param buf a pointer to stack memory
* @param capacity the capacity of the stack memory
*/
cx_attr_nonnull
-cx_attr_export
-void cxPropertiesUseStack(
- CxProperties *prop,
- char *buf,
- size_t capacity
-);
+CX_EXPORT void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity);
/**
* Retrieves the next key/value-pair.
* @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
* @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxPropertiesStatus cxPropertiesNext(
- CxProperties *prop,
- cxstring *key,
- cxstring *value
-);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
/**
* Creates a properties sink for an UCX map.
* @return the sink
* @see cxPropertiesLoad()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-CxPropertiesSink cxPropertiesMapSink(CxMap *map);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT CxPropertiesSink cxPropertiesMapSink(CxMap *map);
/**
* Creates a properties source based on an UCX string.
* @see cxPropertiesLoad()
*/
cx_attr_nodiscard
-cx_attr_export
-CxPropertiesSource cxPropertiesStringSource(cxstring str);
+CX_EXPORT CxPropertiesSource cxPropertiesStringSource(cxstring str);
/**
* Creates a properties source based on C string with the specified length.
* @return the properties source
* @see cxPropertiesLoad()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_access_r(1, 2)
-cx_attr_export
-CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
+cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1, 2)
+CX_EXPORT CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
/**
* Creates a properties source based on a C string.
* @return the properties source
* @see cxPropertiesLoad()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_cstr_arg(1)
-cx_attr_export
-CxPropertiesSource cxPropertiesCstrSource(const char *str);
+cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1)
+CX_EXPORT CxPropertiesSource cxPropertiesCstrSource(const char *str);
/**
* Creates a properties source based on an FILE.
* @return the properties source
* @see cxPropertiesLoad()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_access_r(1)
-cx_attr_export
-CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
+cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1)
+CX_EXPORT CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
/**
* @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
*/
cx_attr_nonnull
-cx_attr_export
-CxPropertiesStatus cxPropertiesLoad(
- CxProperties *prop,
- CxPropertiesSink sink,
- CxPropertiesSource source
-);
+CX_EXPORT CxPropertiesStatus cxPropertiesLoad(CxProperties *prop,
+ CxPropertiesSink sink, CxPropertiesSource source);
#ifdef __cplusplus
} // extern "C"
* @param wfnc the write function
* @param buf a pointer to the copy buffer or @c NULL if a buffer
* shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if @p buf is @c NULL you can
+ * @param bufsize the size of the copy buffer - if @p buf is @c NULL, you can
* set this to zero to let the implementation decide
* @param n the maximum number of bytes that shall be copied.
* If this is larger than @p bufsize, the content is copied over multiple
* @return the total number of bytes copied
*/
cx_attr_nonnull_arg(1, 2, 3, 4)
-cx_attr_access_r(1)
-cx_attr_access_w(2)
-cx_attr_access_w(5)
-cx_attr_export
-size_t cx_stream_bncopy(
- void *src,
- void *dest,
- cx_read_func rfnc,
- cx_write_func wfnc,
- char *buf,
- size_t bufsize,
- size_t n
-);
+cx_attr_access_r(1) cx_attr_access_w(2) cx_attr_access_w(5)
+CX_EXPORT size_t cx_stream_bncopy(void *src, void *dest,
+ cx_read_func rfnc, cx_write_func wfnc,
+ char *buf, size_t bufsize, size_t n);
/**
* Reads data from a stream and writes it to another stream.
* @param buf (@c char*) a pointer to the copy buffer or @c NULL if a buffer
* shall be implicitly created on the heap
* @param bufsize (@c size_t) the size of the copy buffer - if @p buf is
- * @c NULL you can set this to zero to let the implementation decide
+ * @c NULL, you can set this to zero to let the implementation decide
* @return total number of bytes copied
*/
#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \
/**
* Reads data from a stream and writes it to another stream.
*
- * The data is temporarily stored in a stack allocated buffer.
+ * The data is temporarily stored in a stack-allocated buffer.
*
* @param src the source stream
* @param dest the destination stream
* @param n the maximum number of bytes that shall be copied.
* @return total number of bytes copied
*/
-cx_attr_nonnull
-cx_attr_access_r(1)
-cx_attr_access_w(2)
-cx_attr_export
-size_t cx_stream_ncopy(
- void *src,
- void *dest,
- cx_read_func rfnc,
- cx_write_func wfnc,
- size_t n
-);
+cx_attr_nonnull cx_attr_access_r(1) cx_attr_access_w(2)
+CX_EXPORT size_t cx_stream_ncopy(void *src, void *dest,
+ cx_read_func rfnc, cx_write_func wfnc, size_t n);
/**
* Reads data from a stream and writes it to another stream.
*
- * The data is temporarily stored in a stack allocated buffer.
+ * The data is temporarily stored in a stack-allocated buffer.
*
* @param src (@c void*) the source stream
* @param dest (@c void*) the destination stream
/**
* The maximum length of the "needle" in cx_strstr() that can use SBO.
*/
-cx_attr_export
-extern const unsigned cx_strstr_sbo_size;
+CX_EXPORT extern const unsigned cx_strstr_sbo_size;
/**
* The UCX string structure.
*/
size_t pos;
/**
- * Position of next delimiter in the source string.
+ * Position of the next delimiter in the source string.
*
* If the tokenizer has not yet returned a token, the content of this field
- * is undefined. If the tokenizer reached the end of the string, this field
+ * is undefined. If the tokenizer reaches the end of the string, this field
* contains the length of the source string.
*/
size_t delim_pos;
*
* If you need to wrap a constant string, use cx_str().
*
- * @param cstring the string to wrap, must be zero-terminated
+ * @param cstring the string to wrap (must be zero-terminated)
* @return the wrapped string
*
* @see cx_mutstrn()
*/
-cx_attr_nodiscard
-cx_attr_cstr_arg(1)
-cx_attr_export
-cxmutstr cx_mutstr(char *cstring);
+cx_attr_nodiscard cx_attr_cstr_arg(1)
+CX_EXPORT cxmutstr cx_mutstr(char *cstring);
/**
* Wraps a string that does not need to be zero-terminated.
*
* @see cx_mutstr()
*/
-cx_attr_nodiscard
-cx_attr_access_rw(1, 2)
-cx_attr_export
-cxmutstr cx_mutstrn(
- char *cstring,
- size_t length
-);
+cx_attr_nodiscard cx_attr_access_rw(1, 2)
+CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length);
/**
* Wraps a string that must be zero-terminated.
*
* If you need to wrap a non-constant string, use cx_mutstr().
*
- * @param cstring the string to wrap, must be zero-terminated
+ * @param cstring the string to wrap (must be zero-terminated)
* @return the wrapped string
*
* @see cx_strn()
*/
-cx_attr_nodiscard
-cx_attr_cstr_arg(1)
-cx_attr_export
-cxstring cx_str(const char *cstring);
+cx_attr_nodiscard cx_attr_cstr_arg(1)
+CX_EXPORT cxstring cx_str(const char *cstring);
/**
*
* @see cx_str()
*/
-cx_attr_nodiscard
-cx_attr_access_r(1, 2)
-cx_attr_export
-cxstring cx_strn(
- const char *cstring,
- size_t length
-);
+cx_attr_nodiscard cx_attr_access_r(1, 2)
+CX_EXPORT cxstring cx_strn(const char *cstring, size_t length);
#ifdef __cplusplus
} // extern "C"
cx_attr_nodiscard
-static inline cxstring cx_strcast(cxmutstr str) {
+CX_CPPDECL cxstring cx_strcast(cxmutstr str) {
return cx_strn(str.ptr, str.length);
}
cx_attr_nodiscard
-static inline cxstring cx_strcast(cxstring str) {
+CX_CPPDECL cxstring cx_strcast(cxstring str) {
return str;
}
cx_attr_nodiscard
-static inline cxstring cx_strcast(const char *str) {
+CX_CPPDECL cxstring cx_strcast(const char *str) {
return cx_str(str);
}
+cx_attr_nodiscard
+CX_CPPDECL cxstring cx_strcast(const unsigned char *str) {
+ return cx_str(static_cast<const char*>(str));
+}
extern "C" {
#else
/**
* @see cx_strcast()
*/
cx_attr_nodiscard
-static inline cxstring cx_strcast_m(cxmutstr str) {
+CX_INLINE cxstring cx_strcast_m(cxmutstr str) {
return (cxstring) {str.ptr, str.length};
}
/**
* @see cx_strcast()
*/
cx_attr_nodiscard
-static inline cxstring cx_strcast_c(cxstring str) {
+CX_INLINE cxstring cx_strcast_c(cxstring str) {
return str;
}
* @see cx_strcast()
*/
cx_attr_nodiscard
-static inline cxstring cx_strcast_z(const char *str) {
+CX_INLINE cxstring cx_strcast_u(const unsigned char *str) {
+ return cx_str((const char*)str);
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast()
+ */
+cx_attr_nodiscard
+CX_INLINE cxstring cx_strcast_z(const char *str) {
return cx_str(str);
}
/**
-* Casts a mutable string to an immutable string.
-*
-* Does nothing for already immutable strings.
-*
-* @note This is not seriously a cast. Instead, you get a copy
-* of the struct with the desired pointer type. Both structs still
-* point to the same location, though!
-*
-* @param str (@c cxstring or @c cxmutstr) the string to cast
-* @return (@c cxstring) an immutable copy of the string pointer
-*/
+ * Wraps any string into an UCX string.
+ *
+ * @param str (any supported string type) the string to cast
+ * @return (@c cxstring) the string wrapped as UCX string
+ */
#define cx_strcast(str) _Generic((str), \
cxmutstr: cx_strcast_m, \
cxstring: cx_strcast_c, \
+ const unsigned char*: cx_strcast_u, \
+ unsigned char *: cx_strcast_u, \
const char*: cx_strcast_z, \
char *: cx_strcast_z) (str)
#endif
/**
* Passes the pointer in this string to the cxDefaultAllocator's @c free() function.
*
- * The pointer in the struct is set to @c NULL and the length is set to zero
+ * The pointer in the struct is set to @c NULL, and the length is set to zero,
* which means that this function protects you against double-free.
*
* @note There is no implementation for cxstring, because it is unlikely that
* you ever have a <code>const char*</code> you are really supposed to free.
- * If you encounter such situation, you should double-check your code.
+ * If you encounter such a situation, you should double-check your code.
*
* @param str the string to free
*/
-cx_attr_export
-void cx_strfree(cxmutstr *str);
+CX_EXPORT void cx_strfree(cxmutstr *str);
/**
- * Passes the pointer in this string to the allocators free function.
+ * Passes the pointer in this string to the allocator's free function.
*
- * The pointer in the struct is set to @c NULL and the length is set to zero
+ * The pointer in the struct is set to @c NULL, and the length is set to zero,
* which means that this function protects you against double-free.
*
* @note There is no implementation for cxstring, because it is unlikely that
* you ever have a <code>const char*</code> you are really supposed to free.
- * If you encounter such situation, you should double-check your code.
+ * If you encounter such a situation, you should double-check your code.
*
* @param alloc the allocator
* @param str the string to free
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-void cx_strfree_a(
- const CxAllocator *alloc,
- cxmutstr *str
-);
+CX_EXPORT void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
/**
* Copies a string.
* @retval non-zero if re-allocation failed
*/
cx_attr_nonnull_arg(1)
-cx_attr_export
-int cx_strcpy_a(
- const CxAllocator *alloc,
- cxmutstr *dest,
- cxstring src
-);
+CX_EXPORT int cx_strcpy_a(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
/**
* @return the accumulated length of all strings
*/
cx_attr_nodiscard
-cx_attr_export
-size_t cx_strlen(
- size_t count,
- ...
-);
+CX_EXPORT size_t cx_strlen(size_t count, ...);
/**
* Concatenates strings.
* @param ... all other UCX strings
* @return the concatenated string
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_export
-cxmutstr cx_strcat_ma(
- const CxAllocator *alloc,
- cxmutstr str,
- size_t count,
- ...
-);
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT cxmutstr cx_strcat_ma(const CxAllocator *alloc,
+ cxmutstr str, size_t count, ...);
/**
* Concatenates strings and returns a new string.
* @return (@c cxmutstr) the concatenated string
*/
#define cx_strcat_a(alloc, count, ...) \
-cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
+ cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
/**
* Concatenates strings and returns a new string.
* @return (@c cxmutstr) the concatenated string
*/
#define cx_strcat(count, ...) \
-cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
+ cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
/**
* Concatenates strings.
* @return (@c cxmutstr) the concatenated string
*/
#define cx_strcat_m(str, count, ...) \
-cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__)
+ cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__)
/**
* Returns a substring starting at the specified location.
* @see cx_strsubsl_m()
*/
cx_attr_nodiscard
-cx_attr_export
-cxstring cx_strsubs(
- cxstring string,
- size_t start
-);
+CX_EXPORT cxstring cx_strsubs(cxstring string, size_t start);
/**
* Returns a substring starting at the specified location.
* @see cx_strsubsl_m()
*/
cx_attr_nodiscard
-cx_attr_export
-cxstring cx_strsubsl(
- cxstring string,
- size_t start,
- size_t length
-);
+CX_EXPORT cxstring cx_strsubsl(cxstring string, size_t start, size_t length);
/**
* Returns a substring starting at the specified location.
* @see cx_strsubsl()
*/
cx_attr_nodiscard
-cx_attr_export
-cxmutstr cx_strsubs_m(
- cxmutstr string,
- size_t start
-);
+CX_EXPORT cxmutstr cx_strsubs_m(cxmutstr string, size_t start);
/**
* Returns a substring starting at the specified location.
* @see cx_strsubsl()
*/
cx_attr_nodiscard
-cx_attr_export
-cxmutstr cx_strsubsl_m(
- cxmutstr string,
- size_t start,
- size_t length
-);
+CX_EXPORT cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length);
/**
* Returns a substring starting at the location of the first occurrence of the
* @see cx_strchr_m()
*/
cx_attr_nodiscard
-cx_attr_export
-cxstring cx_strchr(
- cxstring string,
- int chr
-);
+CX_EXPORT cxstring cx_strchr(cxstring string, int chr);
/**
* Returns a substring starting at the location of the first occurrence of the
* @see cx_strchr()
*/
cx_attr_nodiscard
-cx_attr_export
-cxmutstr cx_strchr_m(
- cxmutstr string,
- int chr
-);
+CX_EXPORT cxmutstr cx_strchr_m(cxmutstr string, int chr);
/**
* Returns a substring starting at the location of the last occurrence of the
* @see cx_strrchr_m()
*/
cx_attr_nodiscard
-cx_attr_export
-cxstring cx_strrchr(
- cxstring string,
- int chr
-);
+CX_EXPORT cxstring cx_strrchr(cxstring string, int chr);
/**
* Returns a substring starting at the location of the last occurrence of the
* @see cx_strrchr()
*/
cx_attr_nodiscard
-cx_attr_export
-cxmutstr cx_strrchr_m(
- cxmutstr string,
- int chr
-);
+CX_EXPORT cxmutstr cx_strrchr_m(cxmutstr string, int chr);
/**
* Returns a substring starting at the location of the first occurrence of the
* @see cx_strstr_m()
*/
cx_attr_nodiscard
-cx_attr_export
-cxstring cx_strstr(
- cxstring haystack,
- cxstring needle
-);
+CX_EXPORT cxstring cx_strstr(cxstring haystack, cxstring needle);
/**
* Returns a substring starting at the location of the first occurrence of the
* @see cx_strstr()
*/
cx_attr_nodiscard
-cx_attr_export
-cxmutstr cx_strstr_m(
- cxmutstr haystack,
- cxstring needle
-);
+CX_EXPORT cxmutstr cx_strstr_m(cxmutstr haystack, cxstring needle);
/**
* Splits a given string using a delimiter string.
* @param output a preallocated array of at least @p limit length
* @return the actual number of split items
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_access_w(4, 3)
-cx_attr_export
-size_t cx_strsplit(
- cxstring string,
- cxstring delim,
- size_t limit,
- cxstring *output
-);
+cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
+CX_EXPORT size_t cx_strsplit(cxstring string, cxstring delim,
+ size_t limit, cxstring *output);
/**
* Splits a given string using a delimiter string.
* written to
* @return the actual number of split items
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_access_w(5)
-cx_attr_export
-size_t cx_strsplit_a(
- const CxAllocator *allocator,
- cxstring string,
- cxstring delim,
- size_t limit,
- cxstring **output
-);
+cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
+CX_EXPORT size_t cx_strsplit_a(const CxAllocator *allocator,
+ cxstring string, cxstring delim,
+ size_t limit, cxstring **output);
/**
* @param output a preallocated array of at least @p limit length
* @return the actual number of split items
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_access_w(4, 3)
-cx_attr_export
-size_t cx_strsplit_m(
- cxmutstr string,
- cxstring delim,
- size_t limit,
- cxmutstr *output
-);
+cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
+CX_EXPORT size_t cx_strsplit_m(cxmutstr string, cxstring delim,
+ size_t limit, cxmutstr *output);
/**
* Splits a given string using a delimiter string.
* written to
* @return the actual number of split items
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_access_w(5)
-cx_attr_export
-size_t cx_strsplit_ma(
- const CxAllocator *allocator,
- cxmutstr string,
- cxstring delim,
- size_t limit,
- cxmutstr **output
-);
+cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
+CX_EXPORT size_t cx_strsplit_ma(const CxAllocator *allocator,
+ cxmutstr string, cxstring delim, size_t limit,
+ cxmutstr **output);
/**
* Compares two strings.
* than @p s2, zero if both strings equal
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_strcmp(
- cxstring s1,
- cxstring s2
-);
+CX_EXPORT int cx_strcmp_(cxstring s1, cxstring s2);
+
+/**
+ * Compares two strings.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
+ * than @p s2, zero if both strings equal
+ */
+#define cx_strcmp(s1, s2) cx_strcmp_(cx_strcast(s1), cx_strcast(s2))
/**
* Compares two strings ignoring case.
* than @p s2, zero if both strings equal ignoring case
*/
cx_attr_nodiscard
-cx_attr_export
-int cx_strcasecmp(
- cxstring s1,
- cxstring s2
-);
+CX_EXPORT int cx_strcasecmp_(cxstring s1, cxstring s2);
+
+/**
+ * Compares two strings ignoring case.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
+ * than @p s2, zero if both strings equal ignoring case
+ */
+#define cx_strcasecmp(s1, s2) cx_strcasecmp_(cx_strcast(s1), cx_strcast(s2))
/**
* Compares two strings.
*
* This function has a compatible signature for the use as a cx_compare_func.
*
+ * @attention This function can @em only compare UCX strings. It is unsafe to
+ * pass normal C-strings to this function.
+ *
* @param s1 the first string
* @param s2 the second string
* @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
* than @p s2, zero if both strings equal
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_export
-int cx_strcmp_p(
- const void *s1,
- const void *s2
-);
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT int cx_strcmp_p(const void *s1, const void *s2);
/**
* Compares two strings ignoring case.
* @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
* than @p s2, zero if both strings equal ignoring case
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_export
-int cx_strcasecmp_p(
- const void *s1,
- const void *s2
-);
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT int cx_strcasecmp_p(const void *s1, const void *s2);
/**
* @return a duplicate of the string
* @see cx_strdup()
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_export
-cxmutstr cx_strdup_a_(
- const CxAllocator *allocator,
- cxstring string
-);
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
/**
* Creates a duplicate of the specified string.
* @see cx_strdup()
* @see cx_strfree_a()
*/
-#define cx_strdup_a(allocator, string) \
- cx_strdup_a_((allocator), cx_strcast(string))
+#define cx_strdup_a(allocator, string) cx_strdup_a_((allocator), cx_strcast(string))
/**
* Creates a duplicate of the specified string.
* @return the trimmed string
*/
cx_attr_nodiscard
-cx_attr_export
-cxstring cx_strtrim(cxstring string);
+CX_EXPORT cxstring cx_strtrim(cxstring string);
/**
* Omits leading and trailing spaces.
* @return the trimmed string
*/
cx_attr_nodiscard
-cx_attr_export
-cxmutstr cx_strtrim_m(cxmutstr string);
+CX_EXPORT cxmutstr cx_strtrim_m(cxmutstr string);
/**
- * Checks, if a string has a specific prefix.
+ * Checks if a string has a specific prefix.
*
* @param string the string to check
* @param prefix the prefix the string should have
* @c false otherwise
*/
cx_attr_nodiscard
-cx_attr_export
-bool cx_strprefix(
- cxstring string,
- cxstring prefix
-);
+CX_EXPORT bool cx_strprefix_(cxstring string, cxstring prefix);
+
+/**
+ * Checks if a string has a specific prefix.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return @c true, if and only if the string has the specified prefix,
+ * @c false otherwise
+ */
+#define cx_strprefix(string, prefix) cx_strprefix_(cx_strcast(string), cx_strcast(prefix))
/**
- * Checks, if a string has a specific suffix.
+ * Checks if a string has a specific suffix.
*
* @param string the string to check
* @param suffix the suffix the string should have
* @c false otherwise
*/
cx_attr_nodiscard
-cx_attr_export
-bool cx_strsuffix(
- cxstring string,
- cxstring suffix
-);
+CX_EXPORT bool cx_strsuffix_(cxstring string, cxstring suffix);
+
+/**
+ * Checks if a string has a specific suffix.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return @c true, if and only if the string has the specified suffix,
+ * @c false otherwise
+ */
+#define cx_strsuffix(string, suffix) cx_strsuffix_(cx_strcast(string), cx_strcast(suffix))
/**
- * Checks, if a string has a specific prefix, ignoring the case.
+ * Checks if a string has a specific prefix, ignoring the case.
*
* @param string the string to check
* @param prefix the prefix the string should have
* @c false otherwise
*/
cx_attr_nodiscard
-cx_attr_export
-bool cx_strcaseprefix(
- cxstring string,
- cxstring prefix
-);
+CX_EXPORT bool cx_strcaseprefix_(cxstring string, cxstring prefix);
+
+/**
+ * Checks if a string has a specific prefix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return @c true, if and only if the string has the specified prefix,
+ * @c false otherwise
+ */
+#define cx_strcaseprefix(string, prefix) cx_strcaseprefix_(cx_strcast(string), cx_strcast(prefix))
/**
* Checks, if a string has a specific suffix, ignoring the case.
* @c false otherwise
*/
cx_attr_nodiscard
-cx_attr_export
-bool cx_strcasesuffix(
- cxstring string,
- cxstring suffix
-);
+CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix);
+
+/**
+ * Checks, if a string has a specific suffix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return @c true, if and only if the string has the specified suffix,
+ * @c false otherwise
+ */
+#define cx_strcasesuffix(string, suffix) cx_strcasesuffix_(cx_strcast(string), cx_strcast(suffix))
/**
* Replaces a string with another string.
*
- * Replaces at most @p replmax occurrences.
+ * The function replaces at most @p replmax occurrences.
*
* The returned string will be allocated by @p allocator and is guaranteed
* to be zero-terminated.
* @param replmax maximum number of replacements
* @return the resulting string after applying the replacements
*/
-cx_attr_nodiscard
-cx_attr_nonnull
-cx_attr_export
-cxmutstr cx_strreplacen_a(
- const CxAllocator *allocator,
- cxstring str,
- cxstring search,
- cxstring replacement,
- size_t replmax
-);
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator,
+ cxstring str, cxstring search, cxstring replacement, size_t replmax);
/**
* Replaces a string with another string.
*
- * Replaces at most @p replmax occurrences.
+ * The function replaces at most @p replmax occurrences.
*
* The returned string will be allocated by the cxDefaultAllocator and is guaranteed
* to be zero-terminated.
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
#define cx_strreplacen(str, search, replacement, replmax) \
-cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax)
+ cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax)
/**
* Replaces a string with another string.
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
#define cx_strreplace_a(allocator, str, search, replacement) \
-cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX)
+ cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX)
/**
* Replaces a string with another string.
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
#define cx_strreplace(str, search, replacement) \
-cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX)
+ cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX)
/**
* Creates a string tokenization context.
* @return a new string tokenization context
*/
cx_attr_nodiscard
-cx_attr_export
-CxStrtokCtx cx_strtok_(
- cxstring str,
- cxstring delim,
- size_t limit
-);
+CX_EXPORT CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
/**
* Creates a string tokenization context.
* @return (@c CxStrtokCtx) a new string tokenization context
*/
#define cx_strtok(str, delim, limit) \
- cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit))
+ cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit))
/**
* Returns the next token.
* @return true if successful, false if the limit or the end of the string
* has been reached
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_access_w(2)
-cx_attr_export
-bool cx_strtok_next(
- CxStrtokCtx *ctx,
- cxstring *token
-);
+cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2)
+CX_EXPORT bool cx_strtok_next(CxStrtokCtx *ctx, cxstring *token);
/**
* Returns the next token of a mutable string.
* @return true if successful, false if the limit or the end of the string
* has been reached
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_access_w(2)
-cx_attr_export
-bool cx_strtok_next_m(
- CxStrtokCtx *ctx,
- cxmutstr *token
-);
+cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2)
+CX_EXPORT bool cx_strtok_next_m(CxStrtokCtx *ctx, cxmutstr *token);
/**
* Defines an array of more delimiters for the specified tokenization context.
* @param delim array of more delimiters
* @param count number of elements in the array
*/
-cx_attr_nonnull
-cx_attr_access_r(2, 3)
-cx_attr_export
-void cx_strtok_delim(
- CxStrtokCtx *ctx,
- const cxstring *delim,
- size_t count
-);
+cx_attr_nonnull cx_attr_access_r(2, 3)
+CX_EXPORT void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count);
/* ------------------------------------------------------------------------- *
* string to number conversion functions *
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
/**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a single precision floating-point number.
*
* The function returns non-zero when conversion is not possible.
* In that case the function sets errno to EINVAL when the reason is an invalid character.
* @param str the string to convert
* @param output a pointer to the float variable where the result shall be stored
* @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
/**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a double precision floating-point number.
*
* The function returns non-zero when conversion is not possible.
* In that case the function sets errno to EINVAL when the reason is an invalid character.
* @param str the string to convert
* @param output a pointer to the float variable where the result shall be stored
* @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
-int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+CX_EXPORT int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
/**
* Converts a string to a number.
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
- * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
*
* @param str the string to convert
#define cx_strtou64(str, output, base) cx_strtou64_lc_(cx_strcast(str), output, base, ",")
/**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a single precision floating-point number.
*
* The function returns non-zero when conversion is not possible.
* In that case the function sets errno to EINVAL when the reason is an invalid character.
* @param str the string to convert
* @param output a pointer to the float variable where the result shall be stored
* @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
#define cx_strtof_lc(str, output, decsep, groupsep) cx_strtof_lc_(cx_strcast(str), output, decsep, groupsep)
/**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a double precision floating-point number.
*
* The function returns non-zero when conversion is not possible.
* In that case the function sets errno to EINVAL when the reason is an invalid character.
* @param str the string to convert
* @param output a pointer to the double variable where the result shall be stored
* @param decsep the decimal separator
- * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @param groupsep each character in this string is treated as a group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
#define cx_strtod_lc(str, output, decsep, groupsep) cx_strtod_lc_(cx_strcast(str), output, decsep, groupsep)
/**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a single precision floating-point number.
*
* The function returns non-zero when conversion is not possible.
* In that case the function sets errno to EINVAL when the reason is an invalid character.
* It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
*
* The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose a different format, use cx_strtof_lc().
*
* @param str the string to convert
#define cx_strtof(str, output) cx_strtof_lc_(cx_strcast(str), output, '.', ",")
/**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a double precision floating-point number.
*
* The function returns non-zero when conversion is not possible.
* In that case the function sets errno to EINVAL when the reason is an invalid character.
*
* The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
+ * The comma character is treated as a group separator and ignored during parsing.
* If you want to choose a different format, use cx_strtof_lc().
*
* @param str the string to convert
* }
* </code>
*
- * @attention Do not call own functions within a test, that use
+ * @attention Do not call own functions within a test that use
* CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE().
*
* @author Mike Becker
* @param name optional name of the suite
* @return a new test suite
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_cstr_arg(1)
-cx_attr_malloc
+cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) cx_attr_malloc
static inline CxTestSuite* cx_test_suite_new(const char *name) {
CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite));
if (suite != NULL) {
*
* @param suite the test suite to free
*/
-static inline void cx_test_suite_free(CxTestSuite* suite) {
+CX_INLINE void cx_test_suite_free(CxTestSuite* suite) {
if (suite == NULL) return;
CxTestSet *l = suite->tests;
while (l != NULL) {
/**
* Registers a test function with the specified test suite.
*
- * @param suite the suite, the test function shall be added to
+ * @param suite the suite the test function shall be added to
* @param test the test function to register
* @retval zero success
* @retval non-zero failure
*/
cx_attr_nonnull
-static inline int cx_test_register(CxTestSuite* suite, CxTest test) {
+CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) {
CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet));
if (t) {
t->test = test;
* @param out_writer the write function writing to @p out_target
*/
cx_attr_nonnull
-static inline void cx_test_run(CxTestSuite *suite,
- void *out_target, cx_write_func out_writer) {
+CX_INLINE void cx_test_run(CxTestSuite *suite, void *out_target, cx_write_func out_writer) {
if (suite->name == NULL) {
out_writer("*** Test Suite ***\n", 1, 19, out_target);
} else {
* }
* @endcode
*
- * @attention Any CX_TEST_ASSERT() calls must be performed in scope of
+ * @attention Any CX_TEST_ASSERT() calls must be performed in the scope of
* #CX_TEST_DO.
*/
#define CX_TEST_DO _writefnc_("Running ", 1, 8, _output_);\
*
* This iterator is not position-aware in a strict sense, as it does not assume
* a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter variable.
+ * of the number of nodes it has passed in a counter-variable.
* Each node, regardless of the number of passes, is counted only once.
*
* @note Objects that are pointed to by an iterator are mutable through that
* iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.
+ * underlying data structure is mutated by other means than this iterator (e.g.,
* elements added or removed), the iterator becomes invalid (regardless of what
* cxIteratorValid() returns).
*
bool skip;
/**
* Set to true, when the iterator shall visit a node again
- * when all it's children have been processed.
+ * when all its children have been processed.
*/
bool visit_on_exit;
/**
*/
void *node;
/**
- * Stores a copy of the next pointer of the visited node.
+ * Stores a copy of the pointer to the successor of the visited node.
* Allows freeing a node on exit without corrupting the iteration.
*/
void *node_next;
*
* This iterator is not position-aware in a strict sense, as it does not assume
* a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter variable.
+ * of the number of nodes it has passed in a counter-variable.
* Each node, regardless of the number of passes, is counted only once.
*
* @note Objects that are pointed to by an iterator are mutable through that
* iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.
+ * underlying data structure is mutated by other means than this iterator (e.g.,
* elements added or removed), the iterator becomes invalid (regardless of what
* cxIteratorValid() returns).
*
* @param iter the iterator
*/
cx_attr_nonnull
-static inline void cxTreeIteratorDispose(CxTreeIterator *iter) {
- cxFreeDefault(iter->stack);
- iter->stack = NULL;
-}
+CX_EXPORT void cxTreeIteratorDispose(CxTreeIterator *iter);
/**
* Releases internal memory of the given tree visitor.
* @param visitor the visitor
*/
cx_attr_nonnull
-static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) {
- struct cx_tree_visitor_queue_s *q = visitor->queue_next;
- while (q != NULL) {
- struct cx_tree_visitor_queue_s *next = q->next;
- cxFreeDefault(q);
- q = next;
- }
-}
+CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor);
/**
* Advises the iterator to skip the subtree below the current node and
/**
* Links a node to a (new) parent.
*
- * If the node has already a parent, it is unlinked, first.
+ * If the node already has a parent, it is unlinked, first.
* If the parent has children already, the node is @em appended to the list
* of all currently existing children.
*
* @see cx_tree_unlink()
*/
cx_attr_nonnull
-cx_attr_export
-void cx_tree_link(
- void *parent,
- void *node,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+CX_EXPORT void cx_tree_link(void *parent, void *node,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Unlinks a node from its parent.
* @see cx_tree_link()
*/
cx_attr_nonnull
-cx_attr_export
-void cx_tree_unlink(
- void *node,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+CX_EXPORT void cx_tree_unlink(void *node,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Macro that can be used instead of the magic value for infinite search depth.
* Zero means exact match and a positive number is an implementation defined
* measure for the distance to an exact match.
*
- * For example if a tree stores file path information, a node that is
- * describing a parent directory of a filename that is searched, shall
+ * For example, consider a tree that stores file path information.
+ * A node which is describing a parent directory of a searched file shall
* return a positive number to indicate that a child node might contain the
* searched item. On the other hand, if the node denotes a path that is not a
* prefix of the searched filename, the function would return -1 to indicate
*
* @return 0 if the node contains the data,
* positive if one of the children might contain the data,
- * negative if neither the node, nor the children contains the data
+ * negative if neither the node nor the children contains the data
*/
-cx_attr_nonnull
typedef int (*cx_tree_search_data_func)(const void *node, const void *data);
* Zero means exact match and a positive number is an implementation defined
* measure for the distance to an exact match.
*
- * For example if a tree stores file path information, a node that is
- * describing a parent directory of a filename that is searched, shall
+ * For example, consider a tree that stores file path information.
+ * A node which is describing a parent directory of a searched file shall
* return a positive number to indicate that a child node might contain the
* searched item. On the other hand, if the node denotes a path that is not a
* prefix of the searched filename, the function would return -1 to indicate
*
* @return 0 if @p node contains the same data as @p new_node,
* positive if one of the children might contain the data,
- * negative if neither the node, nor the children contains the data
+ * negative if neither the node nor the children contains the data
*/
-cx_attr_nonnull
typedef int (*cx_tree_search_func)(const void *node, const void *new_node);
/**
* Searches for data in a tree.
*
- * When the data cannot be found exactly, the search function might return a
- * closest result which might be a good starting point for adding a new node
+ * When the data cannot be found exactly, the search function might return the
+ * closest result, which might be a good starting point for adding a new node
* to the tree (see also #cx_tree_add()).
*
- * Depending on the tree structure it is not necessarily guaranteed that the
+ * Depending on the tree structure, it is not necessarily guaranteed that the
* "closest" match is uniquely defined. This function will search for a node
* with the best match according to the @p sfunc (meaning: the return value of
* @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary
* could contain the node (but doesn't right now), negative if the tree does not
* contain any node that might be related to the searched data
*/
-cx_attr_nonnull
-cx_attr_access_w(5)
-cx_attr_export
-int cx_tree_search_data(
- const void *root,
- size_t depth,
- const void *data,
- cx_tree_search_data_func sfunc,
- void **result,
- ptrdiff_t loc_children,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull cx_attr_access_w(5)
+CX_EXPORT int cx_tree_search_data(const void *root, size_t depth,
+ const void *data, cx_tree_search_data_func sfunc,
+ void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
/**
* Searches for a node in a tree.
*
* When no node with the same data can be found, the search function might
- * return a closest result which might be a good starting point for adding the
+ * return the closest result, which might be a good starting point for adding the
* new node to the tree (see also #cx_tree_add()).
*
- * Depending on the tree structure it is not necessarily guaranteed that the
+ * Depending on the tree structure, it is not necessarily guaranteed that the
* "closest" match is uniquely defined. This function will search for a node
* with the best match according to the @p sfunc (meaning: the return value of
* @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary
* could contain the node (but doesn't right now), negative if the tree does not
* contain any node that might be related to the searched data
*/
-cx_attr_nonnull
-cx_attr_access_w(5)
-cx_attr_export
-int cx_tree_search(
- const void *root,
- size_t depth,
- const void *node,
- cx_tree_search_func sfunc,
- void **result,
- ptrdiff_t loc_children,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull cx_attr_access_w(5)
+CX_EXPORT int cx_tree_search(const void *root, size_t depth,
+ const void *node, cx_tree_search_func sfunc,
+ void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
/**
* Creates a depth-first iterator for a tree with the specified root node.
* @see cxTreeIteratorDispose()
*/
cx_attr_nodiscard
-cx_attr_export
-CxTreeIterator cx_tree_iterator(
- void *root,
- bool visit_on_exit,
- ptrdiff_t loc_children,
- ptrdiff_t loc_next
-);
+CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
+ ptrdiff_t loc_children, ptrdiff_t loc_next);
/**
* Creates a breadth-first iterator for a tree with the specified root node.
* @see cxTreeVisitorDispose()
*/
cx_attr_nodiscard
-cx_attr_export
-CxTreeVisitor cx_tree_visitor(
- void *root,
- ptrdiff_t loc_children,
- ptrdiff_t loc_next
-);
+CX_EXPORT CxTreeVisitor cx_tree_visitor(void *root,
+ ptrdiff_t loc_children, ptrdiff_t loc_next);
/**
* Describes a function that creates a tree node from the specified data.
- * The first argument points to the data the node shall contain and
- * the second argument may be used for additional data (e.g. an allocator).
+ * The first argument points to the data the node shall contain, and
+ * the second argument may be used for additional data (e.g., an allocator).
* Functions of this type shall either return a new pointer to a newly
* created node or @c NULL when allocation fails.
*
* @note the function may leave the node pointers in the struct uninitialized.
* The caller is responsible to set them according to the intended use case.
*/
-cx_attr_nonnull_arg(1)
typedef void *(*cx_tree_node_create_func)(const void *, void *);
/**
* This variable is used by #cx_tree_add_array() and #cx_tree_add_iter() to
* implement optimized insertion of multiple elements into a tree.
*/
-cx_attr_export
-extern unsigned int cx_tree_add_look_around_depth;
+CX_EXPORT extern unsigned int cx_tree_add_look_around_depth;
/**
* Adds multiple elements efficiently to a tree.
* @return the number of nodes created and added
* @see cx_tree_add()
*/
-cx_attr_nonnull_arg(1, 3, 4, 6, 7)
-cx_attr_access_w(6)
-cx_attr_export
-size_t cx_tree_add_iter(
- struct cx_iterator_base_s *iter,
- size_t num,
- cx_tree_search_func sfunc,
- cx_tree_node_create_func cfunc,
- void *cdata,
- void **failed,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull_arg(1, 3, 4, 6, 7) cx_attr_access_w(6)
+CX_EXPORT size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t num,
+ cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
+ void *cdata, void **failed, void *root,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Adds multiple elements efficiently to a tree.
* @return the number of array elements successfully processed
* @see cx_tree_add()
*/
-cx_attr_nonnull_arg(1, 4, 5, 7, 8)
-cx_attr_access_w(7)
-cx_attr_export
-size_t cx_tree_add_array(
- const void *src,
- size_t num,
- size_t elem_size,
- cx_tree_search_func sfunc,
- cx_tree_node_create_func cfunc,
- void *cdata,
- void **failed,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull_arg(1, 4, 5, 7, 8) cx_attr_access_w(7)
+CX_EXPORT size_t cx_tree_add_array(const void *src, size_t num, size_t elem_size,
+ cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
+ void *cdata, void **failed, void *root,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Adds data to a tree.
* When a location is found, the @p cfunc will be invoked with @p cdata.
*
* The node returned by @p cfunc will be linked into the tree.
- * When @p sfunc returned a positive integer, the new node will be linked as a
+ * When @p sfunc returns a positive integer, the new node will be linked as a
* child. The other children (now siblings of the new node) are then checked
* with @p sfunc, whether they could be children of the new node and re-linked
* accordingly.
*
- * When @p sfunc returned zero and the found node has a parent, the new
- * node will be added as sibling - otherwise, the new node will be added
+ * When @p sfunc returns zero and the found node has a parent, the new
+ * node will be added as a sibling - otherwise, the new node will be added
* as a child.
*
- * When @p sfunc returned a negative value, the new node will not be added to
- * the tree and this function returns a non-zero value.
+ * When @p sfunc returns a negative value, the new node will not be added to
+ * the tree, and this function returns a non-zero value.
* The caller should check if @p cnode contains a node pointer and deal with the
* node that could not be added.
*
* @return zero when a new node was created and added to the tree,
* non-zero otherwise
*/
-cx_attr_nonnull_arg(1, 2, 3, 5, 6)
-cx_attr_access_w(5)
-cx_attr_export
-int cx_tree_add(
- const void *src,
- cx_tree_search_func sfunc,
- cx_tree_node_create_func cfunc,
- void *cdata,
- void **cnode,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull_arg(1, 2, 3, 5, 6) cx_attr_access_w(5)
+CX_EXPORT int cx_tree_add(const void *src,
+ cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
+ void *cdata, void **cnode, void *root,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* A function to create new nodes.
*
* Invocations to this function will receive a pointer to this tree
- * structure as second argument.
+ * structure as the second argument.
*
- * Nodes MAY use #cx_tree_node_base_s as base layout, but do not need to.
+ * Nodes MAY use #cx_tree_node_base_s as the base layout, but do not need to.
*/
cx_tree_node_create_func node_create;
* Macro to roll out the #cx_tree_node_base_s structure with a custom
* node type.
*
- * Must be used as first member in your custom tree struct.
+ * Must be used as the first member in your custom tree struct.
*
* @param type the data type for the nodes
*/
* Implementations SHALL NOT simply invoke @p insert_many as this comes
* with too much overhead.
*/
- int (*insert_element)(
- struct cx_tree_s *tree,
- const void *data
- );
+ int (*insert_element)(struct cx_tree_s *tree, const void *data);
/**
* Member function for inserting multiple elements.
*
- * Implementations SHALL avoid to perform a full search in the tree for
+ * Implementations SHALL avoid performing a full search in the tree for
* every element even though the source data MAY be unsorted.
*/
- size_t (*insert_many)(
- struct cx_tree_s *tree,
- struct cx_iterator_base_s *iter,
- size_t n
- );
+ size_t (*insert_many)(struct cx_tree_s *tree, struct cx_iterator_base_s *iter, size_t n);
/**
* Member function for finding a node.
*/
- void *(*find)(
- struct cx_tree_s *tree,
- const void *subtree,
- const void *data,
- size_t depth
- );
+ void *(*find)(struct cx_tree_s *tree, const void *subtree, const void *data, size_t depth);
};
/**
/**
- * Destroys a node and it's subtree.
+ * Destroys a node and its subtree.
*
* It is guaranteed that the simple destructor is invoked before
* the advanced destructor, starting with the leaf nodes of the subtree.
* @see cxTreeFree()
*/
cx_attr_nonnull
-cx_attr_export
-void cxTreeDestroySubtree(CxTree *tree, void *node);
+CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node);
/**
*
* @attention Be careful when calling this function when no destructor function
* is registered that actually frees the memory of nodes. In that case you will
- * need a reference to the (former) root node of the tree somewhere or
+ * need a reference to the (former) root node of the tree somewhere, or
* otherwise you will be leaking memory.
*
* @param tree the tree
*
* @param tree the tree to free
*/
-cx_attr_export
-void cxTreeFree(CxTree *tree);
+CX_EXPORT void cxTreeFree(CxTree *tree);
/**
* Creates a new tree structure based on the specified layout.
* @see cxTreeCreateSimple()
* @see cxTreeCreateWrapped()
*/
-cx_attr_nonnull_arg(2, 3, 4)
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc(cxTreeFree, 1)
-cx_attr_export
-CxTree *cxTreeCreate(
- const CxAllocator *allocator,
- cx_tree_node_create_func create_func,
- cx_tree_search_func search_func,
- cx_tree_search_data_func search_data_func,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull_arg(2, 3, 4) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1)
+CX_EXPORT CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func,
+ cx_tree_search_func search_func, cx_tree_search_data_func search_data_func,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Creates a new tree structure based on a default layout.
*
- * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as first
+ * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as the first
* member (or at least respect the default offsets specified in the tree
- * struct) and they MUST be allocated with the specified allocator.
+ * struct), and they MUST be allocated with the specified allocator.
*
* @note This function will also register an advanced destructor which
* will free the nodes with the allocator's free() method.
* @return (@c CxTree*) the new tree
* @see cxTreeCreate()
*/
-#define cxTreeCreateSimple(\
- allocator, create_func, search_func, search_data_func \
-) cxTreeCreate(allocator, create_func, search_func, search_data_func, \
-cx_tree_node_base_layout)
+#define cxTreeCreateSimple(allocator, create_func, search_func, search_data_func) \
+ cxTreeCreate(allocator, create_func, search_func, search_data_func, cx_tree_node_base_layout)
/**
* Creates a new tree structure based on an existing tree.
* @attention This function will create an incompletely defined tree structure
* where neither the create function, the search function, nor a destructor
* will be set. If you wish to use any of this functionality for the wrapped
- * tree, you need to specify those functions afterwards.
+ * tree, you need to specify those functions afterward.
*
* @param allocator the allocator that was used for nodes of the wrapped tree
* (if @c NULL, the cxDefaultAllocator is assumed)
* @return the new tree
* @see cxTreeCreate()
*/
-cx_attr_nonnull_arg(2)
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc(cxTreeFree, 1)
-cx_attr_export
-CxTree *cxTreeCreateWrapped(
- const CxAllocator *allocator,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-);
+cx_attr_nonnull_arg(2) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1)
+CX_EXPORT CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Inserts data into the tree.
* @retval non-zero failure
*/
cx_attr_nonnull
-static inline int cxTreeInsert(
- CxTree *tree,
- const void *data
-) {
- return tree->cl->insert_element(tree, data);
-}
+CX_EXPORT int cxTreeInsert(CxTree *tree, const void *data);
/**
* Inserts elements provided by an iterator efficiently into the tree.
* @return the number of elements that could be successfully inserted
*/
cx_attr_nonnull
-static inline size_t cxTreeInsertIter(
- CxTree *tree,
- CxIteratorBase *iter,
- size_t n
-) {
- return tree->cl->insert_many(tree, iter, n);
-}
+CX_EXPORT size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n);
/**
* Inserts an array of data efficiently into the tree.
* @return the number of elements that could be successfully inserted
*/
cx_attr_nonnull
-static inline size_t cxTreeInsertArray(
- CxTree *tree,
- const void *data,
- size_t elem_size,
- size_t n
-) {
- if (n == 0) return 0;
- if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0;
- CxIterator iter = cxIterator(data, elem_size, n);
- return cxTreeInsertIter(tree, cxIteratorRef(iter), n);
-}
+CX_EXPORT size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n);
/**
* Searches the data in the specified tree.
* @param data the data to search for
* @return the first matching node, or @c NULL when the data cannot be found
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxTreeFind(
- CxTree *tree,
- const void *data
-) {
- return tree->cl->find(tree, tree->root, data, 0);
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT void *cxTreeFind(CxTree *tree, const void *data);
/**
* Searches the data in the specified subtree.
* @param max_depth the maximum search depth
* @return the first matching node, or @c NULL when the data cannot be found
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline void *cxTreeFindInSubtree(
- CxTree *tree,
- const void *data,
- void *subtree_root,
- size_t max_depth
-) {
- return tree->cl->find(tree, subtree_root, data, max_depth);
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth);
/**
* Determines the size of the specified subtree.
* @param subtree_root the root node of the subtree
* @return the number of nodes in the specified subtree
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
/**
* Determines the depth of the specified subtree.
* @param subtree_root the root node of the subtree
* @return the tree depth including the @p subtree_root
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
/**
* Determines the size of the entire tree.
* @param tree the tree
* @return the tree size, counting the root as one
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline size_t cxTreeSize(CxTree *tree) {
- return tree->size;
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT size_t cxTreeSize(CxTree *tree);
/**
* Determines the depth of the entire tree.
* @param tree the tree
* @return the tree depth, counting the root as one
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-cx_attr_export
-size_t cxTreeDepth(CxTree *tree);
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT size_t cxTreeDepth(CxTree *tree);
/**
* Creates a depth-first iterator for the specified tree starting in @p node.
* @return a tree iterator (depth-first)
* @see cxTreeVisit()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline CxTreeIterator cxTreeIterateSubtree(
- CxTree *tree,
- void *node,
- bool visit_on_exit
-) {
- return cx_tree_iterator(
- node, visit_on_exit,
- tree->loc_children, tree->loc_next
- );
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit);
/**
* Creates a breadth-first iterator for the specified tree starting in @p node.
* @return a tree visitor (a.k.a. breadth-first iterator)
* @see cxTreeIterate()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) {
- return cx_tree_visitor(
- node, tree->loc_children, tree->loc_next
- );
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node);
/**
* Creates a depth-first iterator for the specified tree.
* @return a tree iterator (depth-first)
* @see cxTreeVisit()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline CxTreeIterator cxTreeIterate(
- CxTree *tree,
- bool visit_on_exit
-) {
- return cxTreeIterateSubtree(tree, tree->root, visit_on_exit);
-}
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
/**
* Creates a breadth-first iterator for the specified tree.
* @return a tree visitor (a.k.a. breadth-first iterator)
* @see cxTreeIterate()
*/
-cx_attr_nonnull
-cx_attr_nodiscard
-static inline CxTreeVisitor cxTreeVisit(CxTree *tree) {
- return cxTreeVisitSubtree(tree, tree->root);
-}
+cx_attr_nonnull cx_attr_nodiscard
+CxTreeVisitor cxTreeVisit(CxTree *tree);
/**
* Sets the (new) parent of the specified child.
*
- * If the @p child is not already member of the tree, this function behaves
+ * If the @p child is not already a member of the tree, this function behaves
* as #cxTreeAddChildNode().
*
* @param tree the tree
* @see cxTreeAddChildNode()
*/
cx_attr_nonnull
-cx_attr_export
-void cxTreeSetParent(
- CxTree *tree,
- void *parent,
- void *child
-);
+CX_EXPORT void cxTreeSetParent(CxTree *tree, void *parent, void *child);
/**
* Adds a new node to the tree.
*
- * If the @p child is already member of the tree, the behavior is undefined.
+ * If the @p child is already a member of the tree, the behavior is undefined.
* Use #cxTreeSetParent() if you want to move a subtree to another location.
*
* @attention The node may be externally created, but MUST obey the same rules
- * as if it was created by the tree itself with #cxTreeAddChild() (e.g. use
+ * as if it was created by the tree itself with #cxTreeAddChild() (e.g., use
* the same allocator).
*
* @param tree the tree
* @see cxTreeSetParent()
*/
cx_attr_nonnull
-cx_attr_export
-void cxTreeAddChildNode(
- CxTree *tree,
- void *parent,
- void *child
-);
+CX_EXPORT void cxTreeAddChildNode(CxTree *tree, void *parent, void *child);
/**
* Creates a new node and adds it to the tree.
* leaving this task to the tree by using #cxTreeInsert().
*
* Be aware that adding nodes at arbitrary locations in the tree might cause
- * wrong or undesired results when subsequently invoking #cxTreeInsert() and
+ * wrong or undesired results when subsequently invoking #cxTreeInsert(), and
* the invariant imposed by the search function does not hold any longer.
*
* @param tree the tree
* @see cxTreeInsert()
*/
cx_attr_nonnull
-cx_attr_export
-int cxTreeAddChild(
- CxTree *tree,
- void *parent,
- const void *data
-);
+CX_EXPORT int cxTreeAddChild(CxTree *tree, void *parent, const void *data);
/**
* A function that is invoked when a node needs to be re-linked to a new parent.
* @param old_parent the old parent of the node
* @param new_parent the new parent of the node
*/
-cx_attr_nonnull
typedef void (*cx_tree_relink_func)(
void *node,
const void *old_parent,
* @return zero on success, non-zero if @p node is the root node of the tree
*/
cx_attr_nonnull_arg(1, 2)
-cx_attr_export
-int cxTreeRemoveNode(
- CxTree *tree,
- void *node,
- cx_tree_relink_func relink_func
-);
+CX_EXPORT int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
/**
- * Removes a node and it's subtree from the tree.
+ * Removes a node and its subtree from the tree.
*
* If the node is not part of the tree, the behavior is undefined.
*
* @param node the node to remove
*/
cx_attr_nonnull
-cx_attr_export
-void cxTreeRemoveSubtree(CxTree *tree, void *node);
+CX_EXPORT void cxTreeRemoveSubtree(CxTree *tree, void *node);
/**
* Destroys a node and re-links its children to its former parent.
* @return zero on success, non-zero if @p node is the root node of the tree
*/
cx_attr_nonnull_arg(1, 2)
-cx_attr_export
-int cxTreeDestroyNode(
- CxTree *tree,
- void *node,
- cx_tree_relink_func relink_func
-);
+CX_EXPORT int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
#ifdef __cplusplus
} // extern "C"
*/
#include "cx/hash_key.h"
+#include "cx/compare.h"
#include <string.h>
void cx_hash_murmur(CxHashKey *key) {
switch (len) {
case 3:
h ^= (data[i + 2] & 0xFF) << 16;
- __attribute__((__fallthrough__));
+ cx_attr_fallthrough;
case 2:
h ^= (data[i + 1] & 0xFF) << 8;
- __attribute__((__fallthrough__));
+ cx_attr_fallthrough;
case 1:
h ^= (data[i + 0] & 0xFF);
h *= m;
- __attribute__((__fallthrough__));
+ cx_attr_fallthrough;
default: // do nothing
;
}
key->hash = h;
}
+
+uint32_t cx_hash_u32(uint32_t x) {
+ x = ((x >> 16) ^ x) * 0x45d9f3bu;
+ x = ((x >> 16) ^ x) * 0x45d9f3bu;
+ x = (x >> 16) ^ x;
+ return x;
+}
+
+uint64_t cx_hash_u64(uint64_t x) {
+ x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
+ x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
+ x = x ^ (x >> 31);
+ return x;
+}
+
CxHashKey cx_hash_key_str(const char *str) {
CxHashKey key;
key.data = str;
return key;
}
+CxHashKey cx_hash_key_ustr(unsigned const char *str) {
+ CxHashKey key;
+ key.data = str;
+ key.len = str == NULL ? 0 : strlen((const char*)str);
+ cx_hash_murmur(&key);
+ return key;
+}
+
+CxHashKey cx_hash_key_cxstr(cxstring str) {
+ return cx_hash_key(str.ptr, str.length);
+}
+
+CxHashKey cx_hash_key_mutstr(cxmutstr str) {
+ return cx_hash_key(str.ptr, str.length);
+}
+
CxHashKey cx_hash_key_bytes(
const unsigned char *bytes,
size_t len
cx_hash_murmur(&key);
return key;
}
+
+CxHashKey cx_hash_key_u32(uint32_t x) {
+ CxHashKey key;
+ key.data = NULL;
+ key.len = 0;
+ key.hash = cx_hash_u32(x);
+ return key;
+}
+
+CxHashKey cx_hash_key_u64(uint64_t x) {
+ CxHashKey key;
+ key.data = NULL;
+ key.len = 0;
+ key.hash = cx_hash_u64(x);
+ return key;
+}
+
+int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) {
+ int d;
+ d = cx_vcmp_uint64(left->hash, right->hash);
+ if (d != 0) return d;
+ d = cx_vcmp_size(left->len, right->len);
+ if (d != 0) return d;
+ if (left->len == 0) return 0;
+ return memcmp(left->data, right->data, left->len);
+}
cxFree(map->collection.allocator, map);
}
-static int cx_hash_map_put(
+static void *cx_hash_map_put(
CxMap *map,
CxHashKey key,
void *value
elm = elm->next;
}
- if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len &&
- memcmp(elm->key.data, key.data, key.len) == 0) {
+ if (elm != NULL && cx_hash_key_cmp(&elm->key, &key) == 0) {
// overwrite existing element, but call destructors first
cx_invoke_destructor(map, elm->data);
- if (map->collection.store_pointer) {
+ if (value == NULL) {
+ memset(elm->data, 0, map->collection.elem_size);
+ } else if (map->collection.store_pointer) {
memcpy(elm->data, &value, sizeof(void *));
} else {
memcpy(elm->data, value, map->collection.elem_size);
allocator,
sizeof(struct cx_hash_map_element_s) + map->collection.elem_size
);
- if (e == NULL) return -1;
+ if (e == NULL) return NULL;
// write the value
- if (map->collection.store_pointer) {
+ if (value == NULL) {
+ memset(e->data, 0, map->collection.elem_size);
+ } else if (map->collection.store_pointer) {
memcpy(e->data, &value, sizeof(void *));
} else {
memcpy(e->data, value, map->collection.elem_size);
// copy the key
void *kd = cxMalloc(allocator, key.len);
- if (kd == NULL) return -1;
+ if (kd == NULL) {
+ cxFree(allocator, e);
+ return NULL;
+ }
memcpy(kd, key.data, key.len);
e->key.data = kd;
e->key.len = key.len;
prev->next = e;
}
e->next = elm;
+ elm = e;
// increase the size
map->collection.size++;
}
- return 0;
+ // return pointer to the element
+ return elm->data;
}
static void cx_hash_map_unlink(
struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
struct cx_hash_map_element_s *prev = NULL;
while (elm && elm->key.hash <= hash) {
- if (elm->key.hash == hash && elm->key.len == key.len) {
- if (memcmp(elm->key.data, key.data, key.len) == 0) {
- if (remove) {
- if (targetbuf == NULL) {
- cx_invoke_destructor(map, elm->data);
- } else {
- memcpy(targetbuf, elm->data, map->collection.elem_size);
- }
- cx_hash_map_unlink(hash_map, slot, prev, elm);
+ if (cx_hash_key_cmp(&elm->key, &key) == 0) {
+ if (remove) {
+ if (targetbuf == NULL) {
+ cx_invoke_destructor(map, elm->data);
+ } else {
+ memcpy(targetbuf, elm->data, map->collection.elem_size);
+ }
+ cx_hash_map_unlink(hash_map, slot, prev, elm);
+ } else {
+ assert(targetbuf != NULL);
+ void *data = NULL;
+ if (map->collection.store_pointer) {
+ data = *(void **) elm->data;
} else {
- assert(targetbuf != NULL);
- void *data = NULL;
- if (map->collection.store_pointer) {
- data = *(void **) elm->data;
- } else {
- data = elm->data;
- }
- memcpy(targetbuf, &data, sizeof(void *));
+ data = elm->data;
}
- return 0;
+ memcpy(targetbuf, &data, sizeof(void *));
}
+ return 0;
}
prev = elm;
elm = prev->next;
static void *cx_hash_map_iter_current_key(const void *it) {
const CxMapIterator *iter = it;
- struct cx_hash_map_element_s *elm = iter->elem;
- return &elm->key;
+ return (void*) iter->entry.key;
}
static void *cx_hash_map_iter_current_value(const void *it) {
const CxMapIterator *iter = it;
- const CxMap *map = iter->map.c;
- struct cx_hash_map_element_s *elm = iter->elem;
- if (map->collection.store_pointer) {
- return *(void **) elm->data;
- } else {
- return elm->data;
- }
+ return iter->entry.value;
}
static bool cx_hash_map_iter_valid(const void *it) {
static void cx_hash_map_iter_next(void *it) {
CxMapIterator *iter = it;
- CxMap *map = iter->map.m;
+ CxMap *map = iter->map;
struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map;
struct cx_hash_map_element_s *elm = iter->elem;
// unlink
cx_hash_map_unlink(hmap, iter->slot, prev, elm);
+ iter->elem_count--;
// advance
elm = next;
// must not modify the iterator (the parameter is const)
if (elm != NULL) {
iter->entry.key = &elm->key;
- if (iter->map.c->collection.store_pointer) {
+ if (map->collection.store_pointer) {
iter->entry.value = *(void **) elm->data;
} else {
iter->entry.value = elm->data;
) {
CxMapIterator iter;
- iter.map.c = map;
+ iter.map = (CxMap*) map;
iter.elem_count = map->collection.size;
switch (type) {
iter.base.valid = cx_hash_map_iter_valid;
iter.base.next = cx_hash_map_iter_next;
iter.base.remove = false;
- iter.base.mutating = false;
+ iter.base.allow_remove = true;
iter.slot = 0;
iter.index = 0;
// only move the last element when we are not currently aiming
// at the last element already
if (iter->index < iter->elem_count) {
- void *last = ((char *) iter->src_handle.m)
+ void *last = ((char *) iter->src_handle)
+ iter->elem_count * iter->elem_size;
memcpy(iter->elem_handle, last, iter->elem_size);
}
}
}
-CxIterator cxMutIterator(
- void *array,
+CxIterator cxIterator(
+ const void *array,
size_t elem_size,
size_t elem_count,
bool remove_keeps_order
CxIterator iter;
iter.index = 0;
- iter.src_handle.m = array;
- iter.elem_handle = array;
+ iter.src_handle = (void*) array;
+ iter.elem_handle = (void*) array;
iter.elem_size = elem_size;
iter.elem_count = array == NULL ? 0 : elem_count;
iter.base.valid = cx_iter_valid;
iter.base.current = cx_iter_current;
iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast;
iter.base.remove = false;
- iter.base.mutating = true;
+ iter.base.allow_remove = true;
return iter;
}
-CxIterator cxIterator(
+CxIterator cxIteratorPtr(
const void *array,
- size_t elem_size,
- size_t elem_count
-) {
- CxIterator iter = cxMutIterator((void*)array, elem_size, elem_count, false);
- iter.base.mutating = false;
- return iter;
-}
-
-CxIterator cxMutIteratorPtr(
- void *array,
size_t elem_count,
bool remove_keeps_order
) {
- CxIterator iter = cxMutIterator(array, sizeof(void*), elem_count, remove_keeps_order);
+ CxIterator iter = cxIterator(array, sizeof(void*), elem_count, remove_keeps_order);
iter.base.current = cx_iter_current_ptr;
return iter;
}
-
-CxIterator cxIteratorPtr(
- const void *array,
- size_t elem_count
-) {
- CxIterator iter = cxMutIteratorPtr((void*) array, elem_count, false);
- iter.base.mutating = false;
- return iter;
-}
#include <assert.h>
#include <stdio.h>
#include <inttypes.h>
+#include <ctype.h>
/*
* RFC 8259
return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name));
}
-static CxJsonObjValue *json_find_objvalue(const CxJsonValue *obj, cxstring name) {
+static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) {
assert(obj->type == CX_JSON_OBJECT);
CxJsonObjValue kv_dummy;
kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length);
- size_t index = cx_array_binary_search(
+ return cx_array_binary_search(
obj->value.object.values,
obj->value.object.values_size,
sizeof(CxJsonObjValue),
&kv_dummy,
json_cmp_objvalue
);
- if (index == obj->value.object.values_size) {
- return NULL;
- } else {
- return &obj->value.object.values[index];
- }
}
static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) {
}
}
-static bool json_isdigit(char c) {
- // TODO: remove once UCX has public API for this
- return c >= '0' && c <= '9';
-}
-
-static bool json_isspace(char c) {
- // TODO: remove once UCX has public API for this
- return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
-}
-
static int num_isexp(const char *content, size_t length, size_t pos) {
if (pos >= length) {
return 0;
int ok = 0;
for (size_t i = pos; i < length; i++) {
char c = content[i];
- if (json_isdigit(c)) {
+ if (isdigit((unsigned char)c)) {
ok = 1;
} else if (i == pos) {
if (!(c == '+' || c == '-')) {
static CxJsonTokenType token_numbertype(const char *content, size_t length) {
if (length == 0) return CX_JSON_TOKEN_ERROR;
- if (content[0] != '-' && !json_isdigit(content[0])) {
+ if (content[0] != '-' && !isdigit((unsigned char)content[0])) {
return CX_JSON_TOKEN_ERROR;
}
type = CX_JSON_TOKEN_NUMBER;
} else if (content[i] == 'e' || content[i] == 'E') {
return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR;
- } else if (!json_isdigit(content[i])) {
+ } else if (!isdigit((unsigned char)content[i])) {
return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep
}
}
return CX_JSON_TOKEN_STRING;
}
default: {
- if (json_isspace(c)) {
+ if (isspace((unsigned char)c)) {
return CX_JSON_TOKEN_SPACE;
}
}
}
}
+void cxJsonReset(CxJson *json) {
+ const CxAllocator *allocator = json->allocator;
+ cxJsonDestroy(json);
+ cxJsonInit(json, allocator);
+}
+
int cxJsonFilln(CxJson *json, const char *buf, size_t size) {
if (cxBufferEof(&json->buffer)) {
// reinitialize the buffer
return value->value.array.array[index];
}
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
+ if (index >= value->value.array.array_size) {
+ return NULL;
+ }
+ CxJsonValue *ret = value->value.array.array[index];
+ // TODO: replace with a low level cx_array_remove()
+ size_t count = value->value.array.array_size - index - 1;
+ if (count > 0) {
+ memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*));
+ }
+ value->value.array.array_size--;
+ return ret;
+}
+
+char *cxJsonAsString(const CxJsonValue *value) {
+ return value->value.string.ptr;
+}
+
+cxstring cxJsonAsCxString(const CxJsonValue *value) {
+ return cx_strcast(value->value.string);
+}
+
+cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
+ return value->value.string;
+}
+
+double cxJsonAsDouble(const CxJsonValue *value) {
+ if (value->type == CX_JSON_INTEGER) {
+ return (double) value->value.integer;
+ } else {
+ return value->value.number;
+ }
+}
+
+int64_t cxJsonAsInteger(const CxJsonValue *value) {
+ if (value->type == CX_JSON_INTEGER) {
+ return value->value.integer;
+ } else {
+ return (int64_t) value->value.number;
+ }
+}
+
CxIterator cxJsonArrIter(const CxJsonValue *value) {
return cxIteratorPtr(
value->value.array.array,
- value->value.array.array_size
+ value->value.array.array_size,
+ true // arrays need to keep order
);
}
return cxIterator(
value->value.object.values,
sizeof(CxJsonObjValue),
- value->value.object.values_size
+ value->value.object.values_size,
+ true // TODO: objects do not always need to keep order
);
}
-CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) {
- CxJsonObjValue *member = json_find_objvalue(value, name);
- if (member == NULL) {
+CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
+ size_t index = json_find_objvalue(value, name);
+ if (index >= value->value.object.values_size) {
return &cx_json_value_nothing;
} else {
- return member->value;
+ return value->value.object.values[index].value;
+ }
+}
+
+CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
+ size_t index = json_find_objvalue(value, name);
+ if (index >= value->value.object.values_size) {
+ return NULL;
+ } else {
+ CxJsonObjValue kv = value->value.object.values[index];
+ cx_strfree_a(value->allocator, &kv.name);
+ // TODO: replace with cx_array_remove() / cx_array_remove_fast()
+ value->value.object.values_size--;
+ memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
+ return kv.value;
}
}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/kv_list.h"
+#include "cx/hash_map.h"
+#include "cx/linked_list.h"
+
+#include <string.h>
+#include <assert.h>
+
+typedef struct cx_kv_list_s cx_kv_list;
+
+struct cx_kv_list_map_s {
+ struct cx_hash_map_s map_base;
+ /** Back-reference to the list. */
+ cx_kv_list *list;
+};
+
+struct cx_kv_list_s {
+ struct cx_linked_list_s list;
+ /** The lookup map - stores pointers to the nodes. */
+ struct cx_kv_list_map_s *map;
+ const cx_list_class *list_methods;
+ const cx_map_class *map_methods;
+ cx_destructor_func list_destr;
+ cx_destructor_func2 list_destr2;
+ void *list_destr_data;
+ cx_destructor_func map_destr;
+ cx_destructor_func2 map_destr2;
+ void *map_destr_data;
+};
+
+static void cx_kv_list_destructor_wrapper(void *list_ptr, void *elem) {
+ const cx_kv_list *kv_list = list_ptr;
+ // list destructor is already called with proper deref of the elem
+ if (kv_list->list_destr) {
+ kv_list->list_destr(elem);
+ }
+ if (kv_list->list_destr2) {
+ kv_list->list_destr2(kv_list->list_destr_data, elem);
+ }
+ if (kv_list->map_destr) {
+ kv_list->map_destr(elem);
+ }
+ if (kv_list->map_destr2) {
+ kv_list->map_destr2(kv_list->map_destr_data, elem);
+ }
+}
+
+static void cx_kv_list_update_destructors(cx_kv_list *list) {
+ // we copy the destructors to our custom fields and register
+ // an own destructor function which invokes all these
+ if (list->list.base.collection.simple_destructor != NULL) {
+ list->list_destr = list->list.base.collection.simple_destructor;
+ list->list.base.collection.simple_destructor = NULL;
+ }
+ if (list->list.base.collection.advanced_destructor != cx_kv_list_destructor_wrapper) {
+ list->list_destr2 = list->list.base.collection.advanced_destructor;
+ list->list_destr_data = list->list.base.collection.destructor_data;
+ list->list.base.collection.advanced_destructor = cx_kv_list_destructor_wrapper;
+ list->list.base.collection.destructor_data = list;
+ }
+ if (list->map->map_base.base.collection.simple_destructor != NULL) {
+ list->map_destr = list->map->map_base.base.collection.simple_destructor;
+ list->map->map_base.base.collection.simple_destructor = NULL;
+ }
+ if (list->map->map_base.base.collection.advanced_destructor != NULL) {
+ list->map_destr2 = list->map->map_base.base.collection.advanced_destructor;
+ list->map_destr_data = list->map->map_base.base.collection.destructor_data;
+ list->map->map_base.base.collection.advanced_destructor = NULL;
+ list->map->map_base.base.collection.destructor_data = NULL;
+ }
+}
+
+static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list, void *node_data) {
+ return (CxHashKey*)((char*)node_data + list->list.base.collection.elem_size);
+}
+
+static void cx_kvl_deallocate(struct cx_list_s *list) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ // patch the destructors
+ cx_kv_list_update_destructors(kv_list);
+ kv_list->map_methods->deallocate(&kv_list->map->map_base.base);
+ // then free the list, now the destructors may be called
+ kv_list->list_methods->deallocate(list);
+}
+
+static void *cx_kvl_insert_element(
+ struct cx_list_s *list,
+ size_t index,
+ const void *data
+) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ return kv_list->list_methods->insert_element(list, index, data);
+}
+
+static size_t cx_kvl_insert_array(
+ struct cx_list_s *list,
+ size_t index,
+ const void *data,
+ size_t n
+) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ return kv_list->list_methods->insert_array(list, index, data, n);
+}
+
+static size_t cx_kvl_insert_sorted(
+ struct cx_list_s *list,
+ const void *sorted_data,
+ size_t n
+) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ return kv_list->list_methods->insert_sorted(list, sorted_data, n);
+}
+
+static size_t cx_kvl_insert_unique(
+ struct cx_list_s *list,
+ const void *sorted_data,
+ size_t n
+) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ return kv_list->list_methods->insert_unique(list, sorted_data, n);
+}
+
+static int cx_kvl_insert_iter(
+ struct cx_iterator_s *iter,
+ const void *elem,
+ int prepend
+) {
+ cx_kv_list *kv_list = iter->src_handle;
+ return kv_list->list_methods->insert_iter(iter, elem, prepend);
+}
+
+static size_t cx_kvl_remove(
+ struct cx_list_s *list,
+ size_t index,
+ size_t num,
+ void *targetbuf
+) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ // patch the destructors
+ // we also have to do that when targetbuf is NULL,
+ // because we do not want wrong destructors to be called when we remove keys from the map
+ cx_kv_list_update_destructors(kv_list);
+ // iterate through the elements first and remove their keys from the map
+ CxIterator iter = kv_list->list_methods->iterator(list, index, false);
+ for (size_t i = 0; i < num && cxIteratorValid(iter); i++) {
+ void *node_data = cxIteratorCurrent(iter);
+ CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data);
+ // when the hash is zero, there is no key assigned to that element
+ if (key->hash != 0) {
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
+ }
+ cxIteratorNext(iter);
+ }
+ return kv_list->list_methods->remove(list, index, num, targetbuf);
+}
+
+static void cx_kvl_clear(struct cx_list_s *list) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ // patch the destructors
+ cx_kv_list_update_destructors(kv_list);
+ // clear the list
+ kv_list->list_methods->clear(list);
+ // then clear the map
+ kv_list->map_methods->clear(&kv_list->map->map_base.base);
+}
+
+static int cx_kvl_swap(
+ struct cx_list_s *list,
+ size_t i,
+ size_t j
+) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ return kv_list->list_methods->swap(list, i, j);
+}
+
+static void *cx_kvl_at(
+ const struct cx_list_s *list,
+ size_t index
+) {
+ const cx_kv_list *kv_list = (const cx_kv_list*)list;
+ return kv_list->list_methods->at(list, index);
+}
+
+static size_t cx_kvl_find_remove(
+ struct cx_list_s *list,
+ const void *elem,
+ bool remove
+) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ // we do not use the original list methods,
+ // because that would need two passes for removal
+ // (the first to find the index, the second to get a pointer)
+ if (list->collection.size == 0) return 0;
+
+ size_t index;
+ cx_linked_list *ll = &kv_list->list;
+ char *node = cx_linked_list_find(
+ ll->begin,
+ ll->loc_next, ll->loc_data,
+ list->collection.cmpfunc, elem,
+ &index
+ );
+ if (node == NULL) {
+ return list->collection.size;
+ }
+ if (remove) {
+ cx_kv_list_update_destructors(kv_list);
+ cx_invoke_advanced_destructor(list, node + ll->loc_data);
+ cx_linked_list_remove(&ll->begin, &ll->end,
+ ll->loc_prev, ll->loc_next, node);
+ CxHashKey *key = cx_kv_list_loc_key(kv_list, node + ll->loc_data);
+ if (key->hash != 0) {
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
+ }
+ list->collection.size--;
+ cxFree(list->collection.allocator, node);
+ }
+ return index;
+}
+
+static void cx_kvl_sort(struct cx_list_s *list) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ kv_list->list_methods->sort(list);
+}
+
+static void cx_kvl_reverse(struct cx_list_s *list) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ kv_list->list_methods->reverse(list);
+}
+
+static void cx_kvl_list_iter_next(void *it) {
+ struct cx_iterator_s *iter = it;
+ if (iter->base.remove) {
+ // remove the assigned key from the map before calling the actual function
+ cx_kv_list *kv_list = iter->src_handle;
+ cx_kv_list_update_destructors(kv_list);
+ char *node = iter->elem_handle;
+ CxHashKey *key = cx_kv_list_loc_key(kv_list, node + kv_list->list.loc_data);
+ if (key->hash != 0) {
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
+ }
+ }
+ // note that we do not clear the remove flag, because the next_impl will do that
+ iter->base.next_impl(it);
+}
+
+static struct cx_iterator_s cx_kvl_iterator(
+ const struct cx_list_s *list,
+ size_t index,
+ bool backward
+) {
+ const cx_kv_list *kv_list = (const cx_kv_list*)list;
+ struct cx_iterator_s iter = kv_list->list_methods->iterator(list, index, backward);
+ iter.base.next_impl = iter.base.next;
+ iter.base.next = cx_kvl_list_iter_next;
+ return iter;
+}
+
+static void cx_kvl_map_deallocate(struct cx_map_s *map) {
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ kv_list->map_methods->deallocate(map);
+ kv_list->list_methods->deallocate(&kv_list->list.base);
+}
+
+static void cx_kvl_map_clear(struct cx_map_s *map) {
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ cx_kv_list_update_destructors(kv_list);
+ kv_list->list_methods->clear(&kv_list->list.base);
+ kv_list->map_methods->clear(map);
+}
+
+static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ // if the hash has not yet been computed, do it now
+ if (key.hash == 0) {
+ cx_hash_murmur(&key);
+ }
+
+ // reserve memory in the map first
+ void **map_data = kv_list->map_methods->put(map, key, NULL);
+ if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
+
+ // insert the data into the list (which most likely destroys the sorted property)
+ kv_list->list.base.collection.sorted = false;
+ void *node_data = kv_list->list_methods->insert_element(
+ &kv_list->list.base, kv_list->list.base.collection.size,
+ kv_list->list.base.collection.store_pointer ? &value : value);
+ if (node_data == NULL) { // LCOV_EXCL_START
+ // non-destructively remove the key again
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+ return NULL;
+ } // LCOV_EXCL_STOP
+
+ // write the node pointer to the map entry
+ *map_data = node_data;
+
+ // copy the key to the node data
+ CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
+ *key_ptr = key;
+
+ // we must return node_data here and not map_data,
+ // because the node_data is the actual element of this collection
+ return node_data;
+}
+
+void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ void *node_data = kv_list->map_methods->get(map, key);
+ if (node_data == NULL) return NULL; // LCOV_EXCL_LINE
+ // return the node data
+ return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data;
+}
+
+int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+
+ void *node_data;
+ if (kv_list->map_methods->remove(map, key, &node_data)) {
+ return 1;
+ }
+ // we cannot just call a list method (because we don't have the index)
+ // and tbh. we also don't want to (because it's not performant when we
+ // can have the node ptr directly instead)
+ // therefore, we re-implement the logic ourselves
+
+ // check if the outside caller want's us to return or to destroy the element
+ if (targetbuf == NULL) {
+ // patch the destructors and invoke them through the wrapper
+ cx_kv_list_update_destructors(kv_list);
+ cx_invoke_advanced_destructor(&kv_list->list.base, node_data);
+ } else {
+ // copy the element to the target buffer
+ memcpy(targetbuf, node_data, kv_list->list.base.collection.elem_size);
+ }
+
+ // calculate the address of the node
+ void *node_ptr = (char*)node_data - kv_list->list.loc_data;
+
+ // unlink the node
+ cx_linked_list_remove(
+ &kv_list->list.begin,
+ &kv_list->list.end,
+ kv_list->list.loc_prev,
+ kv_list->list.loc_next,
+ node_ptr
+ );
+
+ // decrement the list's size
+ kv_list->list.base.collection.size--;
+
+ // deallocate the node
+ cxFree(kv_list->list.base.collection.allocator, node_ptr);
+
+ return 0;
+}
+
+static void *cx_kvl_iter_current_entry(const void *it) {
+ const CxMapIterator *iter = it;
+ return (void*)&iter->entry;
+}
+
+static void *cx_kvl_iter_current_key(const void *it) {
+ const CxMapEntry *entry = cx_kvl_iter_current_entry(it);
+ return (void*)entry->key;
+}
+
+static void *cx_kvl_iter_current_value(const void *it) {
+ const CxMapEntry *entry = cx_kvl_iter_current_entry(it);
+ return entry->value;
+}
+
+static void cx_kvl_iter_next(void *it) {
+ CxMapIterator *iter = it;
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map)->list;
+
+ // find the next list entry that has a key assigned
+ CxHashKey *key = NULL;
+ char *next = iter->elem;
+ while (true) {
+ next = *(char**)(next + kv_list->list.loc_next);
+ if (next == NULL) break;
+ key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data);
+ if (key->hash != 0) break;
+ }
+
+ // remove previous element if requested
+ if (iter->base.remove) {
+ iter->base.remove = false;
+ cx_kv_list_update_destructors(kv_list);
+ char *elem = iter->elem;
+ char *elem_data = elem + kv_list->list.loc_data;
+ CxHashKey *elem_key = cx_kv_list_loc_key(kv_list, elem_data);
+ // key is guaranteed to exist because iterator only iterates over elems with a key
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, *elem_key, NULL);
+ cx_invoke_advanced_destructor(&kv_list->list.base, elem_data);
+ cx_linked_list_remove(
+ &kv_list->list.begin,
+ &kv_list->list.end,
+ kv_list->list.loc_prev,
+ kv_list->list.loc_next,
+ elem
+ );
+ cxFree(kv_list->list.base.collection.allocator, elem);
+ kv_list->list.base.collection.size--;
+ iter->index--;
+ iter->elem_count--;
+ }
+
+ // advance to the next element, if any
+ if (next == NULL) {
+ iter->index = kv_list->list.base.collection.size;
+ iter->elem = NULL;
+ iter->entry = (CxMapEntry){NULL, NULL};
+ return;
+ }
+ iter->index++;
+ iter->elem = next;
+ iter->entry.key = key;
+ if (kv_list->list.base.collection.store_pointer) {
+ iter->entry.value = *(void**)(next + kv_list->list.loc_data);
+ } else {
+ iter->entry.value = (void*)(next + kv_list->list.loc_data);
+ }
+}
+
+static bool cx_kvl_iter_valid(const void *it) {
+ const CxMapIterator *iter = it;
+ return iter->elem != NULL;
+}
+
+CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
+ CxMapIterator iter = {0};
+
+ iter.type = type;
+ iter.map = (CxMap*)map;
+ // although we iterate over the list, we only report that many elements that have a key in the map
+ iter.elem_count = map->collection.size;
+
+ switch (type) {
+ case CX_MAP_ITERATOR_PAIRS:
+ iter.elem_size = sizeof(CxMapEntry);
+ iter.base.current = cx_kvl_iter_current_entry;
+ break;
+ case CX_MAP_ITERATOR_KEYS:
+ iter.elem_size = sizeof(CxHashKey);
+ iter.base.current = cx_kvl_iter_current_key;
+ break;
+ case CX_MAP_ITERATOR_VALUES:
+ iter.elem_size = map->collection.elem_size;
+ iter.base.current = cx_kvl_iter_current_value;
+ break;
+ default:
+ assert(false); // LCOV_EXCL_LINE
+ }
+
+ iter.base.allow_remove = true;
+ iter.base.next = cx_kvl_iter_next;
+ iter.base.valid = cx_kvl_iter_valid;
+
+ // find the first list entry that has a key assigned
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ CxHashKey *key = NULL;
+ char *next = kv_list->list.begin;
+ while (next != NULL) {
+ key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data);
+ if (key->hash != 0) break;
+ next = *(char**)(next + kv_list->list.loc_next);
+ }
+ if (next == NULL) {
+ iter.elem = NULL;
+ iter.entry = (CxMapEntry){NULL, NULL};
+ } else {
+ iter.elem = next;
+ iter.entry.key = key;
+ if (kv_list->list.base.collection.store_pointer) {
+ iter.entry.value = *(void**)(next + kv_list->list.loc_data);
+ } else {
+ iter.entry.value = (void*)(next + kv_list->list.loc_data);
+ }
+ }
+
+ return iter;
+}
+
+static cx_list_class cx_kv_list_class = {
+ cx_kvl_deallocate,
+ cx_kvl_insert_element,
+ cx_kvl_insert_array,
+ cx_kvl_insert_sorted,
+ cx_kvl_insert_unique,
+ cx_kvl_insert_iter,
+ cx_kvl_remove,
+ cx_kvl_clear,
+ cx_kvl_swap,
+ cx_kvl_at,
+ cx_kvl_find_remove,
+ cx_kvl_sort,
+ NULL,
+ cx_kvl_reverse,
+ cx_kvl_iterator,
+};
+
+static cx_map_class cx_kv_map_class = {
+ cx_kvl_map_deallocate,
+ cx_kvl_map_clear,
+ cx_kvl_map_put,
+ cx_kvl_map_get,
+ cx_kvl_map_remove,
+ cx_kvl_map_iterator,
+};
+
+CxList *cxKvListCreate(
+ const CxAllocator *allocator,
+ cx_compare_func comparator,
+ size_t elem_size
+) {
+ if (allocator == NULL) {
+ allocator = cxDefaultAllocator;
+ }
+
+ // create a normal linked list and a normal hash map, first
+ CxList *list = cxLinkedListCreate(allocator, comparator, elem_size);
+ if (list == NULL) return NULL; // LCOV_EXCL_LINE
+ cx_linked_list *ll = (cx_linked_list*)list;
+ ll->extra_data_len = sizeof(CxHashKey);
+ CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
+ if (map == NULL) { // LCOV_EXCL_START
+ cxListFree(list);
+ return NULL;
+ } // LCOV_EXCL_STOP
+
+ // patch the kv-list class with the compare function of the linked list
+ // this allows cxListCompare() to optimize comparisons between linked lists and kv-list
+ cx_kv_list_class.compare = list->cl->compare;
+
+ // reallocate the map to add memory for the list back-reference
+ struct cx_kv_list_map_s *kv_map = cxRealloc(allocator, map, sizeof(struct cx_kv_list_map_s));
+
+ // reallocate the list to add memory for storing the metadata
+ cx_kv_list *kv_list = cxRealloc(allocator, list, sizeof(struct cx_kv_list_s));
+
+ // if any of the reallocations failed, we bail out
+ if (kv_map != NULL && kv_list != NULL) {
+ map = (CxMap*) kv_map;
+ list = (CxList*) kv_list;
+ } else { // LCOV_EXCL_START
+ cxListFree(list);
+ cxMapFree(map);
+ return NULL;
+ } // LCOV_EXCL_STOP
+
+ // zero the custom destructor information
+ memset((char*)kv_list + offsetof(cx_kv_list, list_destr), 0, sizeof(void*)*6);
+
+ // combine the list and the map aspect
+ kv_list->map = kv_map;
+ kv_map->list = kv_list;
+
+ // remember the base methods and override them
+ kv_list->map_methods = map->cl;
+ map->cl = &cx_kv_map_class;
+ if (list->climpl == NULL) {
+ kv_list->list_methods = list->cl;
+ list->cl = &cx_kv_list_class;
+ } else {
+ kv_list->list_methods = list->climpl;
+ list->climpl = &cx_kv_list_class;
+ }
+
+ return list;
+}
+
+CxMap *cxKvListCreateAsMap(
+ const CxAllocator *allocator,
+ cx_compare_func comparator,
+ size_t elem_size
+) {
+ CxList *list = cxKvListCreate(allocator, comparator, elem_size);
+ return list == NULL ? NULL : cxKvListAsMap(list);
+}
+
+CxList *cxKvListAsList(CxMap *map) {
+ return &((struct cx_kv_list_map_s*)map)->list->list.base;
+}
+
+CxMap *cxKvListAsMap(CxList *list) {
+ return &((cx_kv_list*)list)->map->map_base.base;
+}
+
+int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ void *node_data = kv_list->list_methods->at(list, index);
+ if (node_data == NULL) {
+ return 1;
+ }
+ // if the hash has not yet been computed, do it now
+ if (key.hash == 0) {
+ cx_hash_murmur(&key);
+ }
+
+ // check if the key is already assigned
+ void *existing = kv_list->map_methods->get(&kv_list->map->map_base.base, key);
+ if (existing == node_data) {
+ return 0; // nothing to do
+ }
+ if (existing != NULL) {
+ // the key is already assigned to another node, we disallow re-assignment
+ return 1;
+ }
+
+ // add the key to the map;
+ if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) {
+ return 1; // LCOV_EXCL_LINE
+ }
+
+ // write the key to the list's node
+ CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
+ *loc_key = key;
+
+ return 0;
+}
+
+int cxKvListRemoveKey(CxList *list, size_t index) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ void *node_data = kv_list->list_methods->at(list, index);
+ if (node_data == NULL) {
+ return 1;
+ }
+ CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
+ if (loc_key->hash == 0) {
+ return 0;
+ }
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, *loc_key, NULL);
+ // also zero the memory in the list node,
+ // but don't free the key data (that was done by the map remove)
+ memset(loc_key, 0, sizeof(CxHashKey));
+ return 0;
+}
+
+const CxHashKey *cxKvListGetKey(CxList *list, size_t index) {
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ void *node_data = kv_list->list_methods->at(list, index);
+ if (node_data == NULL) {
+ return NULL;
+ }
+ CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data);
+ if (key->hash == 0) {
+ return NULL;
+ }
+ return key;
+}
+
+int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value) {
+ // assume we are losing the sorted property
+ list->collection.sorted = false;
+
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+
+ // reserve memory in the map
+ void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL);
+ if (map_data == NULL) return 1; // LCOV_EXCL_LINE
+
+ // insert the node
+ void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index,
+ kv_list->list.base.collection.store_pointer ? &value : value);
+ if (node_data == NULL) { // LCOV_EXCL_START
+ // non-destructively remove the key again
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+ return 1;
+ } // LCOV_EXCL_STOP
+ *map_data = node_data;
+
+ // write the key to the node
+ CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
+ *loc_key = key;
+
+ return 0;
+}
begin, end, loc_prev, loc_next, new_node, cmp_func);
}
-void cx_linked_list_insert_sorted_chain(
+static void *cx_linked_list_insert_sorted_chain_impl(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *insert_begin,
- cx_compare_func cmp_func
+ cx_compare_func cmp_func,
+ bool allow_duplicates
) {
assert(begin != NULL);
assert(loc_next >= 0);
assert(insert_begin != NULL);
- // track currently observed nodes
- void *dest_prev = NULL;
- void *dest = *begin;
- void *src = insert_begin;
-
- // special case: list is empty
- if (dest == NULL) {
- *begin = src;
- if (end != NULL) {
- *end = cx_linked_list_last(src, loc_next);
+ // strategy: build completely new chains from scratch
+ void *source_original = *begin;
+ void *source_argument = insert_begin;
+ void *new_begin = NULL;
+ void *new_end = NULL;
+ void *dup_begin = NULL;
+ void *dup_end = NULL;
+
+ // determine the new start
+ {
+ int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument);
+ if (d <= 0) {
+ // the new chain starts with the original chain
+ new_begin = new_end = source_original;
+ source_original = ll_next(source_original);
+ if (d == 0) {
+ if (allow_duplicates) {
+ // duplicate allowed, append it to the chain
+ cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+ new_end = source_argument;
+ } else {
+ // duplicate is not allowed, start a duplicate chain with the argument
+ dup_begin = dup_end = source_argument;
+ }
+ source_argument = ll_next(source_argument);
+ }
+ } else {
+ // input is smaller, or there is no original chain;
+ // start the new chain with the source argument
+ new_begin = new_end = source_argument;
+ source_argument = ll_next(source_argument);
}
- return;
}
- // search the list for insertion points
- while (dest != NULL && src != NULL) {
- // compare current list node with source node
- // if less or equal, skip
- if (cmp_func(dest, src) <= 0) {
- dest_prev = dest;
- dest = ll_next(dest);
- continue;
- }
-
- // determine chain of elements that can be inserted
- void *end_of_chain = src;
- void *next_in_chain = ll_next(src);
- while (next_in_chain != NULL) {
- // once we become larger than the list elem, break
- if (cmp_func(dest, next_in_chain) <= 0) {
- break;
+ // now successively compare the elements and add them to the correct chains
+ while (source_original != NULL && source_argument != NULL) {
+ int d = cmp_func(source_original, source_argument);
+ if (d <= 0) {
+ // the original is not larger, add it to the chain
+ cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
+ new_end = source_original;
+ source_original = ll_next(source_original);
+ if (d == 0) {
+ if (allow_duplicates) {
+ // duplicate allowed, append it to the chain
+ cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+ new_end = source_argument;
+ } else {
+ // duplicate is not allowed, append it to the duplicate chain
+ if (dup_end == NULL) {
+ dup_begin = dup_end = source_argument;
+ } else {
+ cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
+ dup_end = source_argument;
+ }
+ }
+ source_argument = ll_next(source_argument);
+ }
+ } else {
+ // the original is larger, append the source argument to the chain
+ // check if we must discard the source argument as duplicate
+ if (!allow_duplicates && cmp_func(new_end, source_argument) == 0) {
+ if (dup_end == NULL) {
+ dup_begin = dup_end = source_argument;
+ } else {
+ cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
+ dup_end = source_argument;
+ }
+ } else {
+ // no duplicate or duplicates allowed
+ cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+ new_end = source_argument;
}
- // otherwise, we can insert one more
- end_of_chain = next_in_chain;
- next_in_chain = ll_next(next_in_chain);
+ source_argument = ll_next(source_argument);
}
+ }
- // insert the elements
- if (dest_prev == NULL) {
- // new begin
- *begin = src;
+ if (source_original != NULL) {
+ // something is left from the original chain, append it
+ cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
+ new_end = cx_linked_list_last(source_original, loc_next);
+ } else if (source_argument != NULL) {
+ // something is left from the input chain;
+ // when we allow duplicates, append it
+ if (allow_duplicates) {
+ cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+ new_end = cx_linked_list_last(source_argument, loc_next);
} else {
- cx_linked_list_link(dest_prev, src, loc_prev, loc_next);
+ // otherwise we must check one-by-one
+ while (source_argument != NULL) {
+ if (cmp_func(new_end, source_argument) == 0) {
+ if (dup_end == NULL) {
+ dup_begin = dup_end = source_argument;
+ } else {
+ cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
+ dup_end = source_argument;
+ }
+ } else {
+ cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
+ new_end = source_argument;
+ }
+ source_argument = ll_next(source_argument);
+ }
}
- cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next);
+ }
- // continue with next
- src = next_in_chain;
- dest_prev = dest;
- dest = ll_next(dest);
+ // null the next pointers at the end of the chain
+ ll_next(new_end) = NULL;
+ if (dup_end != NULL) {
+ ll_next(dup_end) = NULL;
}
- // insert remaining items
- if (src != NULL) {
- cx_linked_list_link(dest_prev, src, loc_prev, loc_next);
+ // null the optional prev pointers
+ if (loc_prev >= 0) {
+ ll_prev(new_begin) = NULL;
+ if (dup_begin != NULL) {
+ ll_prev(dup_begin) = NULL;
+ }
}
- // determine new end of list, if requested
+ // output
+ *begin = new_begin;
if (end != NULL) {
- *end = cx_linked_list_last(
- dest != NULL ? dest : dest_prev, loc_next);
+ *end = new_end;
}
+ return dup_begin;
+}
+
+void cx_linked_list_insert_sorted_chain(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *insert_begin,
+ cx_compare_func cmp_func
+) {
+ cx_linked_list_insert_sorted_chain_impl(
+ begin, end, loc_prev, loc_next,
+ insert_begin, cmp_func, true);
+}
+
+int cx_linked_list_insert_unique(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node,
+ cx_compare_func cmp_func
+) {
+ assert(ll_next(new_node) == NULL);
+ return NULL != cx_linked_list_insert_unique_chain(
+ begin, end, loc_prev, loc_next, new_node, cmp_func);
+}
+
+void *cx_linked_list_insert_unique_chain(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *insert_begin,
+ cx_compare_func cmp_func
+) {
+ return cx_linked_list_insert_sorted_chain_impl(
+ begin, end, loc_prev, loc_next,
+ insert_begin, cmp_func, false);
}
size_t cx_linked_list_remove_chain(
return removed;
}
+void cx_linked_list_remove(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *node
+) {
+ cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node, 1);
+}
+
size_t cx_linked_list_size(
const void *node,
ptrdiff_t loc_next
// HIGH LEVEL LINKED LIST IMPLEMENTATION
-typedef struct cx_linked_list_node cx_linked_list_node;
-struct cx_linked_list_node {
- cx_linked_list_node *prev;
- cx_linked_list_node *next;
- char payload[];
-};
-
-#define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev)
-#define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next)
-#define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload)
-
-typedef struct {
- struct cx_list_s base;
- cx_linked_list_node *begin;
- cx_linked_list_node *end;
-} cx_linked_list;
-
-static cx_linked_list_node *cx_ll_node_at(
+static void *cx_ll_node_at(
const cx_linked_list *list,
size_t index
) {
if (index >= list->base.collection.size) {
return NULL;
} else if (index > list->base.collection.size / 2) {
- return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index);
+ return cx_linked_list_at(list->end, list->base.collection.size - 1, list->loc_prev, index);
} else {
- return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index);
+ return cx_linked_list_at(list->begin, 0, list->loc_next, index);
}
}
-static cx_linked_list_node *cx_ll_malloc_node(const struct cx_list_s *list) {
- return cxMalloc(list->collection.allocator,
- sizeof(cx_linked_list_node) + list->collection.elem_size);
+static void *cx_ll_malloc_node(const cx_linked_list *list) {
+ return cxZalloc(list->base.collection.allocator,
+ list->loc_data + list->base.collection.elem_size + list->extra_data_len);
}
static int cx_ll_insert_at(
struct cx_list_s *list,
- cx_linked_list_node *node,
+ void *node,
const void *elem
) {
+ cx_linked_list *ll = (cx_linked_list *) list;
// create the new new_node
- cx_linked_list_node *new_node = cx_ll_malloc_node(list);
+ void *new_node = cx_ll_malloc_node(ll);
// sortir if failed
if (new_node == NULL) return 1;
- // initialize new new_node
- new_node->prev = new_node->next = NULL;
+ // copy the data
if (elem != NULL) {
- memcpy(new_node->payload, elem, list->collection.elem_size);
+ memcpy((char*)new_node + ll->loc_data, elem, list->collection.elem_size);
}
// insert
- cx_linked_list *ll = (cx_linked_list *) list;
cx_linked_list_insert_chain(
- (void **) &ll->begin, (void **) &ll->end,
- CX_LL_LOC_PREV, CX_LL_LOC_NEXT,
+ &ll->begin, &ll->end,
+ ll->loc_prev, ll->loc_next,
node, new_node, new_node
);
if (index > list->collection.size || n == 0) return 0;
// find position efficiently
- cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+ void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
// perform first insert
if (0 != cx_ll_insert_at(list, node, array)) return 1;
if (n == 1) return 1;
// we now know exactly where we are
- node = node == NULL ? ((cx_linked_list *) list)->begin : node->next;
+ cx_linked_list *ll = (cx_linked_list *) list;
+ node = node == NULL ? ((cx_linked_list *) list)->begin : CX_LL_PTR(node, ll->loc_next);
// we can add the remaining nodes and immediately advance to the inserted node
const char *source = array;
for (size_t i = 1; i < n; i++) {
- source += list->collection.elem_size;
+ if (source != NULL) {
+ source += list->collection.elem_size;
+ }
if (0 != cx_ll_insert_at(list, node, source)) return i;
- node = node->next;
+ node = CX_LL_PTR(node, ll->loc_next);
}
return n;
}
if (index > list->collection.size) return NULL;
// find position efficiently
- cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+ void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
// perform first insert
if (cx_ll_insert_at(list, node, element)) return NULL;
// return a pointer to the data of the inserted node
+ cx_linked_list *ll = (cx_linked_list *) list;
if (node == NULL) {
- return ((cx_linked_list *) list)->begin->payload;
+ return (char*)(ll->begin) + ll->loc_data;
} else {
- return node->next->payload;
+ char *next = CX_LL_PTR(node, ll->loc_next);
+ return next + ll->loc_data;
}
}
static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
+static _Thread_local off_t cx_ll_insert_sorted_loc_data;
static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) {
- const cx_linked_list_node *left = l;
- const cx_linked_list_node *right = r;
- return cx_ll_insert_sorted_cmp_func(left->payload, right->payload);
+ const char *left = (const char*)l + cx_ll_insert_sorted_loc_data;
+ const char *right = (const char*)r + cx_ll_insert_sorted_loc_data;
+ return cx_ll_insert_sorted_cmp_func(left, right);
}
-static size_t cx_ll_insert_sorted(
+static size_t cx_ll_insert_sorted_impl(
struct cx_list_s *list,
const void *array,
- size_t n
+ size_t n,
+ bool allow_duplicates
) {
+ cx_linked_list *ll = (cx_linked_list *) list;
+
// special case
if (n == 0) return 0;
// create a new chain of nodes
- cx_linked_list_node *chain = cx_ll_malloc_node(list);
+ void *chain = cx_ll_malloc_node(ll);
if (chain == NULL) return 0;
- memcpy(chain->payload, array, list->collection.elem_size);
- chain->prev = NULL;
- chain->next = NULL;
+ memcpy((char*)chain + ll->loc_data, array, list->collection.elem_size);
// add all elements from the array to that chain
- cx_linked_list_node *prev = chain;
+ void *prev = chain;
const char *src = array;
size_t inserted = 1;
for (; inserted < n; inserted++) {
- cx_linked_list_node *next = cx_ll_malloc_node(list);
+ void *next = cx_ll_malloc_node(ll);
if (next == NULL) break;
src += list->collection.elem_size;
- memcpy(next->payload, src, list->collection.elem_size);
- prev->next = next;
- next->prev = prev;
+ memcpy((char*)next + ll->loc_data, src, list->collection.elem_size);
+ CX_LL_PTR(prev, ll->loc_next) = next;
+ CX_LL_PTR(next, ll->loc_prev) = prev;
prev = next;
}
- prev->next = NULL;
+ CX_LL_PTR(prev, ll->loc_next) = NULL;
// invoke the low level function
- cx_linked_list *ll = (cx_linked_list *) list;
cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
- cx_linked_list_insert_sorted_chain(
- (void **) &ll->begin,
- (void **) &ll->end,
- CX_LL_LOC_PREV,
- CX_LL_LOC_NEXT,
- chain,
- cx_ll_insert_sorted_cmp_helper
- );
-
- // adjust the list metadata
- list->collection.size += inserted;
+ cx_ll_insert_sorted_loc_data = ll->loc_data;
+ if (allow_duplicates) {
+ cx_linked_list_insert_sorted_chain(
+ &ll->begin,
+ &ll->end,
+ ll->loc_prev,
+ ll->loc_next,
+ chain,
+ cx_ll_insert_sorted_cmp_helper
+ );
+ list->collection.size += inserted;
+ } else {
+ void *duplicates = cx_linked_list_insert_unique_chain(
+ &ll->begin,
+ &ll->end,
+ ll->loc_prev,
+ ll->loc_next,
+ chain,
+ cx_ll_insert_sorted_cmp_helper
+ );
+ list->collection.size += inserted;
+ // free the nodes that did not make it into the list
+ while (duplicates != NULL) {
+ void *next = CX_LL_PTR(duplicates, ll->loc_next);
+ cxFree(list->collection.allocator, duplicates);
+ duplicates = next;
+ list->collection.size--;
+ }
+ }
return inserted;
}
+static size_t cx_ll_insert_sorted(
+ struct cx_list_s *list,
+ const void *array,
+ size_t n
+) {
+ return cx_ll_insert_sorted_impl(list, array, n, true);
+}
+
+static size_t cx_ll_insert_unique(
+ struct cx_list_s *list,
+ const void *array,
+ size_t n
+) {
+ return cx_ll_insert_sorted_impl(list, array, n, false);
+}
+
static size_t cx_ll_remove(
struct cx_list_s *list,
size_t index,
void *targetbuf
) {
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_node *node = cx_ll_node_at(ll, index);
+ void *node = cx_ll_node_at(ll, index);
// out-of-bounds check
if (node == NULL) return 0;
size_t removed = cx_linked_list_remove_chain(
(void **) &ll->begin,
(void **) &ll->end,
- CX_LL_LOC_PREV,
- CX_LL_LOC_NEXT,
+ ll->loc_prev,
+ ll->loc_next,
node,
num
);
// copy or destroy the removed chain
if (targetbuf == NULL) {
- cx_linked_list_node *n = node;
+ char *n = node;
for (size_t i = 0; i < removed; i++) {
// element destruction
- cx_invoke_destructor(list, n->payload);
+ cx_invoke_destructor(list, n + ll->loc_data);
// free the node and advance
- void *next = n->next;
+ void *next = CX_LL_PTR(n, ll->loc_next);
cxFree(list->collection.allocator, n);
n = next;
}
} else {
char *dest = targetbuf;
- cx_linked_list_node *n = node;
+ char *n = node;
for (size_t i = 0; i < removed; i++) {
// copy payload
- memcpy(dest, n->payload, list->collection.elem_size);
+ memcpy(dest, n + ll->loc_data, list->collection.elem_size);
// advance target buffer
dest += list->collection.elem_size;
// free the node and advance
- void *next = n->next;
+ void *next = CX_LL_PTR(n, ll->loc_next);
cxFree(list->collection.allocator, n);
n = next;
}
if (list->collection.size == 0) return;
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_node *node = ll->begin;
+ char *node = ll->begin;
while (node != NULL) {
- cx_invoke_destructor(list, node->payload);
- cx_linked_list_node *next = node->next;
+ cx_invoke_destructor(list, node + ll->loc_data);
+ void *next = CX_LL_PTR(node, ll->loc_next);
cxFree(list->collection.allocator, node);
node = next;
}
left = j;
right = i;
}
- cx_linked_list_node *nleft = NULL, *nright = NULL;
+ void *nleft = NULL, *nright = NULL;
if (left < mid && right < mid) {
// case 1: both items left from mid
nleft = cx_ll_node_at(ll, left);
assert(nleft != NULL);
nright = nleft;
for (size_t c = left; c < right; c++) {
- nright = nright->next;
+ nright = CX_LL_PTR(nright, ll->loc_next);
}
} else if (left >= mid && right >= mid) {
// case 2: both items right from mid
assert(nright != NULL);
nleft = nright;
for (size_t c = right; c > left; c--) {
- nleft = nleft->prev;
+ nleft = CX_LL_PTR(nleft, ll->loc_prev);
}
} else {
// case 3: one item left, one item right
if (closest == left) {
nright = nleft;
for (size_t c = left; c < right; c++) {
- nright = nright->next;
+ nright = CX_LL_PTR(nright, ll->loc_next);
}
} else {
nleft = nright;
for (size_t c = right; c > left; c--) {
- nleft = nleft->prev;
+ nleft = CX_LL_PTR(nleft, ll->loc_prev);
}
}
} else {
}
}
- cx_linked_list_node *prev = nleft->prev;
- cx_linked_list_node *next = nright->next;
- cx_linked_list_node *midstart = nleft->next;
- cx_linked_list_node *midend = nright->prev;
+ void *prev = CX_LL_PTR(nleft, ll->loc_prev);
+ void *next = CX_LL_PTR(nright, ll->loc_next);
+ void *midstart = CX_LL_PTR(nleft, ll->loc_next);
+ void *midend = CX_LL_PTR(nright, ll->loc_prev);
if (prev == NULL) {
ll->begin = nright;
} else {
- prev->next = nright;
+ CX_LL_PTR(prev, ll->loc_next) = nright;
}
- nright->prev = prev;
+ CX_LL_PTR(nright, ll->loc_prev) = prev;
if (midstart == nright) {
// special case: both nodes are adjacent
- nright->next = nleft;
- nleft->prev = nright;
+ CX_LL_PTR(nright, ll->loc_next) = nleft;
+ CX_LL_PTR(nleft, ll->loc_prev) = nright;
} else {
// likely case: a chain is between the two nodes
- nright->next = midstart;
- midstart->prev = nright;
- midend->next = nleft;
- nleft->prev = midend;
+ CX_LL_PTR(nright, ll->loc_next) = midstart;
+ CX_LL_PTR(midstart, ll->loc_prev) = nright;
+ CX_LL_PTR(midend, ll->loc_next) = nleft;
+ CX_LL_PTR(nleft, ll->loc_prev) = midend;
}
- nleft->next = next;
+ CX_LL_PTR(nleft, ll->loc_next) = next;
if (next == NULL) {
ll->end = nleft;
} else {
- next->prev = nleft;
+ CX_LL_PTR(next, ll->loc_prev) = nleft;
}
return 0;
size_t index
) {
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_node *node = cx_ll_node_at(ll, index);
- return node == NULL ? NULL : node->payload;
+ char *node = cx_ll_node_at(ll, index);
+ return node == NULL ? NULL : node + ll->loc_data;
}
static size_t cx_ll_find_remove(
if (list->collection.size == 0) return 0;
size_t index;
- cx_linked_list *ll = ((cx_linked_list *) list);
- cx_linked_list_node *node = cx_linked_list_find(
+ cx_linked_list *ll = (cx_linked_list *) list;
+ char *node = cx_linked_list_find(
ll->begin,
- CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+ ll->loc_next, ll->loc_data,
list->collection.cmpfunc, elem,
&index
);
return list->collection.size;
}
if (remove) {
- cx_invoke_destructor(list, node->payload);
- cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
- CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+ cx_invoke_destructor(list, node + ll->loc_data);
+ cx_linked_list_remove(&ll->begin, &ll->end,
+ ll->loc_prev, ll->loc_next, node);
list->collection.size--;
cxFree(list->collection.allocator, node);
}
static void cx_ll_sort(struct cx_list_s *list) {
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end,
- CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+ cx_linked_list_sort(&ll->begin, &ll->end,
+ ll->loc_prev, ll->loc_next, ll->loc_data,
list->collection.cmpfunc);
}
static void cx_ll_reverse(struct cx_list_s *list) {
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT);
+ cx_linked_list_reverse(&ll->begin, &ll->end, ll->loc_prev, ll->loc_next);
}
static int cx_ll_compare(
) {
cx_linked_list *left = (cx_linked_list *) list;
cx_linked_list *right = (cx_linked_list *) other;
+ assert(left->loc_next == right->loc_next);
+ assert(left->loc_data == right->loc_data);
return cx_linked_list_compare(left->begin, right->begin,
- CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+ left->loc_next, left->loc_data,
list->collection.cmpfunc);
}
static void cx_ll_iter_next(void *it) {
struct cx_iterator_s *iter = it;
+ cx_linked_list *ll = iter->src_handle;
if (iter->base.remove) {
iter->base.remove = false;
- struct cx_list_s *list = iter->src_handle.m;
- cx_linked_list *ll = iter->src_handle.m;
- cx_linked_list_node *node = iter->elem_handle;
- iter->elem_handle = node->next;
- cx_invoke_destructor(list, node->payload);
- cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
- CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+ struct cx_list_s *list = iter->src_handle;
+ char *node = iter->elem_handle;
+ iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
+ cx_invoke_destructor(list, node + ll->loc_data);
+ cx_linked_list_remove(&ll->begin, &ll->end,
+ ll->loc_prev, ll->loc_next, node);
list->collection.size--;
+ iter->elem_count--;
cxFree(list->collection.allocator, node);
} else {
iter->index++;
- cx_linked_list_node *node = iter->elem_handle;
- iter->elem_handle = node->next;
+ void *node = iter->elem_handle;
+ iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
}
}
static void cx_ll_iter_prev(void *it) {
struct cx_iterator_s *iter = it;
+ cx_linked_list *ll = iter->src_handle;
if (iter->base.remove) {
iter->base.remove = false;
- struct cx_list_s *list = iter->src_handle.m;
- cx_linked_list *ll = iter->src_handle.m;
- cx_linked_list_node *node = iter->elem_handle;
- iter->elem_handle = node->prev;
+ struct cx_list_s *list = iter->src_handle;
+ char *node = iter->elem_handle;
+ iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
iter->index--;
- cx_invoke_destructor(list, node->payload);
- cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
- CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+ cx_invoke_destructor(list, node + ll->loc_data);
+ cx_linked_list_remove(&ll->begin, &ll->end,
+ ll->loc_prev, ll->loc_next, node);
list->collection.size--;
+ iter->elem_count--;
cxFree(list->collection.allocator, node);
} else {
iter->index--;
- cx_linked_list_node *node = iter->elem_handle;
- iter->elem_handle = node->prev;
+ char *node = iter->elem_handle;
+ iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
}
}
static void *cx_ll_iter_current(const void *it) {
const struct cx_iterator_s *iter = it;
- cx_linked_list_node *node = iter->elem_handle;
- return node->payload;
+ const cx_linked_list *ll = iter->src_handle;
+ char *node = iter->elem_handle;
+ return node + ll->loc_data;
}
static CxIterator cx_ll_iterator(
) {
CxIterator iter;
iter.index = index;
- iter.src_handle.c = list;
+ iter.src_handle = (void*)list;
iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index);
iter.elem_size = list->collection.elem_size;
iter.elem_count = list->collection.size;
iter.base.valid = cx_ll_iter_valid;
iter.base.current = cx_ll_iter_current;
iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
- iter.base.mutating = false;
+ iter.base.allow_remove = true;
iter.base.remove = false;
return iter;
}
const void *elem,
int prepend
) {
- struct cx_list_s *list = iter->src_handle.m;
- cx_linked_list_node *node = iter->elem_handle;
+ struct cx_list_s *list = iter->src_handle;
+ cx_linked_list *ll = iter->src_handle;
+ void *node = iter->elem_handle;
if (node != NULL) {
assert(prepend >= 0 && prepend <= 1);
- cx_linked_list_node *choice[2] = {node, node->prev};
+ void *choice[2] = {node, CX_LL_PTR(node, ll->loc_prev)};
int result = cx_ll_insert_at(list, choice[prepend], elem);
if (result == 0) {
iter->elem_count++;
static void cx_ll_destructor(CxList *list) {
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_node *node = ll->begin;
+ char *node = ll->begin;
while (node) {
- cx_invoke_destructor(list, node->payload);
- void *next = node->next;
+ cx_invoke_destructor(list, node + ll->loc_data);
+ void *next = CX_LL_PTR(node, ll->loc_next);
cxFree(list->collection.allocator, node);
node = next;
}
cx_ll_insert_element,
cx_ll_insert_array,
cx_ll_insert_sorted,
+ cx_ll_insert_unique,
cx_ll_insert_iter,
cx_ll_remove,
cx_ll_clear,
cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
if (list == NULL) return NULL;
+ list->extra_data_len = 0;
+ list->loc_prev = 0;
+ list->loc_next = sizeof(void*);
+ list->loc_data = sizeof(void*)*2;
cx_list_init((CxList*)list, &cx_linked_list_class,
allocator, comparator, elem_size);
#include "cx/list.h"
#include <string.h>
+#include <assert.h>
// <editor-fold desc="Store Pointers Functionality">
const void *l,
const void *r
) {
+ // l and r are guaranteed to be non-NULL pointing to the list's memory
void *const *lptr = l;
void *const *rptr = r;
- const void *left = lptr == NULL ? NULL : *lptr;
- const void *right = rptr == NULL ? NULL : *rptr;
+ const void *left = *lptr;
+ const void *right = *rptr;
+ if (left == NULL) {
+ // NULL is smaller than any value except NULL
+ return right == NULL ? 0 : -1;
+ } else if (right == NULL) {
+ // any value is larger than NULL
+ return 1;
+ }
return cx_pl_cmpfunc_impl(left, right);
}
return result;
}
+static size_t cx_pl_insert_unique(
+ struct cx_list_s *list,
+ const void *array,
+ size_t n
+) {
+ cx_pl_hack_cmpfunc(list);
+ size_t result = list->climpl->insert_unique(list, array, n);
+ cx_pl_unhack_cmpfunc(list);
+ return result;
+}
+
static int cx_pl_insert_iter(
struct cx_iterator_s *iter,
const void *elem,
int prepend
) {
- struct cx_list_s *list = iter->src_handle.m;
+ struct cx_list_s *list = iter->src_handle;
return list->climpl->insert_iter(iter, &elem, prepend);
}
cx_pl_insert_element,
cx_pl_insert_array,
cx_pl_insert_sorted,
+ cx_pl_insert_unique,
cx_pl_insert_iter,
cx_pl_remove,
cx_pl_clear,
cx_attr_unused bool backwards
) {
CxIterator iter = {0};
- iter.src_handle.c = list;
+ iter.src_handle = (void*) list;
iter.index = index;
iter.base.valid = cx_emptyl_iter_valid;
return iter;
NULL,
NULL,
NULL,
+ NULL,
cx_emptyl_noop,
NULL,
cx_emptyl_at,
const void *data,
size_t n
) {
- size_t elem_size = list->collection.elem_size;
const char *src = data;
size_t i = 0;
for (; i < n; i++) {
if (NULL == invoke_list_func(
- insert_element, list, index + i,
- src + (i * elem_size))) return i;
+ insert_element, list, index + i, src)
+ ) {
+ return i; // LCOV_EXCL_LINE
+ }
+ if (src != NULL) {
+ src += list->collection.elem_size;
+ }
}
return i;
}
-size_t cx_list_default_insert_sorted(
+static size_t cx_list_default_insert_sorted_impl(
struct cx_list_s *list,
const void *sorted_data,
- size_t n
+ size_t n,
+ bool allow_duplicates
) {
// corner case
if (n == 0) return 0;
const char *src = sorted_data;
// track indices and number of inserted items
- size_t di = 0, si = 0, inserted = 0;
+ size_t di = 0, si = 0, processed = 0;
// search the list for insertion points
- for (; di < list->collection.size; di++) {
+ while (di < list->collection.size) {
const void *list_elm = invoke_list_func(at, list, di);
- // compare current list element with first source element
- // if less or equal, skip
- if (cmp(list_elm, src) <= 0) {
- continue;
+ // compare the current list element with the first source element
+ // if less, skip the list elements
+ // if equal, skip the list elements and optionally the source elements
+ {
+ int d = cmp(list_elm, src);
+ if (d <= 0) {
+ if (!allow_duplicates && d == 0) {
+ src += elem_size;
+ si++;
+ processed++; // we also count duplicates for the return value
+ while (si < n && cmp(list_elm, src) == 0) {
+ src += elem_size;
+ si++;
+ processed++;
+ }
+ if (processed == n) {
+ return processed;
+ }
+ }
+ di++;
+ continue;
+ }
}
- // determine number of consecutive elements that can be inserted
- size_t ins = 1;
+ // determine the number of consecutive elements that can be inserted
+ size_t ins = 1, skip = 0;
const char *next = src;
while (++si < n) {
+ if (!allow_duplicates) {
+ // skip duplicates within the source
+ if (cmp(next, next + elem_size) == 0) {
+ next += elem_size;
+ skip++;
+ continue;
+ } else {
+ if (skip > 0) {
+ // if we had to skip something, we must wait for the next run
+ next += elem_size;
+ break;
+ }
+ }
+ }
next += elem_size;
// once we become larger than the list elem, break
if (cmp(list_elm, next) <= 0) {
// insert the elements at location si
if (ins == 1) {
- if (NULL == invoke_list_func(
- insert_element, list, di, src)) return inserted;
+ if (NULL == invoke_list_func(insert_element, list, di, src)) {
+ return processed; // LCOV_EXCL_LINE
+ }
} else {
size_t r = invoke_list_func(insert_array, list, di, src, ins);
- if (r < ins) return inserted + r;
+ if (r < ins) {
+ return processed + r; // LCOV_EXCL_LINE
+ }
}
- inserted += ins;
+ processed += ins + skip;
di += ins;
// everything inserted?
- if (inserted == n) return inserted;
+ if (processed == n) {
+ return processed;
+ }
src = next;
}
// insert remaining items
if (si < n) {
- inserted += invoke_list_func(insert_array, list, di, src, n - si);
+ if (allow_duplicates) {
+ processed += invoke_list_func(insert_array, list, di, src, n - si);
+ } else {
+ const void *last = di == 0 ? NULL : invoke_list_func(at, list, di - 1);
+ for (; si < n; si++) {
+ // skip duplicates within the source
+ if (last == NULL || cmp(last, src) != 0) {
+ if (NULL == invoke_list_func(insert_element, list, di, src)) {
+ return processed; // LCOV_EXCL_LINE
+ }
+ last = src;
+ di++;
+ }
+ processed++;
+ src += elem_size;
+ }
+ }
}
- return inserted;
+ return processed;
+}
+
+size_t cx_list_default_insert_sorted(
+ struct cx_list_s *list,
+ const void *sorted_data,
+ size_t n
+) {
+ return cx_list_default_insert_sorted_impl(list, sorted_data, n, true);
+}
+
+size_t cx_list_default_insert_unique(
+ struct cx_list_s *list,
+ const void *sorted_data,
+ size_t n
+) {
+ return cx_list_default_insert_sorted_impl(list, sorted_data, n, false);
}
void cx_list_default_sort(struct cx_list_s *list) {
size_t elem_size = list->collection.elem_size;
size_t list_size = list->collection.size;
void *tmp = cxMallocDefault(elem_size * list_size);
- if (tmp == NULL) abort();
+ if (tmp == NULL) abort(); // LCOV_EXCL_LINE
// copy elements from source array
char *loc = tmp;
size_t elem_size = list->collection.elem_size;
void *tmp = cxMallocDefault(elem_size);
- if (tmp == NULL) return 1;
+ if (tmp == NULL) return 1; // LCOV_EXCL_LINE
void *ip = invoke_list_func(at, list, i);
void *jp = invoke_list_func(at, list, j);
}
}
-CxIterator cxListMutIteratorAt(
- CxList *list,
- size_t index
-) {
- CxIterator it = list->cl->iterator(list, index, false);
- it.base.mutating = true;
- return it;
+size_t cxListSize(const CxList *list) {
+ return list->collection.size;
}
-CxIterator cxListMutBackwardsIteratorAt(
- CxList *list,
- size_t index
-) {
- CxIterator it = list->cl->iterator(list, index, true);
- it.base.mutating = true;
- return it;
+int cxListAdd(CxList *list, const void *elem) {
+ list->collection.sorted = false;
+ return list->cl->insert_element(list, list->collection.size, elem) == NULL;
}
-void cxListFree(CxList *list) {
- if (list == NULL) return;
- list->cl->deallocate(list);
+size_t cxListAddArray(CxList *list, const void *array, size_t n) {
+ list->collection.sorted = false;
+ return list->cl->insert_array(list, list->collection.size, array, n);
}
-int cxListSet(
- CxList *list,
- size_t index,
- const void *elem
-) {
+int cxListInsert(CxList *list, size_t index, const void *elem) {
+ list->collection.sorted = false;
+ return list->cl->insert_element(list, index, elem) == NULL;
+}
+
+void *cxListEmplaceAt(CxList *list, size_t index) {
+ list->collection.sorted = false;
+ return list->cl->insert_element(list, index, NULL);
+}
+
+void *cxListEmplace(CxList *list) {
+ list->collection.sorted = false;
+ return list->cl->insert_element(list, list->collection.size, NULL);
+}
+
+static bool cx_list_emplace_iterator_valid(const void *it) {
+ const CxIterator *iter = it;
+ return iter->index < iter->elem_count;
+}
+
+CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n) {
+ list->collection.sorted = false;
+ size_t c = list->cl->insert_array(list, index, NULL, n);
+ CxIterator iter = list->cl->iterator(list, index, false);
+ // tweak the fields of this iterator
+ iter.elem_count = c;
+ iter.index = 0;
+ // replace the valid function to abort iteration when c is reached
+ iter.base.valid = cx_list_emplace_iterator_valid;
+ // if we are storing pointers, we want to return the pure pointers.
+ // therefore, we must unwrap the "current" method
+ if (list->collection.store_pointer) {
+ iter.base.current = iter.base.current_impl;
+ }
+ return iter;
+}
+
+CxIterator cxListEmplaceArray(CxList *list, size_t n) {
+ return cxListEmplaceArrayAt(list, list->collection.size, n);
+}
+
+int cxListInsertSorted(CxList *list, const void *elem) {
+ assert(cxCollectionSorted(list));
+ list->collection.sorted = true;
+ const void *data = list->collection.store_pointer ? &elem : elem;
+ return list->cl->insert_sorted(list, data, 1) == 0;
+}
+
+int cxListInsertUnique(CxList *list, const void *elem) {
+ if (cxCollectionSorted(list)) {
+ list->collection.sorted = true;
+ const void *data = list->collection.store_pointer ? &elem : elem;
+ return list->cl->insert_unique(list, data, 1) == 0;
+ } else {
+ if (cxListContains(list, elem)) {
+ return 0;
+ } else {
+ return cxListAdd(list, elem);
+ }
+ }
+}
+
+size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n) {
+ list->collection.sorted = false;
+ return list->cl->insert_array(list, index, array, n);
+}
+
+size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n) {
+ assert(cxCollectionSorted(list));
+ list->collection.sorted = true;
+ return list->cl->insert_sorted(list, array, n);
+}
+
+size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n) {
+ if (cxCollectionSorted(list)) {
+ list->collection.sorted = true;
+ return list->cl->insert_unique(list, array, n);
+ } else {
+ const char *source = array;
+ for (size_t i = 0 ; i < n; i++) {
+ // note: this also checks elements added in a previous iteration
+ const void *data = list->collection.store_pointer ?
+ *((const void**)source) : source;
+ if (!cxListContains(list, data)) {
+ if (cxListAdd(list, data)) {
+ return i; // LCOV_EXCL_LINE
+ }
+ }
+ source += list->collection.elem_size;
+ }
+ return n;
+ }
+}
+
+int cxListInsertAfter(CxIterator *iter, const void *elem) {
+ CxList* list = (CxList*)iter->src_handle;
+ list->collection.sorted = false;
+ return list->cl->insert_iter(iter, elem, 0);
+}
+
+int cxListInsertBefore(CxIterator *iter, const void *elem) {
+ CxList* list = (CxList*)iter->src_handle;
+ list->collection.sorted = false;
+ return list->cl->insert_iter(iter, elem, 1);
+}
+
+int cxListRemove(CxList *list, size_t index) {
+ return list->cl->remove(list, index, 1, NULL) == 0;
+}
+
+int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf) {
+ return list->cl->remove(list, index, 1, targetbuf) == 0;
+}
+
+int cxListRemoveAndGetFirst(CxList *list, void *targetbuf) {
+ return list->cl->remove(list, 0, 1, targetbuf) == 0;
+}
+
+int cxListRemoveAndGetLast(CxList *list, void *targetbuf) {
+ // note: index may wrap - member function will catch that
+ return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0;
+}
+
+size_t cxListRemoveArray(CxList *list, size_t index, size_t num) {
+ return list->cl->remove(list, index, num, NULL);
+}
+
+size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf) {
+ return list->cl->remove(list, index, num, targetbuf);
+}
+
+void cxListClear(CxList *list) {
+ list->cl->clear(list);
+ list->collection.sorted = true; // empty lists are always sorted
+}
+
+int cxListSwap(CxList *list, size_t i, size_t j) {
+ list->collection.sorted = false;
+ return list->cl->swap(list, i, j);
+}
+
+void *cxListAt(const CxList *list, size_t index) {
+ return list->cl->at(list, index);
+}
+
+void *cxListFirst(const CxList *list) {
+ return list->cl->at(list, 0);
+}
+
+void *cxListLast(const CxList *list) {
+ return list->cl->at(list, list->collection.size - 1);
+}
+
+int cxListSet(CxList *list, size_t index, const void *elem) {
if (index >= list->collection.size) {
return 1;
}
return 0;
}
+
+CxIterator cxListIteratorAt(const CxList *list, size_t index) {
+ if (list == NULL) list = cxEmptyList;
+ return list->cl->iterator(list, index, false);
+}
+
+CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index) {
+ if (list == NULL) list = cxEmptyList;
+ return list->cl->iterator(list, index, true);
+}
+
+CxIterator cxListIterator(const CxList *list) {
+ if (list == NULL) list = cxEmptyList;
+ return list->cl->iterator(list, 0, false);
+}
+
+CxIterator cxListBackwardsIterator(const CxList *list) {
+ if (list == NULL) list = cxEmptyList;
+ return list->cl->iterator(list, list->collection.size - 1, true);
+}
+
+size_t cxListFind(const CxList *list, const void *elem) {
+ return list->cl->find_remove((CxList*)list, elem, false);
+}
+
+bool cxListContains(const CxList* list, const void* elem) {
+ return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size;
+}
+
+bool cxListIndexValid(const CxList *list, size_t index) {
+ return index < list->collection.size;
+}
+
+size_t cxListFindRemove(CxList *list, const void *elem) {
+ return list->cl->find_remove(list, elem, true);
+}
+
+void cxListSort(CxList *list) {
+ if (list->collection.sorted) return;
+ list->cl->sort(list);
+ list->collection.sorted = true;
+}
+
+void cxListReverse(CxList *list) {
+ // still sorted, but not according to the cmp_func
+ list->collection.sorted = false;
+ list->cl->reverse(list);
+}
+
+void cxListFree(CxList *list) {
+ if (list == NULL) return;
+ list->cl->deallocate(list);
+}
cx_attr_unused enum cx_map_iterator_type type\r
) {\r
CxMapIterator iter = {0};\r
- iter.map.c = map;\r
+ iter.map = (CxMap*) map;\r
iter.base.valid = cx_empty_map_iter_valid;\r
return iter;\r
}\r
\r
// </editor-fold>\r
\r
-CxMapIterator cxMapMutIteratorValues(CxMap *map) {\r
- CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);\r
- it.base.mutating = true;\r
- return it;\r
+void cxMapClear(CxMap *map) {\r
+ map->cl->clear(map);\r
}\r
\r
-CxMapIterator cxMapMutIteratorKeys(CxMap *map) {\r
- CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);\r
- it.base.mutating = true;\r
- return it;\r
+size_t cxMapSize(const CxMap *map) {\r
+ return map->collection.size;\r
}\r
\r
-CxMapIterator cxMapMutIterator(CxMap *map) {\r
- CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);\r
- it.base.mutating = true;\r
- return it;\r
+CxMapIterator cxMapIteratorValues(const CxMap *map) {\r
+ if (map == NULL) map = cxEmptyMap;\r
+ return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);\r
+}\r
+\r
+CxMapIterator cxMapIteratorKeys(const CxMap *map) {\r
+ if (map == NULL) map = cxEmptyMap;\r
+ return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);\r
+}\r
+\r
+CxMapIterator cxMapIterator(const CxMap *map) {\r
+ if (map == NULL) map = cxEmptyMap;\r
+ return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);\r
+}\r
+\r
+int cx_map_put(CxMap *map, CxHashKey key, void *value) {\r
+ return map->cl->put(map, key, value) == NULL;\r
+}\r
+\r
+void *cx_map_emplace(CxMap *map, CxHashKey key) {\r
+ return map->cl->put(map, key, NULL);\r
+}\r
+\r
+void *cx_map_get(const CxMap *map, CxHashKey key) {\r
+ return map->cl->get(map, key);\r
+}\r
+\r
+int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {\r
+ return map->cl->remove(map, key, targetbuf);\r
}\r
\r
void cxMapFree(CxMap *map) {\r
new_source_allocator->data = source;
// transfer all the data
- memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size);
- dest->size += source->size;
+ if (source->size > 0) {
+ memcpy(&dest->data[dest->size], source->data,
+ sizeof(void*)*source->size);
+ dest->size += source->size;
+ }
// transfer all registered memory
- memcpy(&dest->registered[dest->registered_size], source->registered,
- sizeof(struct cx_mempool_foreign_memory_s) * source->size);
- dest->registered_size += source->registered_size;
+ if (source->registered_size > 0) {
+ memcpy(&dest->registered[dest->registered_size], source->registered,
+ sizeof(struct cx_mempool_foreign_memory_s)
+ * source->registered_size);
+ dest->registered_size += source->registered_size;
+ }
// register the old allocator with the new pool
// we have to remove const-ness for this, but that's okay here
cxBufferDestroy(&prop->buffer);
}
+void cxPropertiesReset(CxProperties *prop) {
+ CxPropertiesConfig config = prop->config;
+ cxPropertiesDestroy(prop);
+ cxPropertiesInit(prop, config);
+}
+
int cxPropertiesFilln(
CxProperties *prop,
const char *buf,
CxMap *map = sink->sink;
CxAllocator *alloc = sink->data;
cxmutstr v = cx_strdup_a(alloc, value);
- int r = cx_map_put_cxstr(map, key, v.ptr);
+ int r = cxMapPut(map, key, v.ptr);
if (r != 0) cx_strfree_a(alloc, &v);
return r;
}
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
+#ifdef MEMRCHR_NEED_GNU
+#define _GNU_SOURCE
+#endif
+
#include "cx/string.h"
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <float.h>
+#include <ctype.h>
#ifdef _WIN32
#define cx_strcasecmp_impl _strnicmp
}
cxstring cx_strrchr(
- cxstring string,
- int chr
+ cxstring string,
+ int chr
) {
+#ifdef WITH_MEMRCHR
+ char *ret = memrchr(string.ptr, 0xFF & chr, string.length);
+ if (ret == NULL) return (cxstring) {NULL, 0};
+ return (cxstring) {ret, string.length - (ret - string.ptr)};
+#else
chr = 0xFF & chr;
size_t i = string.length;
while (i > 0) {
i--;
- // TODO: improve by comparing multiple bytes at once
if (string.ptr[i] == chr) {
return cx_strsubs(string, i);
}
}
return (cxstring) {NULL, 0};
+#endif
}
cxmutstr cx_strrchr_m(
delim, limit, (cxstring **) output);
}
-int cx_strcmp(
+int cx_strcmp_(
cxstring s1,
cxstring s2
) {
}
}
-int cx_strcasecmp(
+int cx_strcasecmp_(
cxstring s1,
cxstring s2
) {
return result;
}
-static bool str_isspace(char c) {
- // TODO: remove once UCX has public API for this
- return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
-}
-
cxstring cx_strtrim(cxstring string) {
cxstring result = string;
- // TODO: optimize by comparing multiple bytes at once
- while (result.length > 0 && str_isspace(*result.ptr)) {
+ while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) {
result.ptr++;
result.length--;
}
- while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) {
+ while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) {
result.length--;
}
return result;
return (cxmutstr) {(char *) result.ptr, result.length};
}
-bool cx_strprefix(
+bool cx_strprefix_(
cxstring string,
cxstring prefix
) {
return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
}
-bool cx_strsuffix(
+bool cx_strsuffix_(
cxstring string,
cxstring suffix
) {
suffix.ptr, suffix.length) == 0;
}
-bool cx_strcaseprefix(
+bool cx_strcaseprefix_(
cxstring string,
cxstring prefix
) {
#endif
}
-bool cx_strcasesuffix(
+bool cx_strcasesuffix_(
cxstring string,
cxstring suffix
) {
return 0;
}
-static bool str_isdigit(char c) {
- // TODO: remove once UCX has public API for this
- return c >= '0' && c <= '9';
-}
-
int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) {
// TODO: overflow check
// TODO: increase precision
// parse all digits until we find the decsep
size_t pos = 0;
do {
- if (str_isdigit(str.ptr[pos])) {
+ if (isdigit((unsigned char)str.ptr[pos])) {
result = result * 10 + (str.ptr[pos] - '0');
} else if (strchr(groupsep, str.ptr[pos]) == NULL) {
break;
// parse everything until exponent or end
double factor = 1.;
do {
- if (str_isdigit(str.ptr[pos])) {
+ if (isdigit((unsigned char)str.ptr[pos])) {
factor *= 0.1;
result = result + factor * (str.ptr[pos] - '0');
} else if (strchr(groupsep, str.ptr[pos]) == NULL) {
// parse the exponent
unsigned int exp = 0;
do {
- if (str_isdigit(str.ptr[pos])) {
+ if (isdigit((unsigned char)str.ptr[pos])) {
exp = 10 * exp + (str.ptr[pos] - '0');
} else if (strchr(groupsep, str.ptr[pos]) == NULL) {
errno = EINVAL;
iter.skip = false;
// assign base iterator functions
- iter.base.mutating = false;
+ iter.base.allow_remove = false;
iter.base.remove = false;
iter.base.current_impl = NULL;
iter.base.valid = cx_tree_iter_valid;
iter.queue_last = NULL;
// assign base iterator functions
- iter.base.mutating = false;
+ iter.base.allow_remove = false;
iter.base.remove = false;
iter.base.current_impl = NULL;
iter.base.valid = cx_tree_visitor_valid;
}
// otherwise, create iterator and hand over to other function
- CxIterator iter = cxIterator(src, elem_size, num);
+ CxIterator iter = cxIterator(src, elem_size, num, false);
return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc,
cfunc, cdata, failed, root,
loc_parent, loc_children, loc_last_child,
cx_tree_default_find
};
-CxTree *cxTreeCreate(
- const CxAllocator *allocator,
+CxTree *cxTreeCreate(const CxAllocator *allocator,
cx_tree_node_create_func create_func,
cx_tree_search_func search_func,
cx_tree_search_data_func search_data_func,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next
) {
if (allocator == NULL) {
allocator = cxDefaultAllocator;
cxFree(tree->allocator, tree);
}
-CxTree *cxTreeCreateWrapped(
- const CxAllocator *allocator,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-) {
+CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next) {
if (allocator == NULL) {
allocator = cxDefaultAllocator;
}
return tree;
}
-void cxTreeSetParent(
- CxTree *tree,
- void *parent,
- void *child
-) {
+void cxTreeSetParent(CxTree *tree, void *parent, void *child) {
size_t loc_parent = tree->loc_parent;
if (tree_parent(child) == NULL) {
tree->size++;
cx_tree_link(parent, child, cx_tree_node_layout(tree));
}
-void cxTreeAddChildNode(
- CxTree *tree,
- void *parent,
- void *child
-) {
+void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) {
cx_tree_link(parent, child, cx_tree_node_layout(tree));
tree->size++;
}
-int cxTreeAddChild(
- CxTree *tree,
- void *parent,
- const void *data) {
+int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
void *node = tree->node_create(data, tree);
if (node == NULL) return 1;
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
return 0;
}
+int cxTreeInsert(CxTree *tree, const void *data) {
+ return tree->cl->insert_element(tree, data);
+}
+
+size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n) {
+ return tree->cl->insert_many(tree, iter, n);
+}
+
+size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n) {
+ if (n == 0) return 0;
+ if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0;
+ CxIterator iter = cxIterator(data, elem_size, n, false);
+ return cxTreeInsertIter(tree, cxIteratorRef(iter), n);
+}
+
+void *cxTreeFind( CxTree *tree, const void *data) {
+ return tree->cl->find(tree, tree->root, data, 0);
+}
+
+void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth) {
+ return tree->cl->find(tree, subtree_root, data, max_depth);
+}
+
size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) {
CxTreeVisitor visitor = cx_tree_visitor(
subtree_root,
return visitor.depth;
}
+size_t cxTreeSize(CxTree *tree) {
+ return tree->size;
+}
+
size_t cxTreeDepth(CxTree *tree) {
CxTreeVisitor visitor = cx_tree_visitor(
tree->root, tree->loc_children, tree->loc_next
tree->root = NULL;
}
}
+
+void cxTreeIteratorDispose(CxTreeIterator *iter) {
+ cxFreeDefault(iter->stack);
+ iter->stack = NULL;
+}
+
+void cxTreeVisitorDispose(CxTreeVisitor *visitor) {
+ struct cx_tree_visitor_queue_s *q = visitor->queue_next;
+ while (q != NULL) {
+ struct cx_tree_visitor_queue_s *next = q->next;
+ cxFreeDefault(q);
+ q = next;
+ }
+}
+
+CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit) {
+ return cx_tree_iterator(
+ node, visit_on_exit,
+ tree->loc_children, tree->loc_next
+ );
+}
+
+CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) {
+ return cx_tree_visitor(
+ node, tree->loc_children, tree->loc_next
+ );
+}
+
+CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit) {
+ return cxTreeIterateSubtree(tree, tree->root, visit_on_exit);
+}
+
+CxTreeVisitor cxTreeVisit(CxTree *tree) {
+ return cxTreeVisitSubtree(tree, tree->root);
+}
--- /dev/null
+QMAKE_CXX.QT_COMPILER_STDCXX = 201703L
+QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 15
+QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 2
+QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 1
+QMAKE_CXX.COMPILER_MACROS = \
+ QT_COMPILER_STDCXX \
+ QMAKE_GCC_MAJOR_VERSION \
+ QMAKE_GCC_MINOR_VERSION \
+ QMAKE_GCC_PATCH_VERSION
+QMAKE_CXX.INCDIRS = \
+ /usr/include/c++/15 \
+ /usr/include/c++/15/x86_64-redhat-linux \
+ /usr/include/c++/15/backward \
+ /usr/lib/gcc/x86_64-redhat-linux/15/include \
+ /usr/local/include \
+ /usr/include
+QMAKE_CXX.LIBDIRS = \
+ /usr/lib/gcc/x86_64-redhat-linux/15 \
+ /usr/lib64 \
+ /lib64 \
+ /usr/lib \
+ /lib
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+BUILD_ROOT = ../
+include ../config.mk
+
+OBJ_DIR = ../build/
+
+include common/objs.mk
+
+UI_LIB = ../build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(LIB_EXT)
+UI_SHLIB = ../build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(SHLIB_EXT)
+
+include $(TOOLKIT)/objs.mk
+OBJ = $(TOOLKITOBJS) $(COMMONOBJS)
+
+all: $(UI_LIB) $(UI_SHLIB)
+
+include $(TOOLKIT)/Makefile
+
+$(COMMON_OBJPRE)uic_%$(OBJ_EXT): common/%.c
+ $(CC) -o $@ -c -I../ucx/ $(CFLAGS) $(SHLIB_CFLAGS) $(TK_CFLAGS) $<
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "Container.h"
+#import "GridLayout.h"
+
+@interface BoxContainer : GridLayout
+
+@property NSUserInterfaceLayoutOrientation orientation;
+
+- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing;
+
+@end
+
--- /dev/null
+
+
+#import "BoxContainer.h"
+
+@implementation BoxContainer
+
+- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing {
+ self = [super init];
+ _orientation = orientation;
+ self.columnspacing = spacing;
+ self.rowspacing = spacing;
+
+ return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+ if(_orientation == NSUserInterfaceLayoutOrientationVertical) {
+ layout->hexpand = TRUE;
+ layout->hfill = TRUE;
+ } else {
+ layout->vexpand = TRUE;
+ layout->vfill = TRUE;
+ }
+ [super addView:view layout:layout];
+ if(_orientation == NSUserInterfaceLayoutOrientationVertical) {
+ self.container->newline = TRUE;
+ }
+}
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "../ui/toolkit.h"
+#import "../common/context.h"
+
+typedef void(*get_eventdata_func)(id sender, UiVar *var, void **eventdata, int *value);
+
+@interface EventData : NSObject
+@property UiObject *obj;
+@property UiVar *var;
+@property int vartype;
+@property ui_callback callback;
+@property void *userdata;
+@property void *data;
+@property int value;
+@property get_eventdata_func get_eventdata;
+
+- (EventData*)init:(ui_callback)cb userdata:(void*)userdata;
+
+- (void)handleEvent:(id)sender;
+
+- (void)handleEventWithEventData:(id)sender;
+
+@end
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "EventData.h"
+
+#import <objc/runtime.h>
+
+
+@implementation EventData
+
+- (EventData*)init:(ui_callback)cb userdata:(void*)userdata {
+ _callback = cb;
+ _userdata = userdata;
+ return self;
+}
+
+- (void)handleEvent:(id)sender {
+ if(_callback) {
+ UiEvent event;
+ event.obj = self.obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = self.data;
+ event.intval = self.value;
+ event.set = ui_get_setop();
+ self.callback(&event, self.userdata);
+ }
+}
+
+- (void)handleEventWithEventData:(id)sender {
+ UiEvent event;
+ event.obj = self.obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.intval = 0;
+ event.set = ui_get_setop();
+ if(_get_eventdata) {
+ _get_eventdata(sender, _var, &event.eventdata, &event.intval);
+ }
+ if(self.callback) {
+ self.callback(&event, self.userdata);
+ }
+}
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+
+#import "Container.h"
+
+#import <cx/array_list.h>
+
+typedef struct GridElm {
+ NSView *view;
+ NSEdgeInsets margin;
+ int x;
+ int y;
+ int colspan;
+ int rowspan;
+ int preferred_width;
+ int preferred_height;
+ BOOL hexpand;
+ BOOL vexpand;
+ BOOL hfill;
+ BOOL vfill;
+} GridElm;
+
+typedef struct GridDef {
+ int size;
+ int pos;
+ int preferred_size;
+ BOOL expand;
+} GridDef;
+
+@interface GridLayout : NSView<Container>
+
+@property UiContainerX *container;
+
+@property int columnspacing;
+@property int rowspacing;
+@property CxList *children;
+@property NSSize preferredSize;
+
+@property int x;
+@property int y;
+@property int cols;
+@property int rows;
+
+- (GridLayout*)init;
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "GridLayout.h"
+
+
+
+@implementation GridLayout
+
+@synthesize container = _container;
+
+- (GridLayout*)init {
+ self = [super init];
+ _columnspacing = 0;
+ _rowspacing = 0;
+ _children = cxArrayListCreateSimple(sizeof(GridElm), 32);
+ _preferredSize.width = -1;
+ _preferredSize.height = -1;
+
+ return self;
+}
+
+/*
+- (void) layout {
+ [super layout];
+
+ NSRect r1 = _test.frame;
+ NSSize s1 = _test.intrinsicContentSize;
+ NSEdgeInsets e1 = _test.alignmentRectInsets;
+
+}
+ */
+
+- (BOOL)isFlipped {
+ return YES;
+}
+
+- (void) layout {
+ int ncols = _cols+1;
+ int nrows = _rows+1;
+
+ GridDef *cols = calloc(ncols, sizeof(GridDef));
+ GridDef *rows = calloc(nrows, sizeof(GridDef));
+
+ //NSRect viewFrame = self.frame;
+ NSRect viewFrame = self.bounds;
+
+ int colspacing = _columnspacing;
+ int rowspacing = _rowspacing;
+
+ int span_max = 1;
+ for(int r=0;r<2;r++) {
+ CxIterator i = cxListIterator(_children);
+ cx_foreach(GridElm *, elm, i) {
+ int x = elm->x;
+ int y = elm->y;
+ GridDef *col = &cols[x];
+ GridDef *row = &rows[y];
+
+ NSSize size = elm->view.intrinsicContentSize;
+ NSSize size2 = elm->view.fittingSize;
+ if(size.width == NSViewNoIntrinsicMetric) {
+ size.width = size2.width;
+ }
+ if(size.height == NSViewNoIntrinsicMetric) {
+ size.height = size2.height;
+ }
+ if(size.width != NSViewNoIntrinsicMetric) {
+ CGFloat width = size.width + elm->margin.left + elm->margin.right;
+ if(width > cols[elm->x].preferred_size && elm->colspan <= 1 && span_max == 1) {
+ cols[elm->x].preferred_size = width;
+ }
+ elm->preferred_width = width;
+ }
+ if(size.height != NSViewNoIntrinsicMetric) {
+ CGFloat height = size.height + elm->margin.top + elm->margin.bottom;
+ if(height > rows[elm->y].preferred_size && elm->rowspan <= 1 && span_max == 1) {
+ rows[elm->y].preferred_size = height;
+ }
+ elm->preferred_height = height;
+ }
+
+
+ if(elm->rowspan > span_max || elm->colspan > span_max) {
+ continue;
+ }
+
+ int end_col = x+elm->colspan;
+ if(end_col > ncols) {
+ end_col = ncols;
+ }
+ int end_row = y+elm->rowspan;
+ if(end_row > nrows) {
+ end_row = nrows;
+ }
+
+ // are all columns in the span > preferred_width?
+ if(elm->colspan > 1) {
+ int span_width = 0;
+ GridDef *last_col = col;
+ for(int c=x;c<end_col;c++) {
+ span_width += cols[c].size;
+ last_col = &cols[c];
+ }
+ if(span_width < elm->preferred_width) {
+ last_col->size += elm->preferred_width - span_width;
+ }
+ }
+ // are all rows in the span > preferred_height?
+ if(elm->rowspan > 1) {
+ int span_height = 0;
+ GridDef *last_row = row;
+ for(int c=x;c<end_row;c++) {
+ span_height += rows[c].size;
+ last_row = &rows[c];
+ }
+ if(span_height < elm->preferred_height) {
+ last_row->size += elm->preferred_height - span_height;
+ }
+ }
+
+ if(elm->hexpand) {
+ if(elm->colspan > 1) {
+ // check if any column in the span is expanding
+ // if not, make the last column expanding
+ GridDef *last_col = col;
+ for(int c=x;c<end_col;c++) {
+ last_col = &cols[c];
+ if(last_col->expand) {
+ break;
+ }
+ }
+ last_col->expand = TRUE;
+ } else {
+ col->expand = TRUE;
+ }
+ }
+ if(elm->vexpand) {
+ if(elm->rowspan > 1) {
+ // same as colspan
+ GridDef *last_row = row;
+ for(int c=x;c<nrows;c++) {
+ last_row = &rows[c];
+ if(last_row->expand) {
+ break;
+ }
+ }
+ last_row->expand = TRUE;
+ } else {
+ row->expand = TRUE;
+ }
+ }
+ }
+ span_max = 50000; // not sure if this is unreasonable low or high
+ }
+
+
+ int col_ext = 0;
+ int row_ext = 0;
+
+ int preferred_width = 0;
+ int preferred_height = 0;
+ for(int j=0;j<ncols;j++) {
+ preferred_width += cols[j].preferred_size;
+ if(cols[j].expand) {
+ col_ext++;
+ }
+ }
+ for(int j=0;j<nrows;j++) {
+ preferred_height += rows[j].preferred_size;
+ if(rows[j].expand) {
+ row_ext++;
+ }
+ }
+ if(ncols > 0) {
+ preferred_width += (ncols-1) * colspacing;
+ }
+ if(nrows > 0) {
+ preferred_height += (nrows-1) * rowspacing;
+ }
+
+ _preferredSize.width = preferred_width;
+ _preferredSize.height = preferred_height;
+
+
+ int hremaining = viewFrame.size.width - preferred_width;
+ int vremaining = viewFrame.size.height - preferred_height;
+ int hext = hremaining/col_ext;
+ int vext = vremaining/row_ext;
+
+ for(int j=0;j<ncols;j++) {
+ GridDef *col = &cols[j];
+ if(col->expand) {
+ col->size = col->preferred_size + hext;
+ } else {
+ col->size = col->preferred_size;
+ }
+ }
+ for(int j=0;j<nrows;j++) {
+ GridDef *row = &rows[j];
+ if(row->expand) {
+ row->size = row->preferred_size + vext;
+ } else {
+ row->size = row->preferred_size;
+ }
+ }
+
+ int pos = 0;
+ for(int j=0;j<ncols;j++) {
+ cols[j].pos = pos;
+ pos += cols[j].size + colspacing;
+ }
+ pos = 0;
+ for(int j=0;j<nrows;j++) {
+ rows[j].pos = pos;
+ pos += rows[j].size + rowspacing;
+ }
+
+ CxIterator i = cxListIterator(_children);
+ cx_foreach(GridElm *, elm, i) {
+ //NSSize size = elm->view.intrinsicContentSize;
+ GridDef *col = &cols[elm->x];
+ GridDef *row = &rows[elm->y];
+
+ NSRect frame;
+ if(elm->hfill) {
+ if(elm->colspan > 1) {
+ int cwidth = 0;
+ int end_col = elm->x + elm->colspan;
+ if(end_col > ncols) {
+ end_col = ncols;
+ }
+ int real_span = 0;
+ for(int c=elm->x;c<end_col;c++) {
+ cwidth += cols[c].size;
+ real_span++;
+ }
+ if(real_span > 0) {
+ cwidth += (real_span-1) * colspacing;
+ }
+ frame.size.width = cwidth;
+ } else {
+ frame.size.width = col->size;
+ }
+ } else {
+ frame.size.width = elm->preferred_width;
+ }
+ frame.size.width -= elm->margin.left + elm->margin.right;
+ if(elm->vfill) {
+ if(elm->rowspan > 1) {
+ int rheight = 0;
+ int end_row = elm->y + elm->rowspan;
+ if(end_row > nrows) {
+ end_row = nrows;
+ }
+ int real_span = 0;
+ for(int r=elm->y;r<end_row;r++) {
+ rheight += rows[r].size;
+ real_span++;
+ }
+ if(real_span > 0) {
+ rheight += (real_span-1) * rowspacing;
+ }
+ frame.size.height = rheight;
+ }
+ frame.size.height = row->size;
+ } else {
+ frame.size.height = elm->preferred_height;
+ }
+ frame.size.height -= elm->margin.top + elm->margin.bottom;
+
+ frame.origin.x = col->pos + elm->margin.left;
+ frame.origin.y = row->pos + elm->margin.top;
+ NSRect viewFrame = [elm->view frameForAlignmentRect:frame];
+ elm->view.frame = viewFrame;
+ }
+
+ free(cols);
+ free(rows);
+}
+
+
+- (NSSize)intrinsicContentSize {
+ if(_preferredSize.width == -1) {
+ [self layout];
+ }
+ return self.preferredSize;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+ _preferredSize.width = -1;
+ _preferredSize.height = -1;
+
+ if(self.container != nil && self.container->newline) {
+ _y++;
+ _x = 0;
+ self.container->newline = FALSE;
+ }
+
+ GridElm elm;
+ elm.x = _x;
+ elm.y = _y;
+ elm.margin = NSEdgeInsetsMake(layout->margin_top, layout->margin_left, layout->margin_bottom, layout->margin_right);
+ elm.colspan = layout->colspan;
+ elm.rowspan = layout->rowspan;
+ if(layout->fill) {
+ elm.hfill = TRUE;
+ elm.vfill = TRUE;
+ elm.hexpand = TRUE;
+ elm.vexpand = TRUE;
+ } else {
+ elm.hfill = layout->hfill;
+ elm.vfill = layout->vfill;
+ elm.hexpand = layout->hexpand;
+ elm.vexpand = layout->vexpand;
+ }
+ elm.view = view;
+ cxListAdd(_children, &elm);
+
+ [self addSubview:view];
+ self.needsLayout = YES;
+
+ if(_x > _cols) {
+ _cols = _x;
+ }
+ if(_y > _rows) {
+ _rows = _y;
+ }
+ _x++;
+}
+
+- (void) dealloc {
+ cxListFree(_children);
+}
+
+@end
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "../ui/tree.h"
+
+@interface ListDataSource : NSObject <NSTableViewDataSource>
+
+@property NSArray<NSTableColumn*> *columns;
+@property UiVar *var;
+@property ui_getvaluefunc2 getvalue;
+@property void *getvaluedata;
+@property UiModel *model;
+
+- (id) init:(NSArray<NSTableColumn*>*) columns var:(UiVar*)var getvalue:(ui_getvaluefunc2) getvaluefunc getvaluedata:(void*)userdata;
+
+@end
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "ListDataSource.h"
+
+@implementation ListDataSource
+
+- (id) init:(NSArray<NSTableColumn*>*) columns var:(UiVar*)var getvalue:(ui_getvaluefunc2) getvaluefunc getvaluedata:(void*)userdata {
+ _columns = columns;
+ _var = var;
+ _getvalue = getvaluefunc;
+ _getvaluedata = userdata;
+ return self;
+}
+
+- (NSInteger) numberOfRowsInTableView:(NSTableView *) tableView {
+ if(_var) {
+ UiList *list = _var->value;
+ if(list->count) {
+ return list->count(list);
+ }
+ }
+ return 0;
+}
+
+- (id) tableView:(NSTableView *) tableView
+objectValueForTableColumn:(NSTableColumn *) tableColumn
+ row:(NSInteger) row
+{
+ id ret = nil;
+ UiList *list = _var->value;
+ void *elm = list->get(list, (int)row);
+ if(elm) {
+ // get column index
+ NSUInteger colIndex = [_columns indexOfObject:tableColumn];
+ if(colIndex == NSNotFound) {
+ return nil;
+ }
+
+ // get UI model type for this column
+ UiModelType type = UI_STRING;
+ UiModel *model = _model;
+ if(model) {
+ if(colIndex >= model->columns) {
+ return nil;
+ }
+ type = model->types[colIndex];
+ }
+
+ // convert the list element
+ UiBool freeResult = FALSE;
+ void *data = _getvalue(list, elm, (int)row, (int)colIndex, _getvaluedata, &freeResult);
+
+ switch(type) {
+ case UI_STRING: {
+ ret = [[NSString alloc] initWithUTF8String:data];
+ break;
+ }
+ case UI_STRING_FREE: {
+ ret = [[NSString alloc] initWithUTF8String:data];
+ freeResult = TRUE;
+ break;
+ }
+ case UI_INTEGER: {
+ break;
+ }
+ case UI_ICON: {
+ break;
+ }
+ case UI_ICON_TEXT: {
+ break;
+ }
+ case UI_ICON_TEXT_FREE: {
+ break;
+ }
+ case UI_STRING_EDITABLE: {
+ break;
+ }
+ case UI_BOOL_EDITABLE: {
+ break;
+ }
+ }
+
+ if(freeResult) {
+ free(data);
+ }
+ }
+ return ret;
+}
+
+@end
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+
+@interface ListDelegate : NSObject <NSTableViewDelegate>
+
+@property (weak) NSTableView *tableview;
+@property UiObject *obj;
+@property ui_callback onselection;
+@property void *onselectiondata;
+@property ui_callback onactivate;
+@property void *onactivatedata;
+
+- (id)init:(NSTableView*) tableview obj:(UiObject*)obj;
+
+- (void)activateEvent:(id)sender;
+
+@end
+
+UiListSelection ui_tableview_selection(NSTableView *tableview);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "ListDelegate.h"
+
+@implementation ListDelegate
+
+- (id)init:(NSTableView*) tableview obj:(UiObject*)obj {
+ _tableview = tableview;
+ _obj = obj;
+ return self;
+}
+
+- (void)activateEvent:(id)sender {
+ NSTableView *table = sender;
+ if(_onactivate) {
+ int row = (int)table.clickedRow;
+
+ UiListSelection sel;
+ sel.count = 1;
+ sel.rows = &row;
+
+ UiEvent event;
+ event.obj = _obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &sel;
+ event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+ event.intval = row;
+ event.set = ui_get_setop();
+
+ _onactivate(&event, _onactivatedata);
+ }
+}
+
+- (void) tableViewSelectionDidChange:(NSNotification *) notification {
+ if(_onselection) {
+ UiListSelection sel = ui_tableview_selection(_tableview);
+
+ UiEvent event;
+ event.obj = _obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &sel;
+ event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+ event.intval = 0;
+ event.set = ui_get_setop();
+
+ _onselection(&event, _onselectiondata);
+ }
+}
+
+@end
+
+UiListSelection ui_tableview_selection(NSTableView *tableview) {
+ NSIndexSet *indexSet = tableview.selectedRowIndexes;
+ NSUInteger count = [indexSet count];
+
+ if(count == 0) {
+ return (UiListSelection){0, NULL};
+ }
+
+ int *rows = calloc(count, sizeof(int));
+
+ __block NSUInteger i = 0;
+ [indexSet enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
+ rows[i++] = (int)index;
+ }];
+
+ UiListSelection sel;
+ sel.count = (int)count;
+ sel.rows = rows;
+ return sel;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+#import "../ui/window.h"
+
+@interface MainWindow : NSWindow<UiToplevelObject>
+
+@property UiObject *obj;
+@property (strong) NSSplitView *splitview;
+@property (strong) NSView *sidebar;
+@property (strong) NSView *leftPanel;
+@property (strong) NSView *rightPanel;
+@property int topOffset;
+
+- (MainWindow*)init:(UiObject*)obj withSidebar:(BOOL)hasSidebar withSplitview:(BOOL)hasSplitview;
+
+@end
+
+
+@interface MainWindowController : NSWindowController<NSMenuItemValidation>
+
+@property UiObject *uiobj;
+@property NSMutableDictionary *checkItemStates;
+@property NSMutableDictionary *radioItems;
+
+- (MainWindowController*)initWithWindow:(UiObject*)obj window:(NSWindow*)window;
+
+- (void) windowDidLoad;
+
+- (void)menuItemAction:(id)sender;
+
+- (BOOL) validateMenuItem:(NSMenuItem *) menuItem;
+
+@end
+
+@interface MenuItemState : NSObject
+@property (weak) MainWindowController *mainWindow;
+@property UiVar *var;
+@property int state;
+@end
+
+
+int64_t ui_menu_check_item_get(UiInteger *i);
+void ui_menu_check_item_set(UiInteger *i, int64_t value);
+
+int64_t ui_menu_radio_item_get(UiInteger *i);
+void ui_menu_radio_item_set(UiInteger *i, int64_t value);
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "MainWindow.h"
+#import "Container.h"
+#import "GridLayout.h"
+#import "BoxContainer.h"
+#import "../common/object.h"
+#import "../ui/properties.h"
+#import <objc/runtime.h>
+
+#import "EventData.h"
+#import "menu.h"
+#import "Toolbar.h"
+
+@implementation MainWindow
+
+- (MainWindow*)init:(UiObject*)obj withSidebar:(BOOL)hasSidebar withSplitview:(BOOL)hasSplitview{
+ NSRect frame = NSMakeRect(300, 200, 600, 500);
+
+ self = [self initWithContentRect:frame
+ styleMask:NSWindowStyleMaskTitled |
+ NSWindowStyleMaskResizable |
+ NSWindowStyleMaskClosable |
+ NSWindowStyleMaskMiniaturizable
+ backing:NSBackingStoreBuffered
+ defer:false];
+ _obj = obj;
+
+
+ int top = 4;
+ NSView *content = self.contentView;
+
+ // A sidebar or splitview window need a NSSplitView
+ NSSplitView *splitview;
+ if(hasSidebar || hasSplitview) {
+ self.styleMask |= NSWindowStyleMaskFullSizeContentView;
+ self.titleVisibility = NSWindowTitleHidden;
+ self.titlebarAppearsTransparent = YES;
+
+ splitview = [[NSSplitView alloc]init];
+ splitview.vertical = YES;
+ splitview.dividerStyle = NSSplitViewDividerStyleThin;
+ splitview.translatesAutoresizingMaskIntoConstraints = false;
+ [self.contentView addSubview:splitview];
+ _splitview = splitview;
+
+ [NSLayoutConstraint activateConstraints:@[
+ [splitview.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0],
+ [splitview.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
+ [splitview.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
+ [splitview.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor]
+ ]];
+
+ top = 34;
+ }
+
+ if(hasSidebar) {
+ // add the sidebar
+ const char *sidebarMaterialProperty = ui_get_property("ui.cocoa.sidebar.usematerial");
+ BOOL useMaterial = YES;
+ if(sidebarMaterialProperty && (sidebarMaterialProperty[0] == 'f' || sidebarMaterialProperty[0] == 'F')) {
+ useMaterial = NO;
+ }
+
+ if(useMaterial) {
+ NSVisualEffectView *v = [[NSVisualEffectView alloc] initWithFrame:NSMakeRect(0,0,0,0)];
+ v.material = NSVisualEffectMaterialSidebar;
+ v.blendingMode = NSVisualEffectBlendingModeBehindWindow;
+ v.state = NSVisualEffectStateActive;
+ _sidebar = v;
+ } else {
+ _sidebar = [[NSView alloc]initWithFrame:NSMakeRect(0,0,0,0)];
+ }
+ _sidebar.translatesAutoresizingMaskIntoConstraints = NO;
+ [splitview addArrangedSubview:_sidebar];
+ [_sidebar.widthAnchor constraintGreaterThanOrEqualToConstant:250].active = YES;
+ }
+ if(hasSplitview) {
+ // add the splitview window left/right panels
+ _leftPanel = [[NSView alloc]initWithFrame:NSMakeRect(0,0,100,100)];
+ [splitview addArrangedSubview:_leftPanel];
+ _rightPanel = [[NSView alloc]initWithFrame:NSMakeRect(0,0,100,100)];
+ [splitview addArrangedSubview:_rightPanel];
+ } else if(hasSidebar) {
+ // sidebar only window: add content view
+ content = [[NSView alloc]initWithFrame:NSMakeRect(0,0,100,100)];
+ [splitview addArrangedSubview:content];
+ }
+
+ // normal or sidebar-only windows get a container
+ if(!hasSplitview) {
+ // create a vertical stackview as default container
+ BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
+ //GridLayout *vbox = [[GridLayout alloc] init];
+ vbox.translatesAutoresizingMaskIntoConstraints = false;
+ [content addSubview:vbox];
+ [NSLayoutConstraint activateConstraints:@[
+ [vbox.topAnchor constraintEqualToAnchor:content.topAnchor constant:top],
+ [vbox.leadingAnchor constraintEqualToAnchor:content.leadingAnchor],
+ [vbox.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],
+ [vbox.bottomAnchor constraintEqualToAnchor:content.bottomAnchor],
+ ]];
+ UiContainerX *container = ui_create_container(obj, vbox);
+ vbox.container = container;
+ uic_object_push_container(obj, container);
+ }
+ _topOffset = top;
+
+ if(uic_toolbar_isenabled()) {
+ UiToolbar *toolbar = [[UiToolbar alloc]initWithWindow:self];
+ [self setToolbar:toolbar];
+ }
+
+
+ return self;
+}
+
+- (BOOL) getIsVisible {
+ return [self isVisible];
+}
+
+- (void) setVisible:(BOOL)visible {
+ if(visible) {
+ [self makeKeyAndOrderFront:nil];
+ } else {
+ [self close];
+ }
+}
+
+
+@end
+
+
+@implementation MainWindowController
+
+- (MainWindowController*)initWithWindow:(UiObject*)obj window:(NSWindow*)window {
+ self = [super initWithWindow:window];
+ _uiobj = obj;
+
+ self.checkItemStates = [[NSMutableDictionary alloc] init];
+ self.radioItems = [[NSMutableDictionary alloc] init];
+
+ // bind all stateful menu items (checkbox, radiobuttons, lists)
+ NSArray *menuBindItems = ui_get_binding_items(); // returns all items that require binding
+ for(MenuItem *item in menuBindItems) {
+ if(item.checkItem || item.radioItem) {
+ // simple check item (ui_menu_toggleitem_create)
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, item.checkItem ? item.checkItem->varname : item.radioItem->varname, UI_VAR_INTEGER);
+ // create the state object for this item/window
+ MenuItemState *state = [[MenuItemState alloc] init];
+ state.mainWindow = self;
+ state.var = var;
+ if(var) {
+ UiInteger *i = var->value;
+ if(item.checkItem) {
+ // bind toggle item
+ state.state = (int)i->value;
+ i->obj = (__bridge void*)state;
+ i->get = ui_menu_check_item_get;
+ i->set = ui_menu_check_item_set;
+ } else {
+ // bind radio item
+ NSMutableArray *rgroup = nil;
+ if(i->obj) {
+ rgroup = (__bridge NSMutableArray*)i->obj;
+ } else {
+ // create a new rgroup array and register it in the window
+ rgroup = [[NSMutableArray alloc] init];
+ NSString *varname = [[NSString alloc] initWithUTF8String:item.radioItem->varname];
+ [_radioItems setObject:rgroup forKey:varname];
+ i->obj = (__bridge void*)rgroup;
+ }
+ i->get = ui_menu_radio_item_get;
+ i->set = ui_menu_radio_item_set;
+ [rgroup addObject:state]; // add this item state to the radio group
+ // i->value can contain a non-zero value, which means a specific radiobutton
+ // should be pre-selected
+ if(i->value == rgroup.count) {
+ state.state = NSControlStateValueOn;
+ }
+ }
+ } else {
+ state.state = 0;
+ }
+ [_checkItemStates setObject:state forKey:item.itemId];
+ }
+ }
+
+ return self;
+}
+
+- (void) windowDidLoad {
+ [self.window setNextResponder:self];
+}
+
+- (void)menuItemAction:(id)sender {
+ EventData *event = objc_getAssociatedObject(sender, "eventdata");
+ if(event) {
+ if(event.obj) {
+ [event handleEvent:sender];
+ } else {
+ event.obj = self.uiobj;
+ [event handleEvent:sender];
+ event.obj = NULL;
+ }
+ }
+}
+
+- (void)menuCheckItemAction:(id)sender {
+ NSMenuItem *menuItem = sender;
+ MenuItem *item = objc_getAssociatedObject(sender, "menuitem");
+ if(!item || !item.checkItem) {
+ return;
+ }
+
+ MenuItemState *state = [_checkItemStates objectForKey:item.itemId];
+ state.state = state.state == NSControlStateValueOff ? NSControlStateValueOn : NSControlStateValueOff;
+ menuItem.state = state.state;
+
+ UiMenuCheckItem *it = item.checkItem;
+ if(it->callback) {
+ UiEvent event;
+ event.obj = _uiobj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = state.var ? state.var->value : NULL;
+ event.intval = state.state;
+ it->callback(&event, it->userdata);
+ }
+}
+
+- (void)menuRadioItemAction:(id)sender {
+ NSMenuItem *menuItem = sender;
+ MenuItem *item = objc_getAssociatedObject(sender, "menuitem");
+ if(!item || !item.radioItem) {
+ return;
+ }
+
+ UiMenuRadioItem *it = item.radioItem;
+ if(!it->varname) {
+ return;
+ }
+
+ MenuItemState *state = [_checkItemStates objectForKey:item.itemId]; // current state of this menu item
+
+ NSString *varname = [[NSString alloc] initWithUTF8String:it->varname];
+ NSArray *radioGroup = [_radioItems objectForKey:varname];
+ if(!radioGroup) {
+ return;
+ }
+ int index = 1;
+ int value = 0;
+ for(MenuItemState *g in radioGroup) {
+ if(g == state) {
+ menuItem.state = NSControlStateValueOn;
+ g.state = NSControlStateValueOn;
+ value = index;
+ } else {
+ menuItem.state = NSControlStateValueOff;
+ g.state = NSControlStateValueOff;
+ }
+ }
+
+ if(it->callback) {
+ UiEvent event;
+ event.obj = _uiobj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = state.var ? state.var->value : NULL;
+ event.intval = value;
+ it->callback(&event, it->userdata);
+ }
+}
+
+
+- (BOOL) validateMenuItem:(NSMenuItem *) menuItem {
+ MenuItem *item = objc_getAssociatedObject(menuItem, "menuitem");
+ if(item) {
+ MenuItemState *state = [_checkItemStates objectForKey:item.itemId];
+ if(state) {
+ menuItem.state = state.state;
+ } else {
+ menuItem.state = NSControlStateValueOff;
+ }
+ }
+
+ return YES;
+}
+
+@end
+
+@implementation MenuItemState
+
+@end
+
+int64_t ui_menu_check_item_get(UiInteger *i) {
+ MenuItemState *state = (__bridge MenuItemState*)i->obj;
+ i->value = state.state;
+ return i->value;
+}
+
+void ui_menu_check_item_set(UiInteger *i, int64_t value) {
+ MenuItemState *state = (__bridge MenuItemState*)i->obj;
+ i->value = value;
+ state.state = (int)value;
+}
+
+int64_t ui_menu_radio_item_get(UiInteger *i) {
+ NSArray *rgroup = (__bridge NSArray*)i->obj;
+ i->value = 0;
+ int index = 1;
+ for(MenuItemState *state in rgroup) {
+ if(state.state == NSControlStateValueOn) {
+ i->value = index;
+ break;
+ }
+ index++;
+ }
+ return i->value;
+}
+
+void ui_menu_radio_item_set(UiInteger *i, int64_t value) {
+ NSArray *rgroup = (__bridge NSArray*)i->obj;
+ i->value = 0;
+ int index = 1;
+ for(MenuItemState *state in rgroup) {
+ state.state = value == index;
+ index++;
+ }
+}
+
+
+UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
+ MainWindow *window = (__bridge MainWindow*)obj->wobj;
+ if(window.sidebar == nil) {
+ return NULL;
+ }
+ NSView *sidebar = window.sidebar;
+
+ // create a vertical stackview as default container
+ BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:args->spacing];
+ vbox.container = ui_create_container(obj, vbox);
+ //GridLayout *vbox = [[GridLayout alloc] init];
+ vbox.translatesAutoresizingMaskIntoConstraints = false;
+ [sidebar addSubview:vbox];
+ [NSLayoutConstraint activateConstraints:@[
+ [vbox.topAnchor constraintEqualToAnchor:sidebar.topAnchor constant:34],
+ [vbox.leadingAnchor constraintEqualToAnchor:sidebar.leadingAnchor],
+ [vbox.trailingAnchor constraintEqualToAnchor:sidebar.trailingAnchor],
+ [vbox.bottomAnchor constraintEqualToAnchor:sidebar.bottomAnchor]
+ ]];
+ uic_object_push_container(obj, vbox.container);
+
+ return NULL;
+}
+
+static UIWIDGET splitview_window_add_panel(UiObject *obj, NSView *panel, UiSidebarArgs *args) {
+ MainWindow *window = (__bridge MainWindow*)obj->wobj;
+ BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
+ //GridLayout *vbox = [[GridLayout alloc] init];
+ vbox.container = ui_create_container(obj, vbox);
+ vbox.translatesAutoresizingMaskIntoConstraints = false;
+ [panel addSubview:vbox];
+ [NSLayoutConstraint activateConstraints:@[
+ [vbox.topAnchor constraintEqualToAnchor:panel.topAnchor constant:window.topOffset],
+ [vbox.leadingAnchor constraintEqualToAnchor:panel.leadingAnchor],
+ [vbox.trailingAnchor constraintEqualToAnchor:panel.trailingAnchor],
+ [vbox.bottomAnchor constraintEqualToAnchor:panel.bottomAnchor],
+ ]];
+ uic_object_push_container(obj, vbox.container);
+ return (__bridge void*)vbox;
+}
+
+UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args) {
+ MainWindow *window = (__bridge MainWindow*)obj->wobj;
+ if(window.leftPanel == nil) {
+ return NULL;
+ }
+ return splitview_window_add_panel(obj, window.leftPanel, args);
+}
+
+UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args) {
+ MainWindow *window = (__bridge MainWindow*)obj->wobj;
+ if(window.rightPanel == nil) {
+ return NULL;
+ }
+ return splitview_window_add_panel(obj, window.rightPanel, args);
+}
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+$(COCOA_OBJPRE)%.o: cocoa/%.m
+ $(CC) -o $@ -c -I../ucx -fobjc-arc $(CFLAGS) $(SHLIB_CFLAGS) $(TK_CFLAGS) $<
+
+$(UI_LIB): $(OBJ)
+ $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ)
+
+$(UI_SHLIB): $(OBJ)
+ $(CC) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) -L../build/lib -lucx
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "Container.h"
+
+@protocol TabView
+
+- (NSView<Container>*) createTab:(int)index title:(NSString*)title;
+- (void) selectTab:(int)index;
+- (void) removeTab:(int)index;
+- (UiObject*) addTab:(int)index title:(NSString*)title;
+
+@end
+
+@interface UiTopTabView : NSTabView<TabView, Container>
+
+@property UiObject *obj;
+@property UiSubContainerType subcontainer;
+@property int padding;
+@property int spacing;
+@property int columnspacing;
+@property int rowspacing;
+@property ui_callback onchange;
+@property void *onchangedata;
+@property UiVar *var;
+
+- (id)init:(UiObject*)obj args:(UiTabViewArgs*)args;
+
+@end
+
+int64_t ui_nstabview_get(UiInteger *i);
+void ui_nstabview_set(UiInteger *i, int64_t value);
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "TabView.h"
+#import "BoxContainer.h"
+#import "GridLayout.h"
+
+@implementation UiTopTabView
+
+@synthesize container = _container;
+
+- (id)init:(UiObject*)obj args:(UiTabViewArgs*)args {
+ self = [super init];
+ _obj = obj;
+ _subcontainer = args->subcontainer;
+ _padding = args->padding;
+ _spacing = args->spacing;
+ _columnspacing = args->columnspacing;
+ _rowspacing = args->rowspacing;
+ _onchange = args->onchange;
+ _onchangedata = args->onchangedata;
+ _var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+
+ if(args->tabview == UI_TABVIEW_INVISIBLE || args->tabview == UI_TABVIEW_NAVIGATION_SIDE) {
+ self.tabViewType = NSNoTabsNoBorder;
+ }
+
+ if(_var) {
+ UiInteger *i = _var->value;
+ i->obj = (__bridge void*)self;
+ i->get = ui_nstabview_get;
+ i->set = ui_nstabview_set;
+ }
+
+ return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+ // noop
+}
+
+- (NSView<Container>*) createTab:(int)index title:(NSString*)title {
+ NSTabViewItem *item = [[NSTabViewItem alloc]initWithIdentifier:nil];
+ [item setLabel:title];
+ if(index < 0) {
+ [self addTabViewItem:item];
+ } else {
+ [self insertTabViewItem:item atIndex:index];
+ }
+
+ BoxContainer *content = [[BoxContainer alloc]init];
+ item.view = content;
+
+ GridLayout *sub;
+ switch(_subcontainer) {
+ default: sub = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:_spacing]; break;
+ case UI_CONTAINER_HBOX: sub = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationHorizontal spacing:_spacing]; break;
+ case UI_CONTAINER_GRID: {
+ sub = [[GridLayout alloc] init];
+ sub.columnspacing = _columnspacing;
+ sub.rowspacing = _rowspacing;
+ break;
+ }
+ }
+ UiLayout layout = {
+ .margin = _padding,
+ .margin_left = _padding, .margin_right = _padding, .margin_top = _padding, .margin_bottom = _padding,
+ .fill = TRUE };
+ [content addView:sub layout:&layout];
+
+ return sub;
+}
+
+- (void) selectTab:(int)index {
+ [self selectTabViewItemAtIndex:index];
+}
+
+- (void) removeTab:(int)index {
+ NSTabViewItem *item = [self tabViewItemAtIndex:index];
+ if(item != nil) {
+ [self removeTabViewItem:item];
+ }
+}
+
+- (UiObject*) addTab:(int)index title:(NSString*)title {
+ NSView<Container> *sub = [self createTab:index title:title];
+
+ UiObject *newobj = uic_object_new_toplevel();
+ newobj->widget = (__bridge void*)sub;
+
+ UiContainerX *container = ui_create_container(newobj, sub);
+ uic_object_push_container(newobj, container);
+
+ return newobj;
+}
+
+@end
+
+int64_t ui_nstabview_get(UiInteger *i) {
+ UiTopTabView *tabview = (__bridge UiTopTabView*)i->obj;
+ i->value = [tabview indexOfTabViewItem:tabview.selectedTabViewItem];
+ return i->value;
+}
+
+void ui_nstabview_set(UiInteger *i, int64_t value) {
+ UiTopTabView *tabview = (__bridge UiTopTabView*)i->obj;
+ [tabview selectTab:(int)value];
+ i->value = [tabview indexOfTabViewItem:tabview.selectedTabViewItem];
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "../common/toolbar.h"
+#import "MainWindow.h"
+
+/*
+ * UiToolbarDelegate
+ */
+@interface UiToolbarDelegate : NSObject <NSToolbarDelegate> {
+ NSMutableArray<NSString*> *allowedItems;
+ NSMutableArray<NSString*> *defaultItems;
+}
+
+- (UiToolbarDelegate*) init;
+
+@end
+
+/*
+ * UiToolbar
+ */
+@interface UiToolbar : NSToolbar <NSToolbarDelegate> {
+ NSMutableArray<NSString*> *allowedItems;
+ NSMutableArray<NSString*> *defaultItems;
+}
+
+@property MainWindow *window;
+@property UiObject *obj;
+
+- (UiToolbar*) initWithWindow:(MainWindow*)window;
+
+@end
+
+
+@interface UiToolbarToggleEventHandler : NSObject
+@property UiObject *obj;
+@property UiVar *var;
+@property ui_callback callback;
+@property void *userdata;
+
+- (UiToolbarToggleEventHandler*)init;
+- (void)handleEvent:(id)sender;
+
+@end
+
+void ui_toolbar_init(void);
+
+NSToolbarItem* ui_nstoolbaritem_create_item(UiObject *obj, UiToolbarItem *item, NSString *identifier);
+NSToolbarItem* ui_nstoolbaritem_create_toggle(UiObject *obj, UiToolbarToggleItem *item, NSString *identifier);
+NSToolbarItem* ui_nstoolbaritem_create_menu(UiObject *obj, UiToolbarMenuItem *item, NSString *identifier);
+
+int64_t ui_toolbar_seg_toggleitem_get(UiInteger *i);
+void ui_toolbar_seg_toggleitem_set(UiInteger *i, int64_t value);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "Toolbar.h"
+#import "EventData.h"
+#import "image.h"
+#import "menu.h"
+#import <objc/runtime.h>
+
+#include "../common/toolbar.h"
+
+
+void ui_toolbar_init(void) {
+
+}
+
+
+
+/* --------------------- UiToolbar --------------------- */
+
+@implementation UiToolbar
+
+- (UiToolbar*) initWithWindow:(MainWindow*)window {
+ self = [super initWithIdentifier:@"UiToolbar"];
+ _window = window;
+ _obj = window.obj;
+
+ allowedItems = [[NSMutableArray alloc]initWithCapacity:16];
+ defaultItems = [[NSMutableArray alloc]initWithCapacity:16];
+
+ if(window.sidebar) {
+ [allowedItems addObject:@"sidebar_separator"];
+ }
+ if(window.leftPanel) {
+ [allowedItems addObject:@"splitview_separator"];
+ }
+
+ CxMap *toolbarItems = uic_get_toolbar_items();
+ CxMapIterator i = cxMapIteratorKeys(toolbarItems);
+ cx_foreach(CxHashKey *, key, i) {
+ NSString *s = [[NSString alloc]initWithBytes:key->data length:key->len encoding:NSUTF8StringEncoding];
+ [allowedItems addObject:s];
+ }
+ [allowedItems addObject: NSToolbarFlexibleSpaceItemIdentifier];
+ [allowedItems addObject: NSToolbarSpaceItemIdentifier];
+
+ // UI_TOOLBAR_LEFT = 0,
+ // UI_TOOLBAR_CENTER,
+ // UI_TOOLBAR_RIGHT,
+ // UI_TOOLBAR_SIDEBAR_LEFT,
+ // UI_TOOLBAR_SIDEBAR_RIGHT,
+ // UI_TOOLBAR_RIGHTPANEL_LEFT,
+ // UI_TOOLBAR_RIGHTPANEL_CENTER,
+ // UI_TOOLBAR_RIGHTPANEL_RIGHT
+ CxList *tbitems[8];
+ for(int i=0;i<8;i++) {
+ tbitems[i] = uic_get_toolbar_defaults(i);
+ }
+
+ if(window.sidebar) {
+ CxIterator iter = cxListIterator(tbitems[UI_TOOLBAR_SIDEBAR_LEFT]);
+ cx_foreach(char *, name, iter) {
+ NSString *s = [[NSString alloc] initWithUTF8String:name];
+ [defaultItems addObject:s];
+ }
+
+ CxList *sidebarRight = tbitems[UI_TOOLBAR_SIDEBAR_RIGHT];
+ if(cxListSize(sidebarRight) > 0) {
+ [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier];
+ iter = cxListIterator(sidebarRight);
+ cx_foreach(char *, name, iter) {
+ NSString *s = [[NSString alloc] initWithUTF8String:name];
+ [defaultItems addObject:s];
+ }
+ }
+
+ [defaultItems addObject:@"sidebar_separator"];
+ }
+
+ int start_pos = UI_TOOLBAR_LEFT;
+ for(int x=0;x<2;x++) {
+ for(int t=start_pos;t<start_pos+3;t++) {
+ CxIterator iter = cxListIterator(tbitems[t]);
+ cx_foreach(char *, name, iter) {
+ NSString *s = [[NSString alloc] initWithUTF8String:name];
+ [defaultItems addObject:s];
+ }
+ if(t < start_pos+2 && cxListSize(tbitems[t+1]) > 0) {
+ [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier];
+ }
+ }
+
+ if(x == 0 && window.rightPanel) {
+ [defaultItems addObject:@"splitview_separator"];
+ }
+ start_pos = UI_TOOLBAR_RIGHTPANEL_LEFT;
+ }
+
+
+ [self setDelegate:self];
+ [self setAllowsUserCustomization:YES];
+ return self;
+}
+
+// implementation of NSToolbarDelegate methods
+
+- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
+ return allowedItems;
+}
+
+- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
+ return defaultItems;
+}
+
+- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
+ itemForItemIdentifier:(NSString *)itemIdentifier
+ willBeInsertedIntoToolbar:(BOOL)flag {
+ CxMap *items = uic_get_toolbar_items();
+ UiToolbarItemI *item = cxMapGet(items, itemIdentifier.UTF8String);
+ if(!item) {
+ if([itemIdentifier isEqualToString:@"sidebar_separator"]) {
+ NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier
+ splitView:_window.splitview
+ dividerIndex:0];
+ return sep;
+ } else if([itemIdentifier isEqualToString:@"splitview_separator"]) {
+ int dividerIndex = _window.sidebar != nil ? 1 : 0;
+ NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier
+ splitView:_window.splitview
+ dividerIndex:dividerIndex];
+ return sep;
+ }
+ return nil;
+ }
+
+ switch(item->type) {
+ default: return nil;
+ case UI_TOOLBAR_ITEM: {
+ return ui_nstoolbaritem_create_item(_obj, (UiToolbarItem*)item, itemIdentifier);
+ }
+ case UI_TOOLBAR_TOGGLEITEM: {
+ return ui_nstoolbaritem_create_toggle(_obj, (UiToolbarToggleItem*)item, itemIdentifier);
+ }
+ case UI_TOOLBAR_MENU: {
+ return ui_nstoolbaritem_create_menu(_obj, (UiToolbarMenuItem*)item, itemIdentifier);
+ }
+ }
+
+ return nil;
+}
+
+@end
+
+@implementation UiToolbarToggleEventHandler
+
+- (UiToolbarToggleEventHandler*)init {
+ return self;
+}
+
+- (void)handleEvent:(id)sender {
+ if(_callback == nil) {
+ return;
+ }
+
+ UiEvent event;
+ event.obj = _obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = 0;
+ event.set = ui_get_setop();
+
+ if([sender isKindOfClass:[NSSegmentedControl class]]) {
+ NSSegmentedControl *seg = (NSSegmentedControl*)sender;
+ event.intval = [seg isSelectedForSegment:0];
+ }
+
+ _callback(&event, _userdata);
+}
+
+@end
+
+NSToolbarItem* ui_nstoolbaritem_create_item(UiObject *obj, UiToolbarItem *item, NSString *identifier) {
+ NSToolbarItem *button = [[NSToolbarItem alloc] initWithItemIdentifier: identifier];
+ button.bordered = YES;
+
+ if(item->args.label) {
+ NSString *label = [[NSString alloc] initWithUTF8String:item->args.label];
+ button.paletteLabel = label;
+ button.label = label;
+ }
+ if(item->args.icon) {
+ button.image = ui_cocoa_named_icon(item->args.icon);
+ }
+
+ if(item->args.onclick) {
+ EventData *event = [[EventData alloc] init:item->args.onclick userdata:item->args.onclickdata];
+ event.obj = obj;
+ button.target = event;
+ button.action = @selector(handleEvent:);
+ objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+ return button;
+}
+
+NSToolbarItem* ui_nstoolbaritem_create_toggle(UiObject *obj, UiToolbarToggleItem *item, NSString *identifier) {
+ UiToolbarToggleEventHandler *event = [[UiToolbarToggleEventHandler alloc]init];
+ event.obj = obj;
+ event.callback = item->args.onchange;
+ event.userdata = item->args.onchangedata;
+
+ NSToolbarItem *button = [[NSToolbarItem alloc] initWithItemIdentifier:identifier];
+ if(item->args.label) {
+ NSString *label = [[NSString alloc] initWithUTF8String:item->args.label];
+ button.paletteLabel = label;
+ button.label = label;
+ }
+ objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+
+ NSSegmentedControl *seg;
+ if(!item->args.icon) {
+ NSArray *labels = @[[[NSString alloc] initWithUTF8String:item->args.label]];
+ seg = [NSSegmentedControl segmentedControlWithLabels:labels trackingMode:NSSegmentSwitchTrackingSelectAny target:event action:@selector(handleEvent:)];
+ button.view = seg;
+ } else {
+ NSArray *images = @[ui_cocoa_named_icon(item->args.icon)];
+ seg = [NSSegmentedControl segmentedControlWithImages:images trackingMode:NSSegmentSwitchTrackingSelectAny target:event action:@selector(handleEvent:)];
+ }
+ button.view = seg;
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER);
+ if(var) {
+ event.var = var;
+ UiInteger *i = var->value;
+ if(i->get) {
+ if(ui_get(i) != 0) {
+ [seg setEnabled:YES forSegment:0];
+ }
+
+ }
+ i->obj = (__bridge void*)seg;
+ printf("seg: %p\n", seg);
+ i->get = ui_toolbar_seg_toggleitem_get;
+ i->set = ui_toolbar_seg_toggleitem_set;
+ }
+
+ return button;
+}
+
+int64_t ui_toolbar_seg_toggleitem_get(UiInteger *i) {
+ NSSegmentedControl *seg = (__bridge NSSegmentedControl*)i->obj;
+ i->value = [seg isSelectedForSegment:0];
+ return i->value;
+}
+
+void ui_toolbar_seg_toggleitem_set(UiInteger *i, int64_t value) {
+ NSSegmentedControl *seg = (__bridge NSSegmentedControl*)i->obj;
+ i->value = value;
+ [seg setSelected:value != 0 forSegment:0];
+}
+
+NSToolbarItem* ui_nstoolbaritem_create_menu(UiObject *obj, UiToolbarMenuItem *item, NSString *identifier) {
+ NSMenuToolbarItem *button = [[NSMenuToolbarItem alloc] initWithItemIdentifier: identifier];
+ button.bordered = YES;
+
+ if(item->args.label) {
+ NSString *label = [[NSString alloc] initWithUTF8String:item->args.label];
+ button.paletteLabel = label;
+ button.label = label;
+ }
+ if(item->args.icon) {
+ button.image = ui_cocoa_named_icon(item->args.icon);
+ }
+
+ NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
+ ui_add_menu_items(obj, menu, 0, &item->menu);
+
+ button.menu = menu;
+
+ return button;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+
+@interface UiThread : NSObject
+
+@property UiObject *obj;
+@property ui_threadfunc job;
+@property void *jobdata;
+@property ui_callback finish_callback;
+@property void *finish_userdata;
+
+- (id)init:(UiObject*)obj jobfunc:(ui_threadfunc)job jobdata:(void*)jobdata;
+
+- (void)start;
+- (void) runJob:(id)n;
+- (void) finish:(id)n;
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "UiThread.h"
+
+
+@implementation UiThread
+
+- (id) init:(UiObject*)obj jobfunc:(ui_threadfunc)job jobdata:(void*)jobdata {
+ _obj = obj;
+ _job = job;
+ _jobdata = jobdata;
+ return self;
+}
+
+- (void) start {
+ [NSThread detachNewThreadSelector:@selector(runJob:)
+ toTarget:self
+ withObject:nil];
+}
+
+
+- (void) runJob:(id)n {
+ int result = _job(_jobdata);
+ if(!result) {
+ [self performSelectorOnMainThread:@selector(finish:)
+ withObject:nil
+ waitUntilDone:NO];
+ }
+}
+
+- (void) finish:(id)n {
+ UiEvent event;
+ event.obj = _obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.intval = 0;
+ _finish_callback(&event, _finish_userdata);
+}
+
+@end
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+
+@interface WindowManager : NSObject<NSWindowDelegate>
+
+@property NSMutableArray<NSWindow*> *windows;
+
++ (WindowManager*) sharedWindowManager;
+
+- (WindowManager*)init;
+
+- (void)addWindow:(NSWindow*)win;
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "WindowManager.h"
+
+@implementation WindowManager
+
+static WindowManager *instance = nil;
+
++ (WindowManager*) sharedWindowManager {
+ if(instance == nil) {
+ instance = [[WindowManager alloc] init];
+ }
+ return instance;
+}
+
+- (WindowManager*)init {
+ _windows = [[NSMutableArray alloc] initWithCapacity:16];
+ return self;
+}
+
+- (void)addWindow:(NSWindow*)win {
+ [_windows addObject:win];
+ [win setDelegate:self];
+}
+
+- (void) windowWillClose:(NSNotification *) notification {
+ NSWindow *window = notification.object;
+ [_windows removeObject:window];
+}
+
+@end
--- /dev/null
+/*
+ * 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 "../ui/toolkit.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "AppDelegate.h"
+
+#import "toolkit.h"
+#import "menu.h"
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ ui_menu_init();
+ NSLog(@"toolkit applicationDidFinishLaunching");
+ ui_cocoa_onstartup();
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ ui_cocoa_onexit();
+}
+
+
+- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
+ return YES;
+}
+
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+
+#import "../ui/button.h"
+
+@interface UiRadioButton : NSButton
+
+@property UiVar *var;
+@property Boolean direct_state;
+
+- (UiRadioButton*)init;
+- (void)setState:(NSControlStateValue)state;
+
+@end
+
+@interface UiLinkButtonData : NSObject
+@property UiObject *obj;
+@property (weak) NSTextField *textfield;
+@property (strong) NSString *label;
+@property (strong) NSString *uri;
+@property BOOL visited;
+@property ui_callback onclick;
+@property void *onclickdata;
+
+- (id)init:(UiObject*)obj textfield:(NSTextField*)textfield;
+- (void)setLinkDataFromJson:(const char*)jsonStr;
+- (void)buildLink;
+
+@end
+
+
+int64_t ui_togglebutton_get(UiInteger *i);
+void ui_togglebutton_set(UiInteger *i, int64_t value);
+
+int64_t ui_switch_get(UiInteger *i);
+void ui_switch_set(UiInteger *i, int64_t value);
+
+int64_t ui_radiobuttons_get(UiInteger *i);
+void ui_radiobuttons_set(UiInteger *i, int64_t value);
+
+char* ui_linkbutton_get(UiString *s);
+void ui_linkbutton_set(UiString *s, const char *str);
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "button.h"
+#import "EventData.h"
+#import "Container.h"
+#import <objc/runtime.h>
+
+#import <cx/buffer.h>
+#import <cx/json.h>
+
+UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
+ NSButton *button = [[NSButton alloc] init];
+ button.translatesAutoresizingMaskIntoConstraints = NO;
+ if(args->label) {
+ NSString *label = [[NSString alloc] initWithUTF8String:args->label];
+ button.title = label;
+ }
+
+ if(args->onclick) {
+ EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+ event.obj = obj;
+ button.target = event;
+ button.action = @selector(handleEvent:);
+ objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, button, &layout);
+
+ return (__bridge void*)button;
+}
+
+
+static void togglebutton_eventdata(id button, UiVar *var, void **eventdata, int *value) {
+ NSButton *btn = (NSButton*)button;
+ NSControlStateValue state = btn.state;
+ *value = (int)state;
+}
+
+UIWIDGET togglebutton_create(UiObject* obj, UiToggleArgs *args, enum NSButtonType type) {
+ NSButton *button = [[NSButton alloc] init];
+ [button setButtonType:type];
+ //[button setAllowsMixedState:YES];
+
+ if(args->label) {
+ NSString *label = [[NSString alloc] initWithUTF8String:args->label];
+ button.title = label;
+ }
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *i = var->value;
+ i->obj = (__bridge void*)button;
+ i->get = ui_togglebutton_get;
+ i->set = ui_togglebutton_set;
+ }
+
+ if(args->onchange) {
+ EventData *event = [[EventData alloc] init:args->onchange userdata:args->onchangedata];
+ event.get_eventdata = togglebutton_eventdata;
+ event.obj = obj;
+ event.var = var;
+ event.vartype = UI_VAR_INTEGER;
+ button.target = event;
+ button.action = @selector(handleEventWithEventData:);
+ objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, button, &layout);
+
+ return (__bridge void*)button;
+}
+
+int64_t ui_togglebutton_get(UiInteger *i) {
+ NSButton *button = (__bridge NSButton*)i->obj;
+ NSControlStateValue state = button.state;
+ i->value = (int64_t)state;
+ return (int64_t)state;
+}
+
+void ui_togglebutton_set(UiInteger *i, int64_t value) {
+ NSButton *button = (__bridge NSButton*)i->obj;
+ NSControlStateValue state;
+ switch(value) {
+ case 0: state = NSControlStateValueOff; break;
+ case 1: state = NSControlStateValueOff; break;
+ default: state = NSControlStateValueMixed;
+ }
+ i->value = (int64_t)state;
+ button.state = state;
+}
+
+UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
+ return togglebutton_create(obj, args, NSButtonTypePushOnPushOff);
+}
+
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
+ return togglebutton_create(obj, args, NSButtonTypeSwitch);
+}
+
+static void switch_eventdata(id button, UiVar *var, void **eventdata, int *value) {
+ NSSwitch *btn = (NSSwitch*)button;
+ NSControlStateValue state = btn.state;
+ *value = (int)state;
+}
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
+ NSSwitch *button = [[NSSwitch alloc] init];
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *i = var->value;
+ i->obj = (__bridge void*)button;
+ i->get = ui_switch_get;
+ i->set = ui_switch_set;
+ }
+
+ if(args->onchange) {
+ EventData *event = [[EventData alloc] init:args->onchange userdata:args->onchangedata];
+ event.get_eventdata = switch_eventdata;
+ event.obj = obj;
+ event.var = var;
+ event.vartype = UI_VAR_INTEGER;
+ button.target = event;
+ button.action = @selector(handleEventWithEventData:);
+ objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, button, &layout);
+
+ return (__bridge void*)button;
+}
+
+int64_t ui_switch_get(UiInteger *i) {
+ NSSwitch *button = (__bridge NSSwitch*)i->obj;
+ NSControlStateValue state = button.state;
+ i->value = (int64_t)state;
+ return (int64_t)state;
+}
+
+void ui_switch_set(UiInteger *i, int64_t value) {
+ NSSwitch *button = (__bridge NSSwitch*)i->obj;
+ NSControlStateValue state;
+ switch(value) {
+ case 0: state = NSControlStateValueOff; break;
+ case 1: state = NSControlStateValueOff; break;
+ default: state = NSControlStateValueMixed;
+ }
+ i->value = (int64_t)state;
+ button.state = state;
+}
+
+
+@implementation UiRadioButton
+
+- (UiRadioButton*)init {
+ self = [super init];
+ _direct_state = NO;
+ [self setButtonType:NSButtonTypeRadio];
+ return self;
+}
+
+- (void)setState:(NSControlStateValue)state {
+ // NOOP
+}
+
+@end
+
+static void radiobutton_eventdata(id button, UiVar *var, void **eventdata, int *value) {
+ if(var) {
+ UiInteger *value = var->value;
+ NSMutableArray *buttons = (__bridge NSMutableArray*)value->obj;
+ for(UiRadioButton *b in buttons) {
+ if(b != button) {
+ b.direct_state = YES;
+ [[b cell] setState:0];
+ b.direct_state = NO;
+ }
+ }
+ }
+}
+
+UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
+ UiRadioButton *button = [[UiRadioButton alloc] init];
+
+ if(args->label) {
+ button.title = [[NSString alloc] initWithUTF8String:args->label];
+ }
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ button.var = var;
+ NSMutableArray *buttons = nil;
+ if(var) {
+ UiInteger *i = var->value;
+ buttons = (__bridge NSMutableArray*)i->obj;
+ if(!buttons) {
+ buttons = [[NSMutableArray alloc] init];
+ i->obj = (__bridge void*)buttons;
+ i->get = ui_radiobuttons_get;
+ i->set = ui_radiobuttons_set;
+ }
+ [buttons addObject:button];
+ objc_setAssociatedObject(button, "radiogroup", buttons, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ if(args->onchange || var) {
+ EventData *event = [[EventData alloc] init:args->onchange userdata:args->onchangedata];
+ event.get_eventdata = radiobutton_eventdata;
+ event.obj = obj;
+ event.var = var;
+ event.vartype = UI_VAR_INTEGER;
+ button.target = event;
+
+
+ button.action = @selector(handleEventWithEventData:);
+
+ objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, button, &layout);
+
+ return (__bridge void*)button;
+}
+
+int64_t ui_radiobuttons_get(UiInteger *i) {
+ NSMutableArray *buttons = (__bridge NSMutableArray*)i->obj;
+ int64_t index = 0;
+ for(UiRadioButton *b in buttons) {
+ if([b cell].state != 0) {
+ i->value = index + 1;
+ break;
+ }
+ index++;
+ }
+ return i->value;
+}
+
+void ui_radiobuttons_set(UiInteger *i, int64_t value) {
+ NSMutableArray *buttons = (__bridge NSMutableArray*)i->obj;
+ int64_t index = 1;
+ for(UiRadioButton *b in buttons) {
+ if(index == value) {
+ [b cell].state = NSControlStateValueOn;
+ } else {
+ [b cell].state = NSControlStateValueOff;
+ }
+ index++;
+ }
+}
+
+
+/* --------------------------- Link Button --------------------------- */
+
+@implementation UiLinkButtonData
+
+- (id)init:(UiObject*)obj textfield:(NSTextField*)textfield {
+ _obj = obj;
+ _textfield = textfield;
+ return self;
+}
+
+- (void)setLinkDataFromJson:(const char*)jsonStr {
+ CxJson json;
+ cxJsonInit(&json, NULL);
+ cxJsonFill(&json, jsonStr);
+
+ CxJsonValue *value;
+ if(cxJsonNext(&json, &value) == CX_JSON_NO_ERROR) {
+ if(cxJsonIsObject(value)) {
+ CxJsonValue *label = cxJsonObjGet(value, "label");
+ CxJsonValue *uri = cxJsonObjGet(value, "uri");
+ CxJsonValue *visited = cxJsonObjGet(value, "visited");
+ if(label) {
+ char *str = cxJsonIsString(label) ? cxJsonAsString(label) : NULL;
+ if(str) {
+ _label = [[NSString alloc]initWithUTF8String:str];
+ } else {
+ _label = nil;
+ }
+ }
+ if(uri) {
+ char *str = cxJsonIsString(uri) ? cxJsonAsString(uri) : NULL;
+ if(str) {
+ _uri = [[NSString alloc]initWithUTF8String:str];
+ } else {
+ _uri = nil;
+ }
+ }
+ if(visited) {
+ _visited = cxJsonIsBool(visited) ? cxJsonAsBool(visited) : FALSE;
+ }
+ }
+ cxJsonValueFree(value);
+ }
+ cxJsonDestroy(&json);
+
+ [self buildLink];
+}
+
+- (void)buildLink {
+ NSString *label = _label ? _label : @"";
+
+ NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:label];
+ [attrString beginEditing];
+ if(_uri) {
+ [attrString addAttribute:NSLinkAttributeName value:_uri range:NSMakeRange(0, attrString.length)];
+ }
+ [attrString addAttribute:NSForegroundColorAttributeName value:[NSColor systemBlueColor] range:NSMakeRange(0, attrString.length)];
+ [attrString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, attrString.length)];
+ [attrString endEditing];
+
+ [_textfield setAttributedStringValue:attrString];
+}
+
+@end
+
+static char* create_linkbutton_jsonvalue(const char *label, const char *uri, UiBool include_null, UiBool visited, UiBool set_visited) {
+ CxJsonValue *obj = cxJsonCreateObj(NULL);
+ if(label) {
+ cxJsonObjPutString(obj, CX_STR("label"), label);
+ } else if(include_null) {
+ cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL);
+ }
+
+ if(uri) {
+ cxJsonObjPutString(obj, CX_STR("uri"), uri);
+ } else if(include_null) {
+ cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL);
+ }
+
+ if(set_visited) {
+ cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
+ }
+
+ CxJsonWriter writer = cxJsonWriterCompact();
+ CxBuffer buf;
+ cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer);
+ cxJsonValueFree(obj);
+ cxBufferTerminate(&buf);
+
+ return buf.space;
+}
+
+UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) {
+ NSTextField *label = [[NSTextField alloc] init];
+ label.editable = NO;
+ label.bezeled = NO;
+ label.drawsBackground = NO;
+ label.allowsEditingTextAttributes = YES;
+ label.selectable = YES;
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ui_container_add(obj, label, &layout);
+
+ UiLinkButtonData *data = [[UiLinkButtonData alloc]init:obj textfield:label];
+ objc_setAssociatedObject(label, "linkdata", data, OBJC_ASSOCIATION_RETAIN);
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiString *s = var->value;
+ s->obj = (__bridge void*)data;
+ s->get = ui_linkbutton_get;
+ s->set = ui_linkbutton_set;
+
+ if(s->value.ptr) {
+ [data setLinkDataFromJson:s->value.ptr];
+ }
+ }
+
+ return (__bridge void*)label;
+}
+
+char* ui_linkbutton_get(UiString *s) {
+ return NULL; // TODO
+}
+
+void ui_linkbutton_set(UiString *s, const char *str) {
+ UiLinkButtonData *data = (__bridge UiLinkButtonData*)s->obj;
+ [data setLinkDataFromJson:str];
+}
+
+
+
+void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri) {
+ char *value = create_linkbutton_jsonvalue(label, uri, TRUE, FALSE, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+void ui_linkbutton_value_set_label(UiString *str, const char *label) {
+ char *value = create_linkbutton_jsonvalue(label, NULL, FALSE, FALSE, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+void ui_linkbutton_value_set_uri(UiString *str, const char *uri) {
+ char *value = create_linkbutton_jsonvalue(NULL, uri, FALSE, FALSE, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+void ui_linkbutton_value_set_visited(UiString *str, UiBool visited) {
+ char *value = create_linkbutton_jsonvalue(NULL, NULL, FALSE, visited, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+// TODO
+
+void ui_linkbutton_set_label(UIWIDGET button, const char *label) {
+
+}
+
+void ui_linkbutton_set_uri(UIWIDGET button, const char *label) {
+
+}
+
+void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited) {
+
+}
+
+char* ui_linkbutton_get_label(UIWIDGET button) {
+ return NULL;
+}
+
+char* ui_linkbutton_get_uri(UIWIDGET button) {
+ return NULL;
+}
+
+UiBool ui_linkbutton_get_visited(UIWIDGET button) {
+ return FALSE;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+
+#import "../ui/container.h"
+
+
+typedef struct UiLayout UiLayout;
+
+
+#define UI_INIT_LAYOUT(args) (UiLayout) {\
+ .fill = args->fill, \
+ .hexpand = args->hexpand, \
+ .vexpand = args->vexpand, \
+ .hfill = args->hfill, \
+ .vfill = args->vfill, \
+ .margin = args->margin, \
+ .margin_left = args->margin_left, \
+ .margin_right = args->margin_right, \
+ .margin_top = args->margin_top, \
+ .margin_bottom = args->margin_bottom, \
+ .colspan = args->colspan, \
+ .rowspan = args->rowspan }
+
+
+@protocol Container
+
+@property UiContainerX *container;
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout;
+
+@end
+
+
+@interface FrameContainer : NSBox<Container>
+
+- (id)init:(NSString*)title;
+
+@end
+
+@interface ScrollViewContainer : NSScrollView<Container>
+
+@property NSView<Container> *child;
+
+- (id)init:(UiSubContainerType)subContainer columnSpacing:(int)columnSpacing rowSpacing:(int)rowSpacing;
+
+@end
+
+
+UiContainerX* ui_create_container(UiObject *obj, id<Container> container);
+
+void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout);
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "Container.h"
+#import "GridLayout.h"
+#import "BoxContainer.h"
+#import "TabView.h"
+
+/* -------------------- public container functions --------------------- */
+
+static UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, NSUserInterfaceLayoutOrientation orientation) {
+ BoxContainer *box = [[BoxContainer alloc] init:orientation spacing:args->spacing];
+ box.translatesAutoresizingMaskIntoConstraints = false;
+ UiContainerX *container = ui_create_container(obj, box);
+
+ // add box to the parent
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, box, &layout);
+
+ // add new box to the obj container chain
+ uic_object_push_container(obj, container);
+
+ return (__bridge void*)box;
+}
+
+UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
+ return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationVertical);
+}
+
+UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
+ return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationHorizontal);
+}
+
+UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
+ GridLayout *grid = [[GridLayout alloc] init];
+ grid.translatesAutoresizingMaskIntoConstraints = false;
+ grid.columnspacing = args->columnspacing;
+ grid.rowspacing = args->rowspacing;
+ UiContainerX *container = ui_create_container(obj, grid);
+ grid.container = container;
+
+ // add box to the parent
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, grid, &layout);
+
+ // add new box to the obj container chain
+ uic_object_push_container(obj, container);
+
+ return (__bridge void*)grid;
+}
+
+UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
+ NSString *title = args->label ? [[NSString alloc]initWithUTF8String:args->label] : nil;
+ FrameContainer *frame = [[FrameContainer alloc] init:title];
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ui_container_add(obj, frame, &layout);
+
+ // add container to the chain
+ UiContainerX *container;
+ UiLayout subLayout = {0};
+ switch(args->subcontainer) {
+ default: {
+ // UI_CONTAINER_NO_SUB
+ container = ui_create_container(obj, frame);
+ break;
+ }
+ case UI_CONTAINER_VBOX: {
+ BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationVertical spacing:args->spacing];
+ box.translatesAutoresizingMaskIntoConstraints = false;
+ [frame addView:box layout:&subLayout];
+ container = ui_create_container(obj, box);
+ break;
+ }
+ case UI_CONTAINER_HBOX: {
+ BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationHorizontal spacing:args->spacing];
+ box.translatesAutoresizingMaskIntoConstraints = false;
+ [frame addView:box layout:&subLayout];
+ container = ui_create_container(obj, box);
+ break;
+ }
+ case UI_CONTAINER_GRID: {
+ GridLayout *grid = [[GridLayout alloc] init];
+ grid.translatesAutoresizingMaskIntoConstraints = false;
+ grid.columnspacing = args->columnspacing;
+ grid.rowspacing = args->rowspacing;
+ [frame addView:grid layout:&subLayout];
+ container = ui_create_container(obj, grid);
+ break;
+ }
+ }
+
+ uic_object_push_container(obj, container);
+
+ return (__bridge void*)frame;
+}
+
+UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) {
+ return ui_frame_create(obj, args); // TODO
+}
+
+UIWIDGET ui_scrolledwindow_create(UiObject *obj, UiFrameArgs *args) {
+ int colspacing = args->spacing;
+ int rowspacing = args->spacing;
+ if(args->subcontainer == UI_CONTAINER_GRID) {
+ colspacing = args->columnspacing;
+ rowspacing = args->rowspacing;
+ }
+ ScrollViewContainer *scrollview = [[ScrollViewContainer alloc]init:args->subcontainer columnSpacing:colspacing rowSpacing:rowspacing];
+ scrollview.hasVerticalScroller = YES;
+ scrollview.scrollerStyle = NSScrollerStyleOverlay;
+ scrollview.autohidesScrollers = YES;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ui_container_add(obj, scrollview, &layout);
+
+ UiContainerX *container = ui_create_container(obj, scrollview);
+ uic_object_push_container(obj, container);
+
+ return (__bridge void*)scrollview;
+}
+
+UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) {
+ NSView<TabView, Container> *tabview;
+ switch(args->tabview) {
+ default: tabview = [[UiTopTabView alloc]init:obj args:args]; break;
+ }
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ui_container_add(obj, tabview, &layout);
+
+ UiContainerX *container = ui_create_container(obj, tabview);
+ uic_object_push_container(obj, container);
+
+ return (__bridge void*)tabview;
+}
+
+void ui_tab_create(UiObject *obj, const char* title) {
+ UiContainerX *ctn = obj->container_end;
+ id<TabView> tabview = (__bridge id<TabView>)ctn->container;
+ NSString *s = title ? [[NSString alloc]initWithUTF8String:title] : @"";
+ NSView<Container> *sub = [tabview createTab:-1 title:s];
+
+ UiContainerX *container = ui_create_container(obj, sub);
+ uic_object_push_container(obj, container);
+}
+
+void ui_tabview_select(UIWIDGET tabview, int tab) {
+ id<TabView> tabv = (__bridge id<TabView>)tabview;
+ [tabv selectTab:tab];
+}
+
+void ui_tabview_remove(UIWIDGET tabview, int tab) {
+ id<TabView> tabv = (__bridge id<TabView>)tabview;
+ [tabv removeTab:tab];
+}
+
+UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
+ id<TabView> tabv = (__bridge id<TabView>)tabview;
+ NSString *s = name ? [[NSString alloc]initWithUTF8String:name] : @"";
+ return [tabv addTab:tab_index title:s];
+}
+
+
+UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) {
+ return NULL; // TODO
+}
+
+UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) {
+ return NULL; // TODO
+}
+
+UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) {
+ return NULL; // TODO
+}
+
+UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) {
+ return NULL; // TODO
+}
+
+
+
+void ui_container_begin_close(UiObject *obj) {
+ UiContainerX *ct = obj->container_end;
+ ct->close = 1;
+}
+
+int ui_container_finish(UiObject *obj) {
+ UiContainerX *ct = obj->container_end;
+ if(ct->close) {
+ ui_end_new(obj);
+ return 0;
+ }
+ return 1;
+}
+
+/* -------------------------- Frame Container -------------------------- */
+
+@implementation FrameContainer
+
+@synthesize container = _container;
+
+- (id)init:(NSString*)title {
+ self = [super init];
+ self.title = title;
+ self.boxType = NSBoxPrimary;
+ if(title != nil) {
+ self.titlePosition = NSAtTop;
+ }
+ return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+ [self.contentView addSubview:view];
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ [NSLayoutConstraint activateConstraints:@[
+ [view.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0],
+ [view.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:0],
+ [view.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-0],
+ [view.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-0]
+ ]];
+}
+
+@end
+
+
+/* -------------------------- Expander Container -------------------------- */
+
+// TODO
+
+
+/* ------------------------ ScrollView Container ------------------------ */
+
+@implementation ScrollViewContainer
+
+@synthesize container = _container;
+
+- (id)init:(UiSubContainerType)subContainer columnSpacing:(int)columnSpacing rowSpacing:(int)rowSpacing {
+ self = [super init];
+
+ if(subContainer != UI_CONTAINER_NO_SUB) {
+ GridLayout *child;
+ switch(subContainer) {
+ default:
+ case UI_CONTAINER_VBOX: {
+ child = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationVertical spacing:columnSpacing];
+ break;
+ }
+ case UI_CONTAINER_HBOX: {
+ child = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationHorizontal spacing:columnSpacing];
+ break;
+ }
+ case UI_CONTAINER_GRID: {
+ child = [[GridLayout alloc]init];
+ child.columnspacing = columnSpacing;
+ child.rowspacing = rowSpacing;
+ break;
+ }
+ }
+ child.translatesAutoresizingMaskIntoConstraints = NO;
+
+ self.documentView = child;
+ [child.widthAnchor constraintEqualToAnchor:self.contentView.widthAnchor].active = YES;
+
+ _child = child;
+ }
+
+
+ return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+ if(_child != nil) {
+ _child.container = self.container; // required, otherwise child has no container and can't access the newline property
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ [_child addView:view layout:layout];
+ } else {
+ self.documentView = view;
+ [view.widthAnchor constraintEqualToAnchor:self.contentView.widthAnchor].active = YES;
+ }
+}
+
+@end
+
+/* ------------------------- private functions ------------------------- */
+
+UiContainerX* ui_create_container(UiObject *obj, id<Container> container) {
+ UiContainerX *ctn = ui_malloc(obj->ctx, sizeof(UiContainerX));
+ ctn->container = (__bridge void*)container;
+ ctn->close = 0;
+ ctn->prev = NULL;
+ ctn->next = NULL;
+ container.container = ctn;
+ return ctn;
+}
+
+void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout) {
+ UiContainerX *ctn = obj->container_end;
+ id<Container> container = (__bridge id<Container>)ctn->container;
+ UiLayout adjustedLayout = *layout;
+ if(adjustedLayout.margin > 0) {
+ adjustedLayout.margin_left = adjustedLayout.margin;
+ adjustedLayout.margin_right = adjustedLayout.margin;
+ adjustedLayout.margin_top = adjustedLayout.margin;
+ adjustedLayout.margin_bottom = adjustedLayout.margin;
+ }
+ [container addView:view layout:&adjustedLayout];
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "Container.h"
+
+#import "../ui/entry.h"
+
+@interface UiSpinBox : NSObject<NSTextFieldDelegate>
+
+@property UiObject *obj;
+@property (weak) NSTextField *textfield;
+@property (weak) NSStepper *stepper;
+@property BOOL isInteger;
+@property ui_callback onchange;
+@property void* onchangedata;
+@property UiObserver **observers;
+
+- (UiSpinBox*)init;
+
+- (void)valueChanged;
+- (void)stepperChanged:(id)sender;
+- (void) controlTextDidChange:(NSNotification *) obj;
+
+@end
+
+int64_t ui_spinbutton_getint(UiInteger *i);
+void ui_spinbutton_setint(UiInteger *i, int64_t val);
+
+double ui_spinbutton_getdouble(UiDouble *d);
+void ui_spinbutton_setdouble(UiDouble *d, double val);
+
+double ui_spinbutton_getrangeval(UiRange *r);
+void ui_spinbutton_setrangeval(UiRange *r, double val);
+void ui_spinbutton_setrange(UiRange *r, double min, double max);
+void ui_spinbutton_setextent(UiRange *r, double extent);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "entry.h"
+#import <objc/runtime.h>
+
+@implementation UiSpinBox
+
+- (UiSpinBox*)init {
+ return self;
+}
+
+- (void)valueChanged {
+ float value = _stepper.doubleValue;
+ UiEvent e;
+ e.obj = _obj;
+ e.window = e.obj->window;
+ e.document = e.obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = (int)value;
+ e.set = ui_get_setop();
+
+ if(_onchange) {
+ _onchange(&e, _onchangedata);
+ }
+
+ if(_observers) {
+ UiObserver *observer = *_observers;
+ ui_notify_evt(observer, &e);
+ }
+}
+
+- (void)stepperChanged:(id)sender {
+ if(_isInteger) {
+ _textfield.integerValue = _stepper.integerValue;
+ } else {
+ _textfield.doubleValue = _stepper.doubleValue;
+ }
+ [self valueChanged];
+}
+
+- (void) controlTextDidChange:(NSNotification *)obj {
+ if(_isInteger) {
+ _stepper.integerValue = _textfield.integerValue;
+ } else {
+ _stepper.doubleValue = _textfield.doubleValue;
+ }
+ [self valueChanged];
+}
+
+@end
+
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
+ double min = args->min;
+ double max = args->max != 0 ? args->max : 1000;
+
+ UiVar *var = NULL;
+ UiVarType vartype = 0;
+ if(args->varname) {
+ var = uic_get_var(obj->ctx, args->varname);
+ if(var) {
+ vartype = var->type;
+ } else {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, args->varname, UI_VAR_RANGE);
+ vartype = UI_VAR_RANGE;
+ }
+ }
+
+ if(!var) {
+ if(args->intvalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+ vartype = UI_VAR_INTEGER;
+ } else if(args->doublevalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+ vartype = UI_VAR_DOUBLE;
+ } else if(args->rangevalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+ vartype = UI_VAR_RANGE;
+ }
+ }
+
+ if(vartype == UI_VAR_RANGE) {
+ UiRange *r = var->value;
+ min = r->min;
+ max = r->max;
+ }
+ if(args->step == 0) {
+ args->step = 1;
+ }
+
+ // create and setup textfield for number input
+ NSTextField *textfield = [[NSTextField alloc] init];
+ textfield.translatesAutoresizingMaskIntoConstraints = NO;
+
+ if(!args->hfill || args->width > 0) {
+ textfield.translatesAutoresizingMaskIntoConstraints = NO;
+ int width = args->width > 0 ? args->width : 100;
+ [[textfield.widthAnchor constraintEqualToConstant:width] setActive:YES];
+ }
+
+ NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
+ formatter.numberStyle = NSNumberFormatterDecimalStyle;
+ formatter.allowsFloats = vartype != UI_VAR_INTEGER;
+ formatter.minimumFractionDigits = args->digits;
+ formatter.maximumFractionDigits = args->digits;
+
+ textfield.formatter = formatter;
+
+ // create view containing the textfield and stepper
+ NSView *view = [[NSView alloc]init];
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+
+ NSStepper *stepper = [[NSStepper alloc] init];
+ stepper.translatesAutoresizingMaskIntoConstraints = NO;
+
+ [view addSubview:textfield];
+ [view addSubview:stepper];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [textfield.leadingAnchor constraintEqualToAnchor:view.leadingAnchor],
+ [textfield.topAnchor constraintEqualToAnchor:view.topAnchor],
+ [textfield.bottomAnchor constraintEqualToAnchor:view.bottomAnchor],
+
+ [stepper.trailingAnchor constraintEqualToAnchor:view.trailingAnchor],
+ [stepper.topAnchor constraintEqualToAnchor:view.topAnchor],
+ [stepper.bottomAnchor constraintEqualToAnchor:view.bottomAnchor],
+
+ [textfield.trailingAnchor constraintEqualToAnchor:stepper.leadingAnchor]
+ ]];
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, view, &layout);
+
+ // create the spinbox object, that handles all textfield and stepper events
+ UiSpinBox *spinbox = [[UiSpinBox alloc]init];
+ spinbox.obj = obj;
+ spinbox.textfield = textfield;
+ spinbox.stepper = stepper;
+ spinbox.onchange = args->onchange;
+ spinbox.onchangedata = args->onchangedata;
+ spinbox.isInteger = vartype == UI_VAR_INTEGER;
+ objc_setAssociatedObject(stepper, "ui_spinbox", spinbox, OBJC_ASSOCIATION_RETAIN);
+
+ stepper.minValue = min;
+ stepper.maxValue = max;
+ stepper.increment = args->step;
+ stepper.target = spinbox;
+ stepper.action = @selector(stepperChanged:);
+ textfield.delegate = spinbox;
+
+ UiObserver **obs = NULL;
+ if(var) {
+ void *varObj = (__bridge void*)spinbox;
+ switch(vartype) {
+ default: break;
+ case UI_VAR_INTEGER: {
+ UiInteger *i = var->value;
+ i->get = ui_spinbutton_getint;
+ i->set = ui_spinbutton_setint;
+ i->obj = varObj;
+ obs = &i->observers;
+
+ stepper.integerValue = i->value;
+ textfield.integerValue = i->value;
+ break;
+ }
+ case UI_VAR_DOUBLE: {
+ UiDouble *d = var->value;
+ d->get = ui_spinbutton_getdouble;
+ d->set = ui_spinbutton_setdouble;
+ d->obj = varObj;
+ obs = &d->observers;
+
+ stepper.doubleValue = d->value;
+ textfield.doubleValue = d->value;
+ break;
+ }
+ case UI_VAR_RANGE: {
+ UiRange *r = var->value;
+ r->get = ui_spinbutton_getrangeval;
+ r->set = ui_spinbutton_setrangeval;
+ r->setrange = ui_spinbutton_setrange;
+ r->setextent = ui_spinbutton_setextent;
+ r->obj = varObj;
+ obs = &r->observers;
+
+ stepper.doubleValue = r->value;
+ textfield.doubleValue = r->value;
+ break;
+ }
+ }
+ }
+ spinbox.observers = obs;
+
+ return (__bridge void*)textfield;
+}
+
+int64_t ui_spinbutton_getint(UiInteger *i) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)i->obj;
+ i->value = spinbox.stepper.integerValue;
+ return i->value;
+}
+
+void ui_spinbutton_setint(UiInteger *i, int64_t val) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)i->obj;
+ i->value = val;
+ spinbox.stepper.integerValue = val;
+ spinbox.textfield.integerValue = val;
+}
+
+double ui_spinbutton_getdouble(UiDouble *d) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)d->obj;
+ d->value = spinbox.stepper.doubleValue;
+ return d->value;
+}
+
+void ui_spinbutton_setdouble(UiDouble *d, double val) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)d->obj;
+ d->value = val;
+ spinbox.stepper.doubleValue = val;
+ spinbox.textfield.doubleValue = val;
+}
+
+double ui_spinbutton_getrangeval(UiRange *r) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+ r->value = spinbox.stepper.doubleValue;
+ return r->value;
+}
+
+void ui_spinbutton_setrangeval(UiRange *r, double val) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+ r->value = val;
+ spinbox.stepper.doubleValue = val;
+ spinbox.textfield.doubleValue = val;
+}
+
+void ui_spinbutton_setrange(UiRange *r, double min, double max) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+ spinbox.stepper.minValue = min;
+ spinbox.stepper.maxValue = max;
+}
+
+void ui_spinbutton_setextent(UiRange *r, double extent) {
+ UiSpinBox *spinbox = (__bridge UiSpinBox*)r->obj;
+ spinbox.stepper.increment = extent;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "../ui/image.h"
+
+#include "Container.h"
+
+void ui_icon_init(void);
+
+NSImage* ui_cocoa_named_icon(const char *name);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "image.h"
+
+static NSDictionary *standardIconNames;
+
+void ui_icon_init(void) {
+ standardIconNames = @{
+ @"NSImageNameActionTemplate": NSImageNameActionTemplate,
+ @"NSImageNameAddTemplate": NSImageNameAddTemplate,
+ @"NSImageNameAdvanced": NSImageNameAdvanced,
+ @"NSImageNameApplicationIcon": NSImageNameApplicationIcon,
+ @"NSImageNameBluetoothTemplate": NSImageNameBluetoothTemplate,
+ @"NSImageNameBonjour": NSImageNameBonjour,
+ @"NSImageNameBookmarksTemplate": NSImageNameBookmarksTemplate,
+ @"NSImageNameCaution": NSImageNameCaution,
+ // TODO
+ @"NSImageNameRefreshTemplate": NSImageNameRefreshTemplate,
+ @"NSImageNameFolder": NSImageNameFolder,
+ @"NSImageNameGoForwardTemplate": NSImageNameGoForwardTemplate,
+ @"NSImageNameGoBackTemplate": NSImageNameGoBackTemplate
+ };
+}
+
+NSImage* ui_cocoa_named_icon(const char *name) {
+ NSString *imageName = [[NSString alloc] initWithUTF8String:name];
+ NSString *imgName = [standardIconNames objectForKey:imageName];
+ if(imgName) {
+ imageName = imgName;
+ }
+ return [NSImage imageNamed:imageName];
+}
+
+
+void ui_image_ref(UIIMAGE img) {
+ // TODO
+}
+
+void ui_image_unref(UIIMAGE img) {
+ // TODO
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "../ui/display.h"
+
+char* ui_label_get(UiString *s);
+void ui_label_set(UiString *s, const char *str);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "label.h"
+#import "Container.h"
+
+#import <string.h>
+
+static UIWIDGET create_label(UiObject *obj, UiLabelArgs *args) {
+ NSTextField *label = [[NSTextField alloc] init];
+ label.editable = NO;
+ label.bezeled = NO;
+ label.drawsBackground = NO;
+ label.selectable = NO;
+
+ NSTextAlignment alignment;
+ switch(args->align) {
+ case UI_ALIGN_LEFT: alignment = NSTextAlignmentLeft; break;
+ case UI_ALIGN_RIGHT: alignment = NSTextAlignmentRight; break;
+ default: alignment = NSTextAlignmentCenter;
+ }
+ label.alignment = alignment;
+
+ if(args->label) {
+ NSString *str = [[NSString alloc] initWithUTF8String:args->label];
+ label.stringValue = str;
+ }
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ui_container_add(obj, label, &layout);
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiString *s = var->value;
+ s->obj = (__bridge void*)label;
+ s->get = ui_label_get;
+ s->set = ui_label_set;
+
+ if(s->value.ptr) {
+ label.stringValue = [[NSString alloc]initWithUTF8String:s->value.ptr];
+ }
+ }
+
+ return (__bridge void*)label;
+}
+
+
+UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs *args) {
+ return create_label(obj, args);
+}
+
+UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs *args) {
+ args->align = UI_ALIGN_LEFT;
+ return create_label(obj, args);
+}
+
+UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs *args) {
+ args->align = UI_ALIGN_RIGHT;
+ return create_label(obj, args);
+}
+
+
+char* ui_label_get(UiString *s) {
+ NSTextField *label = (__bridge NSTextField*)s->obj;
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ s->value.ptr = strdup(label.stringValue.UTF8String);
+ return s->value.ptr;
+}
+
+void ui_label_set(UiString *s, const char *str) {
+ NSTextField *label = (__bridge NSTextField*)s->obj;
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ s->value.ptr = NULL;
+ label.stringValue = [[NSString alloc] initWithUTF8String:str];
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "Container.h"
+#import "../ui/tree.h"
+
+#import "ListDataSource.h"
+
+@interface UiDropDown : NSObject<NSComboBoxDelegate>
+
+@property UiObject *obj;
+@property ui_callback onactivate;
+@property void *onactivatedata;
+@property ui_callback onselection;
+@property void *onselectiondata;
+@property ui_getvaluefunc2 getvalue;
+@property void *getvaluedata;
+@property UiVar *var;
+@property (weak) NSComboBox *combobox;
+
+- (id)init:(UiObject*)obj;
+
+@end
+
+void ui_tableview_update(UiList *list, int i);
+UiListSelection ui_tableview_getselection(UiList *list);
+void ui_tableview_setselection(UiList *list, UiListSelection selection);
+
+void ui_dropdown_update(UiList *list, int i);
+UiListSelection ui_dropdown_getselection(UiList *list);
+void ui_dropdown_setselection(UiList *list, UiListSelection selection);
+
+@class UiSourceList;
+
+@interface UiSourceListItem : NSObject
+@property (weak) UiSourceList *sourcelist;
+@property (weak) UiSourceListItem *parent;
+@property (strong) NSString *label;
+@property (strong) NSString *badge;
+
+@property (strong) NSMutableArray<UiSourceListItem*> *items;
+@property UiVar *var;
+@property UiSubList *sublist;
+
+@property int sublistIndex;
+@property int sublistStartRow;
+@property int rownum;
+
+@property void *eventdata;
+
+/*
+ * Initialize a section item
+ */
+- (id)init:(UiSubListItem*)item parent:(UiSourceListItem*)parent;
+/*
+ * Initialize a child item
+ */
+- (id)init:(UiSourceList*)sourcelist sublist:(UiSubList*)sublist;
+- (BOOL)isSection;
+- (void)update:(int)row;
+
+@end
+
+
+@interface UiSourceList : NSObject <NSOutlineViewDataSource, NSOutlineViewDelegate>
+
+@property UiObject *obj;
+@property (weak) NSOutlineView *outlineView;
+@property CxList *sublists;
+@property UiVar *dynamic_sublists;
+@property ui_sublist_getvalue_func getvalue;
+@property void *getvaluedata;
+@property ui_callback onactivate;
+@property void *onactivatedata;
+@property ui_callback onbuttonclick;
+@property void *onbuttonclickdata;
+
+@property (strong) NSMutableArray<UiSourceListItem*> *sections;
+
+- (id)init:(UiObject*)obj outline:(NSOutlineView*)view;
+
+- (void)update:(int)row;
+
+@end
+
+@interface UiSourceListRow : NSTableRowView
+
+@property NSTrackingArea *trackingArea;
+@property NSView *disclosureButton;
+@property BOOL hover;
+@property BOOL showDisclosureButton;
+
+@end
+
+void ui_sourcelist_update(UiList *list, int row);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "list.h"
+#import "ListDelegate.h"
+#import <objc/runtime.h>
+
+#import <inttypes.h>
+#import <limits.h>
+
+#import <cx/array_list.h>
+
+static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
+ return getvalue(elm, col);
+}
+
+static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ return elm;
+}
+
+/* --------------------------- ListView --------------------------- */
+
+/*
+ * adds a NSTableViewDelegate that handles all events and calls
+ * callbacks specified in the UiListArgs
+ */
+static void add_listdelegate(UiObject *obj, NSTableView *tableview, UiListArgs *args) {
+ ListDelegate *delegate = [[ListDelegate alloc] init:tableview obj:obj];
+ delegate.onactivate = args->onactivate;
+ delegate.onactivatedata = args->onactivatedata;
+ delegate.onselection = args->onselection;
+ delegate.onselectiondata = args->onselectiondata;
+ tableview.delegate = delegate;
+ objc_setAssociatedObject(tableview, "ui_listdelegate", delegate, OBJC_ASSOCIATION_RETAIN);
+ tableview.doubleAction = @selector(activateEvent:);
+ tableview.target = delegate;
+}
+
+static void bind_list_to_tableview(UiList *list, NSTableView *tableview) {
+ list->obj = (__bridge void*)tableview;
+ list->update = ui_tableview_update;
+ list->getselection = ui_tableview_getselection;
+ list->setselection = ui_tableview_setselection;
+}
+
+UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
+ NSScrollView *scrollview = [[NSScrollView alloc] init];
+
+ NSTableView *tableview = [[NSTableView alloc] init];
+ tableview.autoresizingMask = NSViewWidthSizable;
+ tableview.headerView = nil;
+
+ if(args->multiselection) {
+ tableview.allowsMultipleSelection = YES;
+ }
+
+ scrollview.documentView = tableview;
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, scrollview, &layout);
+
+ add_listdelegate(obj, tableview, args);
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+ if(var) {
+ UiList *list = var->value;
+ bind_list_to_tableview(list, tableview);
+
+ ui_getvaluefunc2 getvalue = args->getvalue2;
+ void *getvaluedata = args->getvalue2data;
+ if(!getvalue) {
+ if(args->getvalue) {
+ getvalue = getvalue_wrapper;
+ getvaluedata = (void*)args->getvalue;
+ } else {
+ getvalue = str_getvalue; // by default list values are interpreted as strings
+ }
+ }
+
+ NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"column"];
+ [tableview addTableColumn:column];
+
+ ListDataSource *dataSource = [[ListDataSource alloc] init:tableview.tableColumns var:var getvalue:getvalue getvaluedata:getvaluedata];
+
+ tableview.dataSource = dataSource;
+ [tableview reloadData];
+
+ objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ return (__bridge void*)scrollview;
+}
+
+/* --------------------------- TableView --------------------------- */
+
+UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
+ NSScrollView *scrollview = [[NSScrollView alloc] init];
+
+ NSTableView *tableview = [[NSTableView alloc] init];
+ tableview.autoresizingMask = NSViewWidthSizable;
+ tableview.columnAutoresizingStyle = NSTableViewSequentialColumnAutoresizingStyle;
+
+ if(args->multiselection) {
+ tableview.allowsMultipleSelection = YES;
+ }
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, scrollview, &layout);
+
+ add_listdelegate(obj, tableview, args);
+
+ // convert model
+ NSMutableArray<NSTableColumn*> *cols = [[NSMutableArray alloc] init];
+ UiModel *model = args->model;
+ if(model) {
+ for(int i=0;i<model->columns;i++) {
+ char *title = model->titles[i];
+ UiModelType type = model->types[i];
+ int width = model->columnsize[i];
+ NSString *identifier = [[NSString alloc] initWithUTF8String:title];
+ NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:identifier];
+ column.title = identifier;
+ column.resizingMask = NSTableColumnUserResizingMask;
+ if(width > 0) {
+ column.width = width;
+ } else if(width < 0) {
+ column.resizingMask = NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask;
+ }
+ if(type >= UI_ICON) {
+ // TODO
+ }
+ [tableview addTableColumn:column];
+ [cols addObject:column];
+ }
+ }
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+ if(var) {
+ UiList *list = var->value;
+ bind_list_to_tableview(list, tableview);
+
+ ui_getvaluefunc2 getvalue = args->getvalue2;
+ void *getvaluedata = args->getvalue2data;
+ if(!getvalue) {
+ if(args->getvalue) {
+ getvalue = getvalue_wrapper;
+ getvaluedata = (void*)args->getvalue;
+ } else {
+ fprintf(stderr, "Error: tableview requires getvalue or getvalue2 func\n");
+ return (__bridge void*)scrollview;
+ }
+ }
+
+ ListDataSource *dataSource = [[ListDataSource alloc] init:cols var:var getvalue:getvalue getvaluedata:getvaluedata];
+ if(model) {
+ dataSource.model = ui_model_copy(obj->ctx, model);
+ }
+
+ tableview.dataSource = dataSource;
+ [tableview reloadData];
+
+ objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ scrollview.documentView = tableview;
+
+ return (__bridge void*)scrollview;
+}
+
+/* ------ common functions ------ */
+
+void ui_tableview_update(UiList *list, int i) {
+ NSTableView *tableview = (__bridge NSTableView*)list->obj;
+ if(i < 0) {
+ [tableview reloadData];
+ } else {
+ [tableview reloadData]; // TODO: optimize
+ }
+}
+
+UiListSelection ui_tableview_getselection(UiList *list) {
+ NSTableView *tableview = (__bridge NSTableView*)list->obj;
+ return ui_tableview_selection(tableview);
+}
+
+void ui_tableview_setselection(UiList *list, UiListSelection selection) {
+ NSTableView *tableview = (__bridge NSTableView*)list->obj;
+ NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
+ for(int i=0;i<selection.count;i++) {
+ [indexSet addIndex:selection.rows[i]];
+ }
+ [tableview selectRowIndexes:indexSet byExtendingSelection:NO];
+}
+
+
+/* --------------------------- DropDown --------------------------- */
+
+@implementation UiDropDown
+
+- (id)init:(UiObject*)obj {
+ _obj = obj;
+ return self;
+}
+
+- (void) comboBoxSelectionDidChange:(NSNotification *) notification {
+ int index = (int)_combobox.indexOfSelectedItem;
+
+ void *eventdata = NULL;
+ if(_var) {
+ UiList *list = _var->value;
+ if(index >= 0) {
+ eventdata = list->get(list, index);
+ }
+ } else {
+ NSString *str = _combobox.objectValueOfSelectedItem;
+ if(str) {
+ eventdata = (void*)str.UTF8String;
+ }
+ }
+
+ UiEvent event;
+ event.obj = _obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = eventdata;
+ event.eventdatatype = UI_EVENT_DATA_LIST_ELM;
+ event.intval = index;
+
+ if(_onselection) {
+ _onselection(&event, _onselectiondata);
+ }
+
+ if(_onactivate) {
+ _onactivate(&event, _onactivatedata);
+ }
+}
+
+@end
+
+UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
+ NSComboBox *dropdown = [[NSComboBox alloc] init];
+ dropdown.editable = NO;
+
+ UiDropDown *uidropdown = [[UiDropDown alloc] init:obj];
+ objc_setAssociatedObject(dropdown, "ui_dropdown", uidropdown, OBJC_ASSOCIATION_RETAIN);
+ uidropdown.onactivate = args->onactivate;
+ uidropdown.onactivatedata = args->onactivatedata;
+ uidropdown.onselection = args->onselection;
+ uidropdown.onselectiondata = args->onselectiondata;
+ uidropdown.combobox = dropdown;
+
+ if(!args->getvalue2) {
+ if(args->getvalue) {
+ args->getvalue2 = getvalue_wrapper;
+ args->getvalue2data = (void*)args->getvalue;
+ } else {
+ args->getvalue2 = str_getvalue;
+ }
+ }
+ uidropdown.getvalue = args->getvalue2;
+ uidropdown.getvaluedata = args->getvalue2data;
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, dropdown, &layout);
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+ if(var) {
+ UiList *list = var->value;
+ list->obj = (__bridge void*)dropdown;
+ list->update = ui_dropdown_update;
+ list->getselection = ui_dropdown_getselection;
+ list->setselection = ui_dropdown_setselection;
+ ui_dropdown_update(list, -1);
+ } else {
+ for(int i=0;i<args->static_nelm;i++) {
+ char *str = args->static_elements[i];
+ NSString *item = [[NSString alloc] initWithUTF8String:str];
+ [dropdown addItemWithObjectValue:item];
+ }
+ }
+
+ uidropdown.var = var;
+
+ return (__bridge void*)dropdown;
+}
+
+void ui_dropdown_update(UiList *list, int i) {
+ NSComboBox *combobox = (__bridge NSComboBox*)list->obj;
+ UiDropDown *dropdown = objc_getAssociatedObject(combobox, "ui_dropdown");
+ if(dropdown) {
+ [combobox removeAllItems];
+
+ ui_getvaluefunc2 getvalue = dropdown.getvalue;
+ void *getvaluedata = dropdown.getvaluedata;
+
+ int index = 0;
+ void *elm = list->first(list);
+ while(elm) {
+ UiBool freeResult = FALSE;
+ char *str = getvalue(list, elm, index, 0, getvaluedata, &freeResult);
+ if(str) {
+ NSString *item = [[NSString alloc] initWithUTF8String:str];
+ [combobox addItemWithObjectValue:item];
+ }
+ if(freeResult) {
+ free(str);
+ }
+ elm = list->next(list);
+ index++;
+ }
+ } else {
+ fprintf(stderr, "Error: obj is not a dropdown\n");
+ }
+}
+
+UiListSelection ui_dropdown_getselection(UiList *list) {
+ UiListSelection sel = { 0, NULL };
+ NSComboBox *combobox = (__bridge NSComboBox*)list->obj;
+ NSInteger index = combobox.indexOfSelectedItem;
+ if(index >= 0) {
+ sel.rows = malloc(sizeof(int));
+ sel.count = 1;
+ sel.rows[0] = (int)index;
+ }
+ return sel;
+}
+
+void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
+ NSComboBox *combobox = (__bridge NSComboBox*)list->obj;
+ if(selection.count > 0) {
+ [combobox selectItemAtIndex:selection.rows[0]];
+ } else {
+ [combobox selectItemAtIndex: -1];
+ }
+}
+
+
+/* --------------------------- SourceList --------------------------- */
+
+static ui_sourcelist_update_func sclist_update_callback = NULL;
+
+void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb) {
+ sclist_update_callback = cb;
+}
+
+void ui_sourcelist_updated(void) {
+ if(sclist_update_callback) {
+ sclist_update_callback();
+ }
+}
+
+static void sublist_free(const CxAllocator *a, UiSubList *sl) {
+ cxFree(a, (char*)sl->varname);
+ cxFree(a, (char*)sl->header);
+}
+
+static UiSubList copy_sublist(const CxAllocator *a, UiSubList *sl) {
+ UiSubList new_sl;
+ new_sl.value = sl->value;
+ new_sl.varname = sl->varname ? cx_strdup_a(a, cx_str(sl->varname)).ptr : NULL;
+ new_sl.header = sl->header ? cx_strdup_a(a, cx_str(sl->header)).ptr : NULL;
+ new_sl.separator = sl->separator;
+ new_sl.userdata = sl->userdata;
+ return new_sl;
+}
+
+static CxList* copy_sublists(const CxAllocator *a, UiSourceListArgs *args) {
+ if(args->sublists) {
+ size_t max = args->numsublists;
+ if(max == 0) {
+ max = INT_MAX;
+ }
+
+ CxList *sublists = cxArrayListCreate(a, NULL, sizeof(UiSubList), args->numsublists);
+ sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_free;
+
+ for(int i=0;i<max;i++) {
+ UiSubList *sl = &args->sublists[i];
+ if(sl->value == NULL && sl->varname == NULL) {
+ break;
+ }
+
+ UiSubList new_sl = copy_sublist(a, sl);
+ cxListAdd(sublists, &new_sl);
+ }
+
+ return sublists;
+ }
+ return NULL;
+}
+
+UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
+ // create views
+ NSScrollView *scrollview = [[NSScrollView alloc] init];
+ scrollview.autoresizingMask = NSViewWidthSizable;
+ scrollview.hasVerticalScroller = YES;
+ scrollview.hasHorizontalScroller = NO;
+ scrollview.autohidesScrollers = YES;
+
+ NSOutlineView *outline = [[NSOutlineView alloc]init];
+ NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"x"];
+ [outline addTableColumn:column];
+ outline.outlineTableColumn = column;
+ outline.headerView = NULL;
+ outline.rowSizeStyle = NSTableViewRowSizeStyleDefault;
+ outline.usesAutomaticRowHeights = YES;
+ outline.indentationPerLevel = 0;
+
+ outline.style = NSTableViewStyleSourceList;
+
+ // Make background transparent so vibrancy shows through
+ scrollview.drawsBackground = NO;
+
+ scrollview.documentView = outline;
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ui_container_add(obj, scrollview, &layout);
+
+ // datasource and delegate
+ UiSourceList *data = [[UiSourceList alloc] init:obj outline:outline];
+ data.sublists = copy_sublists(obj->ctx->allocator, args);
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST);
+ if(var) {
+ UiList *list = var->value;
+ list->obj = (__bridge void*)data;
+ list->update = ui_sourcelist_update;
+ }
+ data.dynamic_sublists = var;
+ data.getvalue = args->getvalue;
+ data.getvaluedata = args->getvaluedata;
+ data.onactivate = args->onactivate;
+ data.onactivatedata = args->onactivatedata;
+ data.onbuttonclick = args->onbuttonclick;
+ data.onactivatedata = args->onbuttonclickdata;
+ [data update:-1];
+
+ outline.dataSource = data;
+ outline.delegate = data;
+
+ [data update:-1];
+
+ objc_setAssociatedObject(outline, "ui_datasource", data, OBJC_ASSOCIATION_RETAIN);
+
+ return (__bridge void*)scrollview;
+}
+
+void ui_sourcelist_update(UiList *list, int row) {
+ UiSourceList *sourcelist = (__bridge UiSourceList*)list->obj;
+ [sourcelist update:row];
+}
+
+
+/*
+ * Data Source and Delegate for the sourcelist NSOutlineView
+ */
+@implementation UiSourceList
+
+- (id)init:(UiObject*)obj outline:(NSOutlineView*)view {
+ _obj = obj;
+ _outlineView = view;
+ _sections = [[NSMutableArray alloc] initWithCapacity:16];
+ return self;
+}
+
+- (void)dealloc {
+ cxListFree(_sublists);
+}
+
+- (void)update:(int)row {
+ // TODO: check row
+
+ [_sections removeAllObjects];
+
+ CxIterator i = cxListIterator(_sublists);
+ int index = 0;
+ int rownum = 0;
+ cx_foreach(UiSubList *, sl, i) {
+ UiSourceListItem *section = [[UiSourceListItem alloc] init:self sublist:sl];
+ section.sublistIndex = index;
+ section.rownum = rownum;
+ section.sublistStartRow = rownum;
+ [section update:-1];
+ [_sections addObject:section];
+ index++;
+ rownum += 1 + section.items.count;
+ }
+
+ [_outlineView reloadData];
+ [_outlineView expandItem:nil expandChildren:YES];
+}
+
+// NSOutlineViewDataSource implementation
+
+- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
+ if(item == nil) {
+ return _sections.count;
+ } else {
+ UiSourceListItem *i = item;
+ return i.items.count;
+ }
+}
+
+- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
+ UiSourceListItem *i = item;
+ return [i isSection] ? YES : NO;
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
+ UiSourceListItem *i = item;
+ if(i) {
+ return [i.items objectAtIndex:index];
+ }
+ return [_sections objectAtIndex:index];
+}
+
+- (void)outlineView:(NSOutlineView *)outlineView
+ setObjectValue:(id)object
+ forTableColumn:(NSTableColumn *)tableColumn
+ byItem:(id)item
+{
+
+}
+
+// NSOutlineViewDelegate implementation
+
+- (NSView *)outlineView:(NSOutlineView *)outlineView
+ viewForTableColumn:(NSTableColumn *)tableColumn
+ item:(id)item
+{
+ UiSourceListItem *i = item;
+
+ NSTableCellView *cell = [[NSTableCellView alloc] init];
+ cell.identifier = @"cell";
+ // Icon
+ NSImageView *iconView = [[NSImageView alloc] initWithFrame:NSZeroRect];
+ iconView.translatesAutoresizingMaskIntoConstraints = NO;
+ [cell addSubview:iconView];
+ cell.imageView = iconView;
+
+ // Label
+ //NSTextField *textField = [NSTextField labelWithString:@""];
+ NSTextField *textField = [[NSTextField alloc] initWithFrame:NSZeroRect];
+ textField.translatesAutoresizingMaskIntoConstraints = NO;
+ textField.bezeled = NO;
+ textField.editable = NO;
+ textField.drawsBackground = NO;
+ textField.selectable = NO;
+ textField.lineBreakMode = NSLineBreakByTruncatingTail;
+
+
+ [cell addSubview:textField];
+ cell.textField = textField;
+
+ if([i isSection]) {
+ NSFont *font = [NSFont boldSystemFontOfSize:[NSFont systemFontSize]*0.85];
+ //NSFont *font = [NSFont preferredFontForTextStyle:NSFontTextStyleCaption1 options:@{}];
+ NSDictionary *attrs = @{
+ NSFontAttributeName: font,
+ NSForegroundColorAttributeName: [NSColor tertiaryLabelColor]
+ };
+ textField.attributedStringValue = [[NSAttributedString alloc] initWithString:i.label attributes:attrs];
+
+ // Layout constraints
+ [NSLayoutConstraint activateConstraints:@[
+ [iconView.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+ [iconView.bottomAnchor constraintEqualToAnchor:cell.bottomAnchor constant:-1],
+
+ [textField.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+ [textField.bottomAnchor constraintEqualToAnchor:cell.bottomAnchor constant:-1],
+ [textField.trailingAnchor constraintEqualToAnchor:cell.trailingAnchor constant:0],
+ ]];
+ } else {
+ textField.stringValue = i.label;
+
+ // Layout constraints
+ [NSLayoutConstraint activateConstraints:@[
+ [iconView.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+ [iconView.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
+
+ [textField.leadingAnchor constraintEqualToAnchor:cell.leadingAnchor constant:0],
+ [textField.centerYAnchor constraintEqualToAnchor:cell.centerYAnchor],
+ [textField.trailingAnchor constraintEqualToAnchor:cell.trailingAnchor constant:0],
+ ]];
+ }
+
+ return cell;
+}
+
+- (NSTableRowView *) outlineView:(NSOutlineView *) outlineView
+ rowViewForItem:(id)item {
+ UiSourceListItem *it = item;
+ UiSourceListRow *row = [[UiSourceListRow alloc]init];
+ if([it isSection] && it.sublist->header) {
+ row.showDisclosureButton = YES;
+ }
+ return row;
+}
+
+- (BOOL) outlineView:(NSOutlineView *) outlineView
+ shouldSelectItem:(id)item
+{
+ UiSourceListItem *i = item;
+ return [i isSection] ? NO : YES;
+}
+
+- (CGFloat) outlineView:(NSOutlineView *) outlineView
+ heightOfRowByItem:(id) item
+{
+ UiSourceListItem *i = item;
+ CGFloat rowHeight = outlineView.rowHeight;
+ if([i isSection]) {
+ if(i.sublist->header) {
+ rowHeight += i.sublistIndex == 0 ? -12 : 4;
+ } else {
+ rowHeight = i.sublistIndex == 0 ? 0.1 : 12;
+ }
+ }
+ return rowHeight;
+}
+
+- (void) outlineViewSelectionDidChange:(NSNotification *) notification {
+ UiEvent event;
+ event.obj = _obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = 0;
+ event.set = ui_get_setop();
+
+ UiSubListEventData sublistEvent;
+
+ NSInteger selectedRow = _outlineView.selectedRow;
+ if(selectedRow >= 0) {
+ UiSourceListItem *item = [_outlineView itemAtRow:selectedRow];
+ UiSourceListItem *parent = item.parent;
+ UiSubList *sublist = parent != nil ? parent.sublist : item.sublist;
+ UiVar *var = parent != nil ? parent.var : item.var;
+ if(item && var) {
+ sublistEvent.list = var->value;
+ sublistEvent.sublist_index = parent ? parent.sublistIndex : item.sublistIndex;
+ sublistEvent.row_index = (int)selectedRow - item.sublistStartRow - 1;
+ sublistEvent.sublist_userdata = sublist ? sublist->userdata : NULL;
+ sublistEvent.event_data = item.eventdata;
+ sublistEvent.row_data = sublistEvent.list->get(sublistEvent.list, sublistEvent.row_index);
+
+ event.eventdata = &sublistEvent;
+ event.eventdatatype = UI_EVENT_DATA_SUBLIST;
+ }
+ }
+
+ if(_onactivate) {
+ _onactivate(&event, _onactivatedata);
+ }
+}
+
+@end
+
+/*
+ * Outline datasource item
+ * Is used for sections (sublists) and individual items
+ */
+@implementation UiSourceListItem
+
+- (id)init:(UiSourceList*)sourcelist sublist:(UiSubList*)sublist {
+ _sourcelist = sourcelist;
+ _sublist = sublist;
+ _items = [[NSMutableArray alloc]initWithCapacity:16];
+ if(sublist->header) {
+ _label = [[NSString alloc]initWithUTF8String:sublist->header];
+ } else {
+ _label = @"";
+ }
+ UiVar *var = uic_widget_var(sourcelist.obj->ctx,
+ sourcelist.obj->ctx,
+ sublist->value,
+ sublist->varname,
+ UI_VAR_LIST);
+ _var = var;
+ return self;
+}
+
+- (id)init:(UiSubListItem*)item parent:(UiSourceListItem*)parent {
+ _parent = parent;
+ if(item->label) {
+ _label = [[NSString alloc]initWithUTF8String:item->label];
+ } else {
+ _label = @"";
+ }
+ _eventdata = item->eventdata;
+ return self;
+}
+
+- (BOOL)isSection {
+ return _sublist != NULL;
+}
+
+- (void)update:(int)row {
+ // TODO: check row
+
+ [_items removeAllObjects];
+ if(_var == NULL) {
+ return;
+ }
+ UiList *list = _var->value;
+ void *elm = list->first(list);
+ int index = 0;
+ while(elm) {
+ UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
+ if(_sourcelist.getvalue) {
+ _sourcelist.getvalue(list, _sublist->userdata, elm, index, &item, _sourcelist.getvaluedata);
+ } else {
+ item.label = strdup(elm);
+ }
+
+ UiSourceListItem *it = [[UiSourceListItem alloc] init:&item parent:self];
+ it.sublistIndex = index;
+ it.rownum = self.rownum + index;
+ it.sublistStartRow = _parent ? _parent.sublistStartRow : _sublistStartRow;
+ [_items addObject:it];
+
+ elm = list->next(list);
+ index++;
+ }
+}
+
+@end
+
+/*
+ * Custom NSTableRowView implementation
+ * Moves the disclosure button to the right side
+ * Handles mouse hover events (for hiding the disclosure button)
+ */
+@implementation UiSourceListRow
+
+- (void)layout {
+ [super layout];
+
+ for (NSView *subview in self.subviews) {
+ if ([subview.identifier isEqualToString:NSOutlineViewDisclosureButtonKey] ||
+ [subview.identifier isEqualToString:NSOutlineViewShowHideButtonKey])
+ {
+ NSRect frame = subview.frame;
+ frame.origin.x = self.bounds.size.width - frame.size.width - 16.0;
+ subview.frame = frame;
+
+ if(!_hover) {
+ subview.hidden = YES;
+ }
+
+ if(subview != _disclosureButton) {
+ // init disclosure button
+ _disclosureButton = (NSButton*)subview;
+ if ([subview isKindOfClass:[NSButton class]]) {
+ NSButton *button = (NSButton*)subview;
+ button.contentTintColor = [NSColor tertiaryLabelColor];
+ }
+ }
+
+
+ } else if ([subview.identifier isEqualToString:@"cell"]) {
+ NSRect frame = subview.frame;
+ frame.origin.x = 16;
+ subview.frame = frame;
+ }
+ }
+}
+
+- (void)updateTrackingAreas {
+ [super updateTrackingAreas];
+ if(_trackingArea != nil) {
+ [self removeTrackingArea:_trackingArea];
+ }
+ _trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds
+ options:NSTrackingMouseEnteredAndExited |
+ NSTrackingActiveInActiveApp |
+ NSTrackingInVisibleRect
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:_trackingArea];
+}
+
+- (void)mouseEntered:(NSEvent *)event {
+ _hover = YES;
+ _disclosureButton.hidden = _showDisclosureButton ? NO : YES;
+}
+
+- (void)mouseExited:(NSEvent *)event {
+ _hover = NO;
+ _disclosureButton.hidden = YES;
+}
+
+@end
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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.
+ */
+
+#import "../ui/menu.h"
+#import "toolkit.h"
+
+#import "../common/menu.h"
+
+@interface MenuItem : NSObject
+
+@property (weak) NSMenuItem *menuItem;
+@property (strong) NSString *itemId;
+@property UiMenuCheckItem *checkItem;
+@property UiMenuRadioItem *radioItem;
+@property ui_callback callback;
+@property void *userdata;
+@property (strong) NSString *varname;
+@property UiObject *obj;
+@property UiVar *var;
+@property BOOL state;
+
+- (MenuItem*)init:(int)itId;
+
+- (void)handleToggleEvent:(id)sender;
+
+@end
+
+void ui_menu_init(void);
+
+typedef void(*ui_menu_add_f)(UiObject*, NSMenu*, int, UiMenuItemI*);
+
+void add_menu_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item);
+void add_menuitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item);
+void add_menuseparator_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item);
+void add_checkitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item);
+void add_radioitem_widget(UiObject *obj, NSMenu *parent, int index, UiMenuItemI *item);
+void add_checkitemnv_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item);
+void add_menuitem_list_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item);
+
+void ui_add_menu_items(UiObject *obj, NSMenu *parent, int i, UiMenu *menu);
+
+NSArray* ui_get_binding_items(void);
+
+int64_t ui_menu_toggleitem_get(UiInteger *i);
+void ui_menu_toggleitem_set(UiInteger *i, int64_t value);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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.
+ */
+
+#import <stdio.h>
+#import <stdlib.h>
+#import <string.h>
+#import <stdarg.h>
+#import <objc/runtime.h>
+
+#import "menu.h"
+#import "window.h"
+#import "EventData.h"
+
+#pragma GCC diagnostic ignored "-Wundeclared-selector"
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+
+// holds all items that need bindings
+// value type: MenuItem*
+static NSMutableArray *bindingItems;
+
+@implementation MenuItem
+
+- (MenuItem*)init:(int)itId {
+ self.itemId = [[NSString alloc] initWithFormat:@"item%d", itId];
+ return self;
+}
+
+- (void)handleToggleEvent:(id)sender {
+ NSMenuItem *item = (NSMenuItem*)sender;
+ if(!_state) {
+ item.state = NSControlStateValueOn;
+ } else {
+ item.state = NSControlStateValueOff;
+ }
+ _state = !_state;
+
+ if(_callback) {
+ UiEvent event;
+ event.obj = _obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = _state;
+ event.set = ui_get_setop();
+ _callback(&event, _userdata);
+ }
+}
+
+@end
+
+static ui_menu_add_f createMenuItem[] = {
+ /* UI_MENU */ add_menu_widget,
+ /* UI_MENU_ITEM */ add_menuitem_widget,
+ /* UI_MENU_CHECK_ITEM */ add_checkitem_widget,
+ /* UI_MENU_RADIO_ITEM */ add_radioitem_widget,
+ /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_CHECKITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_RADIOITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_SEPARATOR */ add_menuseparator_widget
+};
+
+void ui_add_menu_items(UiObject *obj, NSMenu *parent, int i, UiMenu *menu) {
+ UiMenuItemI *it = menu->items_begin;
+ int index = 0;
+ while(it) {
+ createMenuItem[it->type](obj, parent, index, it);
+ it = it->next;
+ index++;
+ }
+}
+
+void add_menu_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) {
+ UiMenu *it = (UiMenu*)item;
+ NSString *str = [[NSString alloc] initWithUTF8String:it->label];
+ NSMenu *menu = [[NSMenu alloc] initWithTitle: str];
+ NSMenuItem *menuItem = [parent addItemWithTitle:str action:nil keyEquivalent:@""];
+ [parent setSubmenu:menu forItem:menuItem];
+
+ ui_add_menu_items(obj, menu, i, it);
+}
+
+void add_menuitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) {
+ UiMenuItem *it = (UiMenuItem*)item;
+
+ NSString *str = [[NSString alloc] initWithUTF8String:it->label];
+ NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuItemAction) keyEquivalent:@""];
+
+ if(it->callback) {
+ EventData *event = [[EventData alloc] init:it->callback userdata:it->userdata];
+ if(obj) {
+ event.obj = obj;
+ menuItem.target = event;
+ menuItem.action = @selector(handleEvent:);
+ }
+ objc_setAssociatedObject(menuItem, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+}
+
+void add_menuseparator_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) {
+ NSMenuItem *menuItem = [NSMenuItem separatorItem];
+ [parent addItem:menuItem];
+}
+
+static int nItem = 0;
+
+void add_checkitem_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) {
+ UiMenuCheckItem *it = (UiMenuCheckItem*)item;
+
+ NSString *str = [[NSString alloc] initWithUTF8String:it->label];
+ NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuCheckItemAction:) keyEquivalent:@""];
+
+ MenuItem *mItem = [[MenuItem alloc] init:nItem++];
+ mItem.menuItem = menuItem;
+ mItem.obj = obj;
+ mItem.callback = it->callback;
+ mItem.userdata = it->userdata;
+ mItem.checkItem = it;
+ if(it->varname) {
+ mItem.varname = [[NSString alloc] initWithUTF8String:it->varname];
+ }
+
+ objc_setAssociatedObject(menuItem, "menuitem", mItem, OBJC_ASSOCIATION_RETAIN);
+
+ if(!obj) {
+ [bindingItems addObject:mItem];
+ } else {
+ mItem.var = uic_widget_var(obj->ctx, obj->ctx, NULL, it->varname, UI_VAR_INTEGER);
+ if(mItem.var) {
+ UiInteger *i = mItem.var->value;
+ i->obj = (__bridge void*)mItem;
+ i->get = ui_menu_toggleitem_get;
+ i->set = ui_menu_toggleitem_set;
+ }
+ menuItem.target = mItem;
+ menuItem.action = @selector(handleToggleEvent:);
+ }
+}
+
+void add_radioitem_widget(UiObject *obj, NSMenu *parent, int index, UiMenuItemI *item) {
+ UiMenuRadioItem *it = (UiMenuRadioItem*)item;
+
+ NSString *str = [[NSString alloc] initWithUTF8String:it->label];
+ NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuRadioItemAction:) keyEquivalent:@""];
+
+ MenuItem *mItem = [[MenuItem alloc] init:nItem++];
+ mItem.callback = it->callback;
+ mItem.userdata = it->userdata;
+ mItem.radioItem = it;
+
+ objc_setAssociatedObject(menuItem, "menuitem", mItem, OBJC_ASSOCIATION_RETAIN);
+ [bindingItems addObject:mItem];
+}
+
+void add_checkitemnv_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) {
+
+}
+
+void add_menuitem_list_widget(UiObject *obj, NSMenu *parent, int i, UiMenuItemI *item) {
+
+}
+
+
+void ui_menu_init(void) {
+ bindingItems = [[NSMutableArray alloc] init];
+
+ UiMenu *menus_begin = uic_get_menu_list();
+ UiMenu *ls = menus_begin;
+ int index = 1;
+ while(ls) {
+ if(ls->item.type == UI_MENU) {
+ NSString *str = [[NSString alloc] initWithUTF8String:ls->label];
+ NSMenu *menu = [[NSMenu alloc] initWithTitle: str];
+ NSMenuItem *menuItem = [[NSApp mainMenu] insertItemWithTitle:str action:nil keyEquivalent:@"" atIndex:index];
+ [[NSApp mainMenu] setSubmenu:menu forItem:menuItem];
+
+ ui_add_menu_items(NULL, menu, 0, ls);
+ }
+ ls = (UiMenu*)ls->item.next;
+ index++;
+ }
+}
+
+NSArray* ui_get_binding_items(void) {
+ return bindingItems;
+}
+
+
+int64_t ui_menu_toggleitem_get(UiInteger *i) {
+ MenuItem *item = (__bridge MenuItem*)i->obj;
+ i->value = item.state;
+ return i->value;
+}
+
+void ui_menu_toggleitem_set(UiInteger *i, int64_t value) {
+ MenuItem *item = (__bridge MenuItem*)i->obj;
+ i->value = value;
+ if(value != 0) {
+ item.menuItem.state = NSControlStateValueOn;
+ } else {
+ item.menuItem.state = NSControlStateValueOff;
+ }
+}
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+COCOA_SRC_DIR = ui/cocoa/
+COCOA_OBJPRE = $(OBJ_DIR)/$(COCOA_SRC_DIR)
+
+COCOAOBJ = toolkit.o
+COCOAOBJ += AppDelegate.o
+COCOAOBJ += GridLayout.o
+COCOAOBJ += BoxContainer.o
+COCOAOBJ += EventData.o
+COCOAOBJ += UiThread.o
+COCOAOBJ += MainWindow.o
+COCOAOBJ += WindowManager.o
+COCOAOBJ += window.o
+COCOAOBJ += Container.o
+COCOAOBJ += button.o
+COCOAOBJ += text.o
+COCOAOBJ += menu.o
+COCOAOBJ += Toolbar.o
+COCOAOBJ += ListDelegate.o
+COCOAOBJ += ListDataSource.o
+COCOAOBJ += label.o
+COCOAOBJ += list.o
+COCOAOBJ += widget.o
+COCOAOBJ += image.o
+COCOAOBJ += entry.o
+COCOAOBJ += TabView.o
+
+TOOLKITOBJS += $(COCOAOBJ:%=$(COCOA_OBJPRE)%)
+TOOLKITSOURCE += $(COCOAOBJ:%.o=cocoa/%.m)
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+
+#import "../ui/text.h"
+
+
+void ui_textarea_save(UiText *text);
+void ui_textarea_destroy(UiText *text);
+void ui_textarea_restore(UiText *text);
+void ui_textarea_set(UiText *text, const char *str);
+char* ui_textarea_get(UiText *text);
+char* ui_textarea_getsubstr(UiText *text, int begin, int end);
+void ui_textarea_insert(UiText *text, int pos, char *str);
+void ui_textarea_setposition(UiText *text, int pos);
+int ui_textarea_position(UiText *text);
+void ui_textarea_setselection(UiText *text, int begin, int end);
+void ui_textarea_selection(UiText *text, int *begin, int *end);
+int ui_textarea_length(UiText *text);
+void ui_textarea_remove(UiText *text, int begin, int end);
+
+char* ui_textfield_get(UiString *s);
+void ui_textfield_set(UiString *s, const char *value);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "text.h"
+#import "EventData.h"
+#import "Container.h"
+#import <objc/runtime.h>
+
+UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
+ NSTextView *textview = [[NSTextView alloc] init];
+ textview.autoresizingMask = NSViewWidthSizable;
+ textview.minSize = NSMakeSize(0, 0);
+ textview.maxSize = NSMakeSize(FLT_MAX, FLT_MAX);
+
+ NSScrollView *scrollview = [[NSScrollView alloc] init];
+ scrollview.hasVerticalScroller = YES;
+ scrollview.documentView = textview;
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, scrollview, &layout);
+
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_TEXT);
+ if(var) {
+ UiText *text = var->value;
+ text->obj = (__bridge void*)textview;
+ ui_textarea_restore(text);
+
+ text->save = ui_textarea_save;
+ text->destroy = ui_textarea_destroy;
+ text->restore = ui_textarea_restore;
+ text->set = ui_textarea_set;
+ text->get = ui_textarea_get;
+ text->getsubstr = ui_textarea_getsubstr;
+ text->insert = ui_textarea_insert;
+ text->setposition = ui_textarea_setposition;
+ text->position = ui_textarea_position;
+ text->setselection = ui_textarea_setselection;
+ text->selection = ui_textarea_selection;
+ text->length = ui_textarea_length;
+ text->remove = ui_textarea_remove;
+ }
+
+ return (__bridge void*)scrollview;
+}
+
+
+
+
+void ui_textarea_save(UiText *text) {
+
+}
+
+void ui_textarea_destroy(UiText *text) {
+ (void)(__bridge_transfer NSTextStorage*)text->data1;
+}
+
+void ui_textarea_restore(UiText *text) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ NSTextStorage *textStorage;
+ if(text->data1) {
+ textStorage = (__bridge NSTextStorage*)text->data1;
+ } else {
+ textStorage = [[NSTextStorage alloc] init];
+ }
+ [textview.layoutManager replaceTextStorage:textStorage];
+ text->data1 = (__bridge_retained void*)textStorage;
+}
+
+void ui_textarea_set(UiText *text, const char *str) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ if(text->value.free) {
+ text->value.free(text->value.ptr);
+ }
+ text->value.ptr = strdup(str);
+ text->value.free = free;
+ textview.string = [[NSString alloc] initWithUTF8String:str];
+}
+
+char* ui_textarea_get(UiText *text) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ if(text->value.free) {
+ text->value.free(text->value.ptr);
+ }
+ text->value.ptr = strdup(textview.string.UTF8String);
+ text->value.free = free;
+ return text->value.ptr;
+}
+
+char* ui_textarea_getsubstr(UiText *text, int begin, int end) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ NSString *str = textview.string;
+ NSRange range = NSMakeRange(begin, end-begin);
+ NSString *sub = [str substringWithRange:range];
+ return strdup(sub.UTF8String);
+}
+
+void ui_textarea_insert(UiText *text, int pos, char *str) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ NSString *s = [[NSString alloc] initWithUTF8String:str];
+ NSAttributedString *attributedStr = [[NSAttributedString alloc] initWithString:s];
+ [textview.textStorage insertAttributedString:attributedStr atIndex:pos];
+}
+
+void ui_textarea_setposition(UiText *text, int pos) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ NSRange range = NSMakeRange(pos, 0);
+ [textview setSelectedRange:range];
+}
+
+int ui_textarea_position(UiText *text) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ NSRange range = textview.selectedRange;
+ return (int)range.location;
+}
+
+void ui_textarea_setselection(UiText *text, int begin, int end) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ NSRange range = NSMakeRange(begin, end-begin);
+ [textview setSelectedRange:range];
+}
+
+void ui_textarea_selection(UiText *text, int *begin, int *end) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ NSRange range = textview.selectedRange;
+ if(begin) {
+ *begin = (int)range.location;
+ }
+ if(end) {
+ *end = (int)(range.location+range.length);
+ }
+}
+
+int ui_textarea_length(UiText *text) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+ return (int)textview.string.length;
+}
+
+void ui_textarea_remove(UiText *text, int begin, int end) {
+ NSTextView *textview = (__bridge NSTextView*)text->obj;
+
+ if (begin < 0 || end < begin || end > textview.string.length) {
+ return;
+ }
+
+ NSRange range = NSMakeRange(begin, end - begin);
+ [[textview textStorage] deleteCharactersInRange:range];
+}
+
+
+
+/* -------------------------- TextField -------------------------- */
+
+static UIWIDGET textfield_create(UiObject *obj, UiTextFieldArgs *args, BOOL password, BOOL frameless) {
+ NSTextField *textfield;
+ if(password) {
+ textfield = [[NSSecureTextField alloc] init];
+ } else {
+ textfield = [[NSTextField alloc] init];
+ }
+
+ if(!args->hfill || args->width > 0) {
+ textfield.translatesAutoresizingMaskIntoConstraints = NO;
+ int width = args->width > 0 ? args->width : 100;
+ [[textfield.widthAnchor constraintEqualToConstant:width] setActive:YES];
+ }
+
+
+ if(frameless) {
+ [textfield setBezeled: NO];
+ }
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, textfield, &layout);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiString *s = var->value;
+ if(s->value.ptr) {
+ textfield.stringValue = [[NSString alloc] initWithUTF8String:s->value.ptr];
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ }
+ s->obj = (__bridge void*)textfield;
+ s->get = ui_textfield_get;
+ s->set = ui_textfield_set;
+ }
+
+ return (__bridge void*)textfield;
+}
+
+UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) {
+ return textfield_create(obj, args, FALSE, FALSE);
+}
+
+UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs *args) {
+ return textfield_create(obj, args, FALSE, TRUE);
+}
+
+UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs *args) {
+ return textfield_create(obj, args, TRUE, FALSE);
+}
+
+char* ui_textfield_get(UiString *s) {
+ NSTextField *textfield = (__bridge NSTextField*)s->obj;
+ NSString *str = textfield.stringValue;
+ const char *cstr = str.UTF8String;
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ s->value.ptr = strdup(cstr);
+ s->value.free = free;
+ return s->value.ptr;
+}
+
+void ui_textfield_set(UiString *s, const char *value) {
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ s->value.ptr = NULL;
+ s->value.free = NULL;
+ NSTextField *textfield = (__bridge NSTextField*)s->obj;
+ textfield.stringValue = [[NSString alloc] initWithUTF8String:value];
+}
--- /dev/null
+/*
+ * 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 "../ui/toolkit.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+@interface UiAppCallback : NSObject
+
+@property ui_threadfunc callback;
+@property void *userdata;
+
+- (id) initWithCallback:(ui_threadfunc)func userdata:(void*)userdata;
+
+- (void) callMainThread;
+
+- (void) mainThread:(id)n;
+
+@end
+
+@protocol UiToplevelObject
+
+- (BOOL) getIsVisible;
+- (void) setVisible:(BOOL)visible;
+
+@end
+
+void ui_cocoa_onstartup(void);
+void ui_cocoa_onopen(const char *file);
+void ui_cocoa_onexit(void);
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+
+#include "../common/document.h"
+#include "../common/properties.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
+#include "../common/threadpool.h"
+
+#import "image.h"
+#import "menu.h"
+#import "Toolbar.h"
+#import "UiThread.h"
+
+#import "AppDelegate.h"
+
+static const char *application_name;
+
+static int app_argc;
+static const char **app_argv;
+
+static ui_callback startup_func;
+static void *startup_data;
+static ui_callback open_func;
+static void *open_data;
+static ui_callback exit_func;
+static void *exit_data;
+
+static UiBool exit_on_shutdown;
+
+/* ------------------- App Init / Event Loop functions ------------------- */
+
+
+void ui_init(const char *appname, int argc, char **argv) {
+ application_name = appname;
+ app_argc = argc;
+ app_argv = (const char**)argv;
+
+ uic_init_global_context();
+
+ uic_menu_init();
+ uic_toolbar_init();
+
+ uic_load_app_properties();
+
+ NSApplication *app = [NSApplication sharedApplication];
+ //[app setActivationPolicy:NSApplicationActivationPolicyRegular];
+
+ //[NSBundle loadNibNamed:@"MainMenu" owner:NSApp ];
+ //[[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:&topLevelObjects];
+
+ ui_icon_init();
+ ui_toolbar_init();
+
+}
+
+const char* ui_appname() {
+ return application_name;
+}
+
+void ui_onstartup(ui_callback f, void *userdata) {
+ startup_func = f;
+ startup_data = userdata;
+}
+
+void ui_onopen(ui_callback f, void *userdata) {
+ open_func = f;
+ open_data = userdata;
+}
+
+void ui_onexit(ui_callback f, void *userdata) {
+ exit_func = f;
+ exit_data = userdata;
+}
+
+void ui_app_exit_on_shutdown(UiBool exitapp) {
+ exit_on_shutdown = exitapp;
+}
+
+void ui_cocoa_onstartup(void) {
+ UiEvent e;
+ e.obj = NULL;
+ e.window = NULL;
+ e.document = NULL;
+ e.eventdata = NULL;
+ e.intval = 0;
+ if(startup_func) {
+ startup_func(&e, startup_data);
+ }
+}
+
+void ui_cocoa_onopen(const char *file) {
+ UiEvent e;
+ e.obj = NULL;
+ e.window = NULL;
+ e.document = NULL;
+ e.eventdata = NULL;
+ e.intval = 0;
+ if(open_func) {
+ open_func(&e, open_data);
+ }
+}
+
+void ui_cocoa_onexit(void) {
+ UiEvent e;
+ e.obj = NULL;
+ e.window = NULL;
+ e.document = NULL;
+ e.eventdata = NULL;
+ e.intval = 0;
+ if(exit_func) {
+ exit_func(&e, exit_data);
+ }
+}
+
+void ui_main(void) {
+ NSApplicationMain(app_argc, app_argv);
+ if(exit_on_shutdown) {
+ exit(0);
+ }
+}
+
+/* ------------------- Window Visibility functions ------------------- */
+
+void ui_show(UiObject *obj) {
+ if(obj->wobj) {
+ id<UiToplevelObject> window = (__bridge id<UiToplevelObject>)obj->wobj;
+
+ if(![window getIsVisible]) {
+ obj->ref++;
+ }
+
+ [window setVisible:YES];
+ }
+}
+
+void ui_close(UiObject *obj) {
+ // TODO: unref, window close, ...
+ if(obj->wobj) {
+ id<UiToplevelObject> window = (__bridge id<UiToplevelObject>)obj->wobj;
+ [window setVisible:NO];
+ }
+}
+
+/* ------------------- Job Control / Threadpool functions ------------------- */
+
+void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) {
+ UiThread *thread = [[UiThread alloc]init:obj jobfunc:tf jobdata:td];
+ thread.finish_callback = f;
+ thread.finish_userdata = fd;
+ [thread start];
+}
+
+@implementation UiAppCallback
+
+- (id) initWithCallback:(ui_threadfunc)func userdata:(void*)userdata {
+ _callback = func;
+ _userdata = userdata;
+ return self;
+}
+
+- (void) callMainThread {
+ [self performSelectorOnMainThread:@selector(mainThread:)
+ withObject:nil
+ waitUntilDone:NO];
+}
+
+- (void) mainThread:(id)n {
+ if(_callback) {
+ _callback(_userdata);
+ }
+}
+
+@end
+
+void ui_call_mainthread(ui_threadfunc tf, void* td) {
+ UiAppCallback *cb = [[UiAppCallback alloc]initWithCallback:tf userdata:td];
+ [cb callMainThread];
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "../ui/webview.h"
+
+#import <WebKit/WebKit.h>
+
+
+enum WebViewDataType {
+ WEBVIEW_CONTENT_URL,
+ WEBVIEW_CONTENT_CONTENT
+};
+
+struct UiWebViewData {
+ void *webview;
+ char *uri;
+ char *mimetype;
+ char *encoding;
+ char *content;
+ size_t contentlength;
+ enum WebViewDataType type;
+
+ double zoom;
+ UiBool javascript;
+};
+
+UiWebViewData* ui_webview_data_clone(UiWebViewData *data);
+
+void* ui_webview_get(UiGeneric *g);
+const char* ui_webview_get_type(UiGeneric *g);
+int ui_webview_set(UiGeneric *g, void *data, const char *type);
+void ui_webview_destroy(UiGeneric *g);
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "webview.h"
+#import "Container.h"
+
+UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) {
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC);
+
+ WKWebView *webview = [[WKWebView alloc]init];
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ui_container_add(obj, webview, &layout);
+
+ if(var) {
+ UiGeneric *value = var->value;
+ value->get = ui_webview_get;
+ value->get_type = ui_webview_get_type;
+ value->set = ui_webview_set;
+ value->destroy = ui_webview_destroy;
+ value->obj = (__bridge void*)webview;
+ if(value->value) {
+ ui_webview_set(value, value->value, UI_WEBVIEW_OBJECT_TYPE);
+ } else {
+ UiWebViewData *data = malloc(sizeof(UiWebViewData));
+ memset(data, 0, sizeof(UiWebViewData));
+ data->webview = (__bridge void*)webview;
+ data->javascript = TRUE;
+ data->zoom = 1;
+ value->value = value;
+ }
+ }
+
+ return (__bridge void*)webview;
+}
+
+UiWebViewData* ui_webview_data_clone(UiWebViewData *data) {
+ UiWebViewData *newdata = malloc(sizeof(UiWebViewData));
+ memset(newdata, 0, sizeof(UiWebViewData));
+ newdata->zoom = 1;
+ newdata->javascript = TRUE;
+
+ if(data) {
+ newdata->uri = data->uri ? strdup(data->uri) : NULL;
+ newdata->mimetype = data->mimetype ? strdup(data->mimetype) : NULL;
+ newdata->encoding = data->encoding ? strdup(data->encoding) : NULL;
+ newdata->type = data->type;
+ newdata->javascript = data->javascript;
+ newdata->zoom = data->zoom;
+ if(data->content && data->contentlength > 0) {
+ newdata->content = malloc(data->contentlength + 1);
+ memcpy(newdata->content, data->content, data->contentlength);
+ newdata->content[data->contentlength] = 0;
+ newdata->contentlength = data->contentlength;
+ }
+ }
+
+ return newdata;
+}
+
+void ui_webview_data_free(UiWebViewData *data) {
+ if(!data) {
+ return;
+ }
+ free(data->uri);
+ free(data->mimetype);
+ free(data->encoding);
+ free(data->content);
+ free(data);
+}
+
+void* ui_webview_get(UiGeneric *g) {
+ UiWebViewData *data = g->value;
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+
+ if(data->type == WEBVIEW_CONTENT_URL) {
+ (void)ui_webview_get_uri(g); // this updates data->uri
+ }
+ data->zoom = webview.pageZoom;
+
+ return ui_webview_data_clone(g->value);
+}
+
+const char* ui_webview_get_type(UiGeneric *g) {
+ return UI_WEBVIEW_OBJECT_TYPE;
+}
+
+int ui_webview_set(UiGeneric *g, void *data, const char *type) {
+ if(!data || !type) {
+ return 1;
+ }
+ if(strcmp(type, UI_WEBVIEW_OBJECT_TYPE)) {
+ return 1;
+ }
+ ui_webview_data_free(g->value);
+ g->value = ui_webview_data_clone(data);
+
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+ UiWebViewData *webd = data;
+ if(webd->type == WEBVIEW_CONTENT_URL) {
+ const char *uri = webd->uri ? webd->uri : "about:blank";
+ NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:uri]];
+ if(url) {
+ NSURLRequest *req = [NSURLRequest requestWithURL:url];
+ if(req) {
+ [webview loadRequest:req];
+ }
+ }
+ } else {
+ NSString *mimetype = [[NSString alloc]initWithUTF8String: webd->mimetype ? webd->mimetype : "text/plain"];
+ NSString *encoding = [[NSString alloc]initWithUTF8String: webd->encoding ? webd->encoding : "UTF-8"];
+ NSString *urlStr = [[NSString alloc]initWithUTF8String: webd->uri ? webd->uri : "file:///"];
+ NSURL *url = [NSURL URLWithString:urlStr];
+ NSData *content = [NSData dataWithBytes:webd->content length:webd->contentlength];
+ if(!url) {
+ url = [NSURL URLWithString:@"about:blank"];
+ }
+ [webview loadData:content MIMEType:mimetype characterEncodingName:encoding baseURL:url];
+ }
+
+ webview.pageZoom = webd->zoom;
+
+ return 1;
+}
+
+void ui_webview_destroy(UiGeneric *g) {
+ ui_webview_data_free(g->value);
+ g->value = NULL;
+}
+
+
+void ui_webview_load_url(UiGeneric *g, const char *url) {
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+ UiWebViewData *data = g->value;
+ data->type = WEBVIEW_CONTENT_URL;
+
+ if(!url) {
+ url = "about:blank";
+ }
+
+ NSURL *nsurl = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]];
+ if(nsurl) {
+ NSURLRequest *req = [NSURLRequest requestWithURL:nsurl];
+ if(req) {
+ [webview loadRequest:req];
+ }
+ }
+}
+
+void ui_webview_load_content(
+ UiGeneric *g,
+ const char *uri,
+ const char *content,
+ size_t contentlength,
+ const char *mimetype,
+ const char *encoding)
+{
+ UiWebViewData *data = g->value;
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+
+ data->type = WEBVIEW_CONTENT_CONTENT;
+
+ free(data->uri);
+ data->uri = NULL;
+ free(data->mimetype);
+ free(data->encoding);
+ free(data->content);
+ data->type = WEBVIEW_CONTENT_URL;
+
+ data->content = malloc(contentlength+1);
+ memcpy(data->content, content, contentlength);
+ data->content[contentlength] = 0;
+
+ if(!mimetype) {
+ mimetype = "text/plain";
+ }
+ if(!encoding) {
+ encoding = "UTF-8";
+ }
+
+ data->mimetype = strdup(mimetype);
+ data->encoding = strdup(encoding);
+
+ NSString *mtype = [[NSString alloc]initWithUTF8String:mimetype];
+ NSString *enc = [[NSString alloc]initWithUTF8String:encoding];
+ NSData *ct = [NSData dataWithBytes:content length:contentlength];
+ NSURL *url;
+ if(uri) {
+ NSString *uriStr = [[NSString alloc]initWithUTF8String:uri];
+ url = [NSURL URLWithString:uriStr];
+ } else {
+ url = [NSURL URLWithString:@"file:///"];
+ }
+ [webview loadData:ct MIMEType:mtype characterEncodingName:enc baseURL:url];
+}
+
+
+void ui_webview_reload(UiGeneric *g) {
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+ [webview reload];
+}
+
+UiBool ui_webview_can_go_back(UiGeneric *g) {
+ return FALSE;
+}
+
+UiBool ui_webview_can_go_forward(UiGeneric *g) {
+ return FALSE;
+}
+
+void ui_webview_go_back(UiGeneric *g) {
+
+}
+
+void ui_webview_go_forward(UiGeneric *g) {
+
+}
+
+const char * ui_webview_get_uri(UiGeneric *g) {
+ UiWebViewData *data = g->value;
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+
+ free(data->uri);
+ data->uri = NULL;
+
+ NSURL *url = webview.URL;
+ if(url) {
+ NSString *s = [url absoluteString];
+ if(s) {
+ data->uri = strdup(s.UTF8String);
+ }
+ }
+ return data->uri;
+}
+
+void ui_webview_enable_javascript(UiGeneric *g, UiBool enable) {
+ // unsupported
+}
+
+void ui_webview_set_zoom(UiGeneric *g, double zoom) {
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+ webview.pageZoom = zoom;
+}
+
+double ui_webview_get_zoom(UiGeneric *g) {
+ WKWebView *webview = (__bridge WKWebView*)g->obj;
+ return webview.pageZoom;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+#import "Container.h"
+#import "../ui/widget.h"
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "widget.h"
+
+/* genereal widget functions */
+
+void ui_set_enabled(UIWIDGET widget, int enabled) {
+ NSControl *control = (__bridge NSControl*)widget;
+ control.enabled = enabled;
+}
+
+void ui_set_show_all(UIWIDGET widget, int value) {
+ // TODO: is this relevant?
+}
+
+void ui_set_visible(UIWIDGET widget, int visible) {
+ NSView *view = (__bridge NSView*)widget;
+ view.hidden = !visible;
+}
+
+
+UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
+ // TODO
+ return NULL;
+}
+
+
+/* custom widget */
+
+UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
+ UIWIDGET widget = create_widget(obj, args, userdata);
+
+ NSView *view = (__bridge NSView*)widget;
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, view, &layout);
+
+ return widget;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "toolkit.h"
+#import "../ui/window.h"
+
+@interface UiDialogWindow : NSPanel<UiToplevelObject>
+
+@property UiObject *obj;
+@property NSWindow *parent;
+@property UiTri modal;
+@property ui_callback onclick;
+@property void *onclickdata;
+
+@end
--- /dev/null
+/*
+ * 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.
+ */
+
+#import "window.h"
+
+#import "MainWindow.h"
+#import "WindowManager.h"
+#import "BoxContainer.h"
+#import "EventData.h"
+
+#import <objc/runtime.h>
+
+#include "../ui/window.h"
+#include "../ui/properties.h"
+#include "../common/context.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
+#include "../common/object.h"
+
+#include <cx/mempool.h>
+
+static int window_default_width = 650;
+static int window_default_height = 550;
+
+static int splitview_window_default_pos = -1;
+static UiBool splitview_window_use_prop = TRUE;
+
+static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar, BOOL splitview) {
+ UiObject *obj = uic_object_new_toplevel();
+
+ MainWindow *window = [[MainWindow alloc] init:obj withSidebar:sidebar withSplitview:splitview];
+ [[WindowManager sharedWindowManager] addWindow:window];
+ window.releasedWhenClosed = false; // TODO: we still need a cleanup strategy
+
+ obj->wobj = (__bridge void*)window;
+
+ MainWindowController *controller = [[MainWindowController alloc] initWithWindow:obj window:window];
+ window.windowController = controller;
+ [window setNextResponder:(NSResponder*)controller];
+ objc_setAssociatedObject(window, "windowcontroller", controller, OBJC_ASSOCIATION_RETAIN);
+
+ return obj;
+}
+
+UiObject* ui_window(const char *title, void *window_data) {
+ UiObject *obj = create_window(title, FALSE, FALSE, FALSE);
+ obj->window = window_data;
+ return obj;
+}
+
+UiObject* ui_simple_window(const char *title, void *window_data) {
+ UiObject *obj = create_window(title, TRUE, FALSE, FALSE);
+ obj->window = window_data;
+ return obj;
+}
+
+UiObject* ui_sidebar_window(const char *title, void *window_data) {
+ UiObject *obj = create_window(title, FALSE, TRUE, FALSE);
+ obj->window = window_data;
+ return obj;
+}
+
+UiObject* ui_splitview_window(const char *title, UiBool sidebar) {
+ sleep(1);
+ return create_window(title, FALSE, sidebar, TRUE);
+}
+
+void ui_window_size(UiObject *obj, int width, int height) {
+ // TODO
+}
+
+void ui_window_default_size(int width, int height) {
+ window_default_width = width;
+ window_default_height = height;
+}
+
+/* --------------------------------- File Dialogs --------------------------------- */
+
+void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) {
+ NSOpenPanel *openPanel = [NSOpenPanel openPanel];
+ if((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) {
+ openPanel.allowsMultipleSelection = YES;
+ }
+ if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) {
+ openPanel.canChooseFiles = NO;
+ openPanel.canChooseDirectories = YES;
+ }
+
+ NSWindow *window = (__bridge NSWindow*)obj->wobj;
+ [openPanel beginSheetModalForWindow:window completionHandler:^(NSModalResponse result) {
+ UiEvent event;
+ event.obj = obj;
+ event.window = obj->window;
+ event.document = obj->ctx->document;
+ event.intval = 0;
+ event.set = 0;
+
+ UiFileList flist = { NULL, 0 };
+ event.eventdata = &flist;
+ event.eventdatatype = UI_EVENT_DATA_FILE_LIST;
+
+ if(result == NSModalResponseOK) {
+ NSArray<NSURL *> *urls = [openPanel URLs];
+ flist.files = calloc(urls.count, sizeof(char*));
+ for(NSURL *url in urls) {
+ if([url isFileURL]) {
+ flist.files[flist.nfiles++] = strdup(url.path.UTF8String);
+ }
+ }
+ }
+
+ if(file_selected_callback) {
+ file_selected_callback(&event, cbdata);
+ }
+ ui_filelist_free(flist);
+ }];
+}
+
+void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) {
+ NSSavePanel *savePanel = [NSSavePanel savePanel];
+ if(name) {
+ NSString *nameStr = [[NSString alloc] initWithUTF8String:name];
+ [savePanel setNameFieldStringValue: nameStr];
+ }
+
+ NSWindow *window = (__bridge NSWindow*)obj->wobj;
+ [savePanel beginSheetModalForWindow:window completionHandler:^(NSModalResponse result) {
+ UiEvent event;
+ event.obj = obj;
+ event.window = obj->window;
+ event.document = obj->ctx->document;
+ event.intval = 0;
+ event.set = 0;
+
+ UiFileList flist = { NULL, 0 };
+ event.eventdata = &flist;
+ event.eventdatatype = UI_EVENT_DATA_FILE_LIST;
+
+ if(result == NSModalResponseOK) {
+ NSURL *url = [savePanel URL];
+ if([url isFileURL]) {
+ NSString *path = url.path;
+ flist.files = malloc(sizeof(char*));
+ flist.files[0] = strdup(path.UTF8String);
+ flist.nfiles = 1;
+ }
+ file_selected_callback(NULL, NULL);
+ }
+ if(file_selected_callback) {
+ file_selected_callback(&event, cbdata);
+ }
+ ui_filelist_free(flist);
+ }];
+}
+
+/* ------------------------------------- Dialog ------------------------------------- */
+
+void ui_dialog_create(UiObject *parent, UiDialogArgs *args) {
+ NSAlert *dialog = [[NSAlert alloc] init];
+
+ if(args->title) {
+ dialog.messageText = [[NSString alloc]initWithUTF8String:args->title];
+ }
+ if(args->content) {
+ dialog.informativeText = [[NSString alloc]initWithUTF8String:args->content];
+ }
+ NSTextField *textfield = nil;
+ if(args->input) {
+ NSRect frame = NSMakeRect(0,0,300,22);
+ textfield = args->password ? [[NSSecureTextField alloc] initWithFrame:frame] : [[NSTextField alloc]initWithFrame:frame];
+ if(args->input_value) {
+ textfield.stringValue = [[NSString alloc]initWithUTF8String:args->input_value];
+ }
+ dialog.accessoryView = textfield;
+ }
+
+ int b = 0;
+ int b1 = -1;
+ int b2 = -1;
+ if(args->button1_label) {
+ [dialog addButtonWithTitle:[[NSString alloc]initWithUTF8String:args->button1_label]];
+ b1 = b++;
+ }
+ if(args->button2_label) {
+ [dialog addButtonWithTitle:[[NSString alloc]initWithUTF8String:args->button2_label]];
+ b2 = b;
+ }
+ if(args->closebutton_label) {
+ [dialog addButtonWithTitle:[[NSString alloc]initWithUTF8String:args->closebutton_label]];
+ }
+
+ ui_callback callback = args->result;
+ void *userdata = args->resultdata;
+
+ NSWindow *window = (__bridge NSWindow*)parent->wobj;
+ [dialog beginSheetModalForWindow:window completionHandler:^(NSModalResponse returnCode) {
+ UiEvent event;
+ event.obj = parent;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.set = 0;
+ event.intval = 0;
+
+ long ret = returnCode - NSAlertFirstButtonReturn;
+ if(ret == b1) {
+ event.intval = 1;
+ } else if(ret == b2) {
+ event.intval = 2;
+ }
+
+ NSString *value = nil;
+ if(textfield) {
+ value = textfield.stringValue;
+ event.eventdata = (void*)value.UTF8String;
+ event.eventdatatype = UI_EVENT_DATA_STRING;
+ }
+
+ if(callback) {
+ callback(&event, userdata);
+ }
+ }];
+
+}
+
+/* ------------------------------------- Dialog Window ------------------------------------- */
+
+@implementation UiDialogWindow
+
+- (BOOL) getIsVisible {
+ return self.isVisible;
+}
+
+- (void) setVisible:(BOOL)visible {
+ //[self makeKeyAndOrderFront:nil];
+ if(visible) {
+ [_parent beginSheet:self completionHandler:^(NSModalResponse returnCode) {
+ // TODO: close event
+ }];
+ } else {
+ [self.sheetParent endSheet:self returnCode:NSModalResponseCancel];
+ }
+}
+
+- (void)cancelOperation:(id)sender {
+ [self.sheetParent endSheet:self returnCode:NSModalResponseCancel];
+ // TODO: close event
+}
+
+@end
+
+UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
+ UiObject *obj = uic_object_new_toplevel();
+ UiDialogWindow *panel = [[UiDialogWindow alloc] initWithContentRect:NSMakeRect(0, 0, args->width, args->height)
+ styleMask:(NSWindowStyleMaskTitled |
+ NSWindowStyleMaskClosable |
+ NSWindowStyleMaskResizable |
+ NSWindowStyleMaskUtilityWindow)
+ backing:NSBackingStoreBuffered
+ defer:NO];
+ panel.parent = (__bridge NSWindow*)parent->wobj;
+ panel.obj = obj;
+ panel.modal = args->modal;
+ panel.onclick = args->onclick;
+ panel.onclickdata = args->onclickdata;
+ [panel center];
+ [[WindowManager sharedWindowManager] addWindow:panel];
+ obj->wobj = (__bridge void*)panel;
+
+ NSView *content = panel.contentView;
+
+ // Create a view for the dialog window buttons (lbutton1, lbutton2, rbutton3, rbutton4)
+ NSView *buttonArea = [[NSView alloc]init];
+ buttonArea.translatesAutoresizingMaskIntoConstraints = NO;
+ [content addSubview:buttonArea];
+ [NSLayoutConstraint activateConstraints:@[
+ [buttonArea.bottomAnchor constraintEqualToAnchor:content.bottomAnchor constant:-10],
+ [buttonArea.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:10],
+ [buttonArea.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-10],
+ [buttonArea.heightAnchor constraintEqualToConstant:20]
+ ]];
+
+ NSButton *lbutton1 = nil;
+ if(args->lbutton1) {
+ lbutton1 = [[NSButton alloc]init];
+ lbutton1.title = [[NSString alloc]initWithUTF8String:args->lbutton1];
+ lbutton1.translatesAutoresizingMaskIntoConstraints = NO;
+ [buttonArea addSubview:lbutton1];
+ [NSLayoutConstraint activateConstraints:@[
+ [lbutton1.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+ [lbutton1.leadingAnchor constraintEqualToAnchor:buttonArea.leadingAnchor constant:0]
+ ]];
+
+ EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+ event.obj = obj;
+ event.value = 1;
+ lbutton1.target = event;
+ lbutton1.action = @selector(handleEvent:);
+ objc_setAssociatedObject(lbutton1, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+ NSButton *lbutton2 = nil;
+ if(args->lbutton2) {
+ lbutton2 = [[NSButton alloc]init];
+ lbutton2.title = [[NSString alloc]initWithUTF8String:args->lbutton2];
+ lbutton2.translatesAutoresizingMaskIntoConstraints = NO;
+ [buttonArea addSubview:lbutton2];
+ NSLayoutXAxisAnchor *anchor = lbutton1 != nil ? lbutton1.trailingAnchor : buttonArea.leadingAnchor;
+ int off = lbutton1 != nil ? 4 : 0;
+ [NSLayoutConstraint activateConstraints:@[
+ [lbutton2.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+ [lbutton2.leadingAnchor constraintEqualToAnchor:anchor constant:off]
+ ]];
+
+ EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+ event.obj = obj;
+ event.value = 2;
+ lbutton2.target = event;
+ lbutton2.action = @selector(handleEvent:);
+ objc_setAssociatedObject(lbutton2, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+
+ NSButton *rbutton4 = nil;
+ if(args->rbutton4) {
+ rbutton4 = [[NSButton alloc]init];
+ rbutton4.title = [[NSString alloc]initWithUTF8String:args->rbutton4];
+ rbutton4.translatesAutoresizingMaskIntoConstraints = NO;
+ [buttonArea addSubview:rbutton4];
+ [NSLayoutConstraint activateConstraints:@[
+ [rbutton4.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+ [rbutton4.trailingAnchor constraintEqualToAnchor:buttonArea.trailingAnchor constant:0]
+ ]];
+
+ EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+ event.obj = obj;
+ event.value = 2;
+ rbutton4.target = event;
+ rbutton4.action = @selector(handleEvent:);
+ objc_setAssociatedObject(rbutton4, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+ NSButton *rbutton3 = nil;
+ if(args->rbutton3) {
+ rbutton3 = [[NSButton alloc]init];
+ rbutton3.title = [[NSString alloc]initWithUTF8String:args->rbutton3];
+ rbutton3.translatesAutoresizingMaskIntoConstraints = NO;
+ [buttonArea addSubview:rbutton3];
+ NSLayoutXAxisAnchor *anchor = rbutton4 != nil ? rbutton4.leadingAnchor : buttonArea.trailingAnchor;
+ int off = rbutton4 != nil ? -4 : 0;
+ [NSLayoutConstraint activateConstraints:@[
+ [rbutton3.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0],
+ [rbutton3.trailingAnchor constraintEqualToAnchor:anchor constant:off]
+ ]];
+
+ EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata];
+ event.obj = obj;
+ event.value = 2;
+ rbutton3.target = event;
+ rbutton3.action = @selector(handleEvent:);
+ objc_setAssociatedObject(rbutton3, "eventdata", event, OBJC_ASSOCIATION_RETAIN);
+ }
+ switch(args->default_button) {
+ default: break;
+ case 1: if(lbutton1 != nil) lbutton1.keyEquivalent = @"\r"; break;
+ case 2: if(lbutton2 != nil) lbutton2.keyEquivalent = @"\r"; break;
+ case 3: if(rbutton3 != nil) rbutton3.keyEquivalent = @"\r"; break;
+ case 4: if(rbutton4 != nil) rbutton4.keyEquivalent = @"\r"; break;
+ }
+
+ // space between left and right buttons
+ NSView *space = [[NSView alloc]init];
+ space.translatesAutoresizingMaskIntoConstraints = NO;
+ [buttonArea addSubview:space];
+ NSLayoutXAxisAnchor *leftAnchor = buttonArea.leadingAnchor;
+ NSLayoutXAxisAnchor *rightAnchor = buttonArea.trailingAnchor;
+ if(lbutton2 != nil) {
+ leftAnchor = lbutton2.trailingAnchor;
+ } else if(lbutton1 != nil) {
+ leftAnchor = lbutton1.trailingAnchor;
+ }
+
+ if(rbutton3 != nil) {
+ rightAnchor = rbutton3.leadingAnchor;
+ } else if(rbutton4 != nil) {
+ rightAnchor = rbutton4.leadingAnchor;
+ }
+ [NSLayoutConstraint activateConstraints:@[
+ [space.topAnchor constraintEqualToAnchor:buttonArea.topAnchor],
+ [space.leadingAnchor constraintEqualToAnchor:leftAnchor constant:10],
+ [space.trailingAnchor constraintEqualToAnchor:rightAnchor constant:-10]
+ ]];
+
+ // dialog window main content
+ BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
+ vbox.translatesAutoresizingMaskIntoConstraints = NO;
+ [content addSubview:vbox];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [vbox.topAnchor constraintEqualToAnchor:content.topAnchor constant:0],
+ [vbox.leadingAnchor constraintEqualToAnchor:content.leadingAnchor],
+ [vbox.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],
+ [vbox.bottomAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0]
+ ]];
+
+ UiContainerX *container = ui_create_container(obj, vbox);
+ vbox.container = container;
+ uic_object_push_container(obj, container);
+
+ return obj;
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "args.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "../ui/container.h"
+
+
+/* ---------------------------- UiDialogArgs ---------------------------- */
+
+UiDialogArgs* ui_dialog_args_new(void) {
+ UiDialogArgs *args = malloc(sizeof(UiDialogArgs));
+ memset(args, 0, sizeof(UiDialogArgs));
+ return args;
+}
+
+void ui_dialog_args_set_title(UiDialogArgs *args, const char *title) {
+ args->title = strdup(title);
+}
+
+void ui_dialog_args_set_content(UiDialogArgs *args, const char *str) {
+ args->content = strdup(str);
+}
+
+void ui_dialog_args_set_button1_label(UiDialogArgs *args, const char *label) {
+ args->button1_label = strdup(label);
+}
+
+void ui_dialog_args_set_button2_label(UiDialogArgs *args, const char *label) {
+ args->button2_label = strdup(label);
+}
+
+void ui_dialog_args_set_closebutton_label(UiDialogArgs *args, const char *label) {
+ args->closebutton_label = strdup(label);
+}
+
+void ui_dialog_args_set_input_value(UiDialogArgs *args, const char *value) {
+ args->input_value = strdup(value);
+}
+
+void ui_dialog_args_set_input(UiDialogArgs *args, UiBool input) {
+ args->input = input;
+}
+
+void ui_dialog_args_set_password(UiDialogArgs *args, UiBool password) {
+ args->password = password;
+}
+
+void ui_dialog_args_set_result(UiDialogArgs *args, ui_callback cb) {
+ args->result = cb;
+}
+
+void ui_dialog_args_set_resultdata(UiDialogArgs *args, void *userdata) {
+ args->resultdata = userdata;
+}
+
+void ui_dialog_args_free(UiDialogArgs *args) {
+ free((void*)args->title);
+ free((void*)args->button1_label);
+ free((void*)args->button2_label);
+ free((void*)args->content);
+ free((void*)args->closebutton_label);
+ free((void*)args->input_value);
+ free(args);
+}
+
+
+/* -------------------------- UiDialogWindowArgs -------------------------- */
+
+UiDialogWindowArgs* ui_dialogwindow_args_new(void) {
+ UiDialogWindowArgs *args = malloc(sizeof(UiDialogWindowArgs));
+ memset(args, 0, sizeof(UiDialogWindowArgs));
+ return args;
+}
+
+void ui_dialogwindow_args_set_modal(UiDialogWindowArgs *args, UiTri value) {
+ args->modal = value;
+}
+
+void ui_dialogwindow_args_set_titlebar_buttons(UiDialogWindowArgs *args, UiTri value) {
+ args->titlebar_buttons = value;
+}
+
+void ui_dialogwindow_args_set_show_closebutton(UiDialogWindowArgs *args, UiTri value) {
+ args->show_closebutton = value;
+}
+
+void ui_dialogwindow_args_set_title(UiDialogWindowArgs *args, const char *title) {
+ args->title = strdup(title);
+}
+
+void ui_dialogwindow_args_set_lbutton1(UiDialogWindowArgs *args, const char *label) {
+ args->lbutton1 = strdup(label);
+}
+
+void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label) {
+ args->lbutton2 = strdup(label);
+}
+
+void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label) {
+ args->rbutton3 = strdup(label);
+}
+
+void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label) {
+ args->rbutton4 = strdup(label);
+}
+
+void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states) {
+ // TODO
+}
+
+void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states) {
+ // TODO
+}
+
+void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states) {
+ // TODO
+}
+
+void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states) {
+ // TODO
+}
+
+void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button) {
+ args->default_button = button;
+}
+
+void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width) {
+ args->width = width;
+}
+
+void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height) {
+ args->height = height;
+}
+
+void ui_dialogwindow_args_set_onclick(UiDialogWindowArgs *args, ui_callback cb) {
+ args->onclick = cb;
+}
+
+void ui_dialogwindow_args_set_onclickdata(UiDialogWindowArgs *args, void *userdata) {
+ args->onclickdata = userdata;
+}
+
+void ui_dialogwindow_args_free(UiDialogWindowArgs *args) {
+ free((void*)args->title);
+ free((void*)args->lbutton1);
+ free((void*)args->lbutton2);
+ free((void*)args->rbutton3);
+ free((void*)args->rbutton4);
+ free((void*)args->lbutton1_groups);
+ free((void*)args->lbutton2_groups);
+ free((void*)args->rbutton3_groups);
+ free((void*)args->rbutton4_groups);
+ free(args);
+}
+
+
+/* ---------------------------- UiMenuItemArgs ---------------------------- */
+
+UiMenuItemArgs* ui_menuitem_args_new(void) {
+ UiMenuItemArgs *args = malloc(sizeof(UiMenuItemArgs));
+ memset(args, 0, sizeof(UiMenuItemArgs));
+ return args;
+}
+
+void ui_menuitem_args_set_label(UiMenuItemArgs *args, const char *label) {
+ args->label = strdup(label);
+}
+
+void ui_menuitem_args_set_icon(UiMenuItemArgs *args, const char *icon) {
+ args->icon = strdup(icon);
+}
+
+void ui_menuitem_args_set_onclick(UiMenuItemArgs *args, ui_callback callback) {
+ args->onclick = callback;
+}
+
+void ui_menuitem_args_set_onclickdata(UiMenuItemArgs *args, void *onclickdata) {
+ args->onclickdata = onclickdata;
+}
+
+void ui_menuitem_args_free(UiMenuItemArgs *args) {
+ free((void*)args->label);
+ free((void*)args->icon);
+ free(args);
+}
+
+
+/* ---------------------------- UiMenuToggleItemArgs ---------------------------- */
+
+UiMenuToggleItemArgs* ui_menutoggleitem_args_new(void) {
+ UiMenuToggleItemArgs *args = malloc(sizeof(UiMenuToggleItemArgs));
+ memset(args, 0, sizeof(UiMenuToggleItemArgs));
+ return args;
+}
+
+void ui_menutoggleitem_args_set_label(UiMenuToggleItemArgs *args, const char *label) {
+ args->label = strdup(label);
+}
+
+void ui_menutoggleitem_args_set_icon(UiMenuToggleItemArgs *args, const char *icon) {
+ args->icon = strdup(icon);
+}
+
+void ui_menutoggleitem_args_set_varname(UiMenuToggleItemArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_menutoggleitem_args_set_onchange(UiMenuToggleItemArgs *args, ui_callback callback) {
+ args->onchange = callback;
+}
+
+void ui_menutoggleitem_args_set_onchangedata(UiMenuToggleItemArgs *args, void *onclickdata) {
+ args->onchangedata = onclickdata;
+}
+
+void ui_menutoggleitem_args_free(UiMenuToggleItemArgs *args) {
+ free((void*)args->label);
+ free((void*)args->icon);
+ free((void*)args->varname);
+ free(args);
+}
+
+/* --------------------------- UiMenuItemListArgs --------------------------- */
+
+UiMenuItemListArgs* ui_menuitemlist_args_new(void) {
+ UiMenuItemListArgs *args = malloc(sizeof(UiMenuItemListArgs));
+ memset(args, 0, sizeof(UiMenuItemListArgs));
+ return args;
+}
+
+void ui_menuitemlist_args_set_varname(UiMenuItemListArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_menuitemlist_args_set_getvalue(UiMenuItemListArgs *args, ui_getvaluefunc func) {
+ args->getvalue = func;
+}
+
+void ui_menuitemlist_args_set_onselect(UiMenuItemListArgs *args, ui_callback callback) {
+ args->onselect = callback;
+}
+
+void ui_menuitemlist_args_set_onselectdata(UiMenuItemListArgs *args, void *data){
+ args->onselectdata = data;
+}
+
+void ui_menuitemlist_args_set_addseparator(UiMenuItemListArgs *args, UiBool value) {
+ args->addseparator = value;
+}
+
+void ui_menuitemlist_args_free(UiMenuItemListArgs *args){
+ free((void*)args->varname);
+ free(args);
+}
+
+/* --------------------------- UiToolbarItemArgs --------------------------- */
+
+UiToolbarItemArgs* ui_toolbar_item_args_new(void) {
+ UiToolbarItemArgs *args = malloc(sizeof(UiToolbarItemArgs));
+ memset(args, 0, sizeof(UiToolbarItemArgs));
+ return args;
+}
+
+void ui_toolbar_item_args_set_label(UiToolbarItemArgs *args, const char *label) {
+ args->label = strdup(label);
+}
+
+void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char *icon) {
+ args->icon = strdup(icon);
+}
+
+void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip) {
+ args->tooltip = strdup(tooltip);
+}
+
+void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback) {
+ args->onclick = callback;
+}
+
+void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclickdata) {
+ args->onclickdata = onclickdata;
+}
+
+void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+void ui_toolbar_item_args_free(UiToolbarItemArgs *args) {
+ free((void*)args->label);
+ free((void*)args->icon);
+ free((void*)args->tooltip);
+ free((void*)args->groups);
+ free(args);
+}
+
+/* ---------------------------- UiToolbarToggleItemArgs ---------------------------- */
+
+UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void) {
+ UiToolbarToggleItemArgs *args = malloc(sizeof(UiToolbarToggleItemArgs));
+ memset(args, 0, sizeof(UiToolbarToggleItemArgs));
+ return args;
+}
+
+void ui_toolbar_toggleitem_args_set_label(UiToolbarToggleItemArgs *args, const char *label) {
+ args->label = strdup(label);
+}
+
+void ui_toolbar_toggleitem_args_set_icon(UiToolbarToggleItemArgs *args, const char *icon) {
+ args->icon = strdup(icon);
+}
+
+void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *args, const char *tooltip) {
+ args->tooltip = strdup(tooltip);
+}
+
+void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback) {
+ args->onchange = callback;
+}
+
+void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata) {
+ args->onchangedata = onchangedata;
+}
+
+void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args,int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) {
+ free((void*)args->label);
+ free((void*)args->icon);
+ free((void*)args->tooltip);
+ free((void*)args->varname);
+ free((void*)args->groups);
+ free(args);
+}
+
+/* ---------------------------- UiToolbarMenuArgs ---------------------------- */
+
+
+UiToolbarMenuArgs* ui_toolbar_menu_args_new(void) {
+ UiToolbarMenuArgs *args = malloc(sizeof(UiToolbarMenuArgs));
+ memset(args, 0, sizeof(UiToolbarMenuArgs));
+ return args;
+}
+
+void ui_toolbar_menu_args_set_label(UiToolbarMenuArgs *args, const char *label) {
+ args->label = strdup(label);
+}
+
+void ui_toolbar_menu_args_set_icon(UiToolbarMenuArgs *args, const char *icon) {
+ args->icon = strdup(icon);
+}
+
+void ui_toolbar_menu_args_set_tooltip(UiToolbarMenuArgs *args, const char *tooltip) {
+ args->tooltip = strdup(tooltip);
+}
+
+void ui_toolbar_menu_args_free(UiToolbarMenuArgs *args) {
+ free((void*)args->label);
+ free((void*)args->icon);
+ free((void*)args->tooltip);
+ free(args);
+}
+
+
+/* ---------------------------- UiContainerArgs ---------------------------- */
+
+UiContainerArgs* ui_container_args_new(void) {
+ UiContainerArgs *args = malloc(sizeof(UiContainerArgs));
+ memset(args, 0, sizeof(UiContainerArgs));
+ return args;
+}
+
+void ui_container_args_set_fill(UiContainerArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_container_args_set_hexpand(UiContainerArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_container_args_set_vexpand(UiContainerArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_container_args_set_hfill(UiContainerArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_container_args_set_vfill(UiContainerArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_container_args_set_override_defaults(UiContainerArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+
+void ui_container_args_set_colspan(UiContainerArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_container_args_set_rowspan(UiContainerArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_container_args_set_def_hexpand(UiContainerArgs *args, UiBool value) {
+ args->def_hexpand = value;
+}
+
+
+void ui_container_args_set_def_vexpand(UiContainerArgs *args, UiBool value) {
+ args->def_vexpand = value;
+}
+
+
+void ui_container_args_set_def_hfill(UiContainerArgs *args, UiBool value) {
+ args->def_hfill = value;
+}
+
+
+void ui_container_args_set_def_vfill(UiContainerArgs *args, UiBool value) {
+ args->def_vfill = value;
+}
+
+
+void ui_container_args_set_name(UiContainerArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_container_args_set_style_class(UiContainerArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+
+void ui_container_args_set_margin(UiContainerArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_container_args_set_margin_left(UiContainerArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_container_args_set_margin_right(UiContainerArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_container_args_set_margin_top(UiContainerArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_container_args_set_margin_bottom(UiContainerArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+
+void ui_container_args_set_spacing(UiContainerArgs *args, int value) {
+ args->spacing = value;
+}
+
+
+void ui_container_args_set_columnspacing(UiContainerArgs *args, int value) {
+ args->columnspacing = value;
+}
+
+
+void ui_container_args_set_rowspacing(UiContainerArgs *args, int value) {
+ args->rowspacing = value;
+}
+
+
+void ui_container_args_free(UiContainerArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free(args);
+}
+
+
+/* ------------------------------- UiFrameArgs ------------------------------*/
+
+UiFrameArgs* ui_frame_args_new(void) {
+ UiFrameArgs *args = malloc(sizeof(UiFrameArgs));
+ memset(args, 0, sizeof(UiContainerArgs));
+ return args;
+}
+
+
+void ui_frame_args_set_fill(UiFrameArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_frame_args_set_hexpand(UiFrameArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_frame_args_set_vexpand(UiFrameArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_frame_args_set_hfill(UiFrameArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_frame_args_set_vfill(UiFrameArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_frame_args_set_override_defaults(UiFrameArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_frame_args_set_margin(UiFrameArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_frame_args_set_margin_left(UiFrameArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_frame_args_set_margin_right(UiFrameArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_frame_args_set_margin_top(UiFrameArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_frame_args_set_margin_bottom(UiFrameArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+
+void ui_frame_args_set_colspan(UiFrameArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_frame_args_set_rowspan(UiFrameArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_frame_args_set_name(UiFrameArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_frame_args_set_style_class(UiFrameArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_frame_args_set_subcontainer(UiFrameArgs *args, UiSubContainerType subcontainer) {
+ args->subcontainer = subcontainer;
+}
+
+void ui_frame_args_set_padding(UiFrameArgs *args, int value) {
+ args->padding = value;
+}
+
+void ui_frame_args_set_spacing(UiFrameArgs *args, int value) {
+ args->spacing = value;
+}
+
+
+void ui_frame_args_set_columnspacing(UiFrameArgs *args, int value) {
+ args->columnspacing = value;
+}
+
+
+void ui_frame_args_set_rowspacing(UiFrameArgs *args, int value) {
+ args->rowspacing = value;
+}
+
+
+void ui_frame_args_set_expanded(UiFrameArgs *args, UiBool value) {
+ args->isexpanded = value;
+}
+
+
+void ui_frame_args_set_label(UiFrameArgs *args, const char *label) {
+ args->label = strdup(label);
+}
+
+void ui_frame_args_free(UiFrameArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->label);
+ free(args);
+}
+
+
+/* ---------------------------- UiSidebarArgs -------------------------------*/
+
+UiSidebarArgs* ui_sidebar_args_new(void) {
+ UiSidebarArgs *args = malloc(sizeof(UiSidebarArgs));
+ memset(args, 0, sizeof(UiSidebarArgs));
+ return args;
+}
+
+void ui_sidebar_args_set_name(UiSidebarArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_sidebar_args_set_style_class(UiSidebarArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_sidebar_args_set_margin(UiSidebarArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_sidebar_args_set_margin_left(UiSidebarArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_sidebar_args_set_margin_right(UiSidebarArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_sidebar_args_set_margin_top(UiSidebarArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_sidebar_args_set_margin_bottom(UiSidebarArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_sidebar_args_set_spacing(UiSidebarArgs *args, int value) {
+ args->spacing = value;
+}
+
+void ui_sidebar_args_free(UiSidebarArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free(args);
+}
+
+
+/* --------------------------- UiSplitPaneArgs ------------------------------*/
+
+UiSplitPaneArgs* ui_splitpane_args_new(void) {
+ UiSplitPaneArgs *args = malloc(sizeof(UiSplitPaneArgs));
+ memset(args, 0, sizeof(UiSplitPaneArgs));
+ return args;
+}
+
+void ui_splitpane_args_set_fill(UiSplitPaneArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_splitpane_args_set_hexpand(UiSplitPaneArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+void ui_splitpane_args_set_vexpand(UiSplitPaneArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+void ui_splitpane_args_set_hfill(UiSplitPaneArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+void ui_splitpane_args_set_vfill(UiSplitPaneArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+void ui_splitpane_args_set_override_defaults(UiSplitPaneArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_splitpane_args_set_colspan(UiSplitPaneArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+void ui_splitpane_args_set_rowspan(UiSplitPaneArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+void ui_splitpane_args_set_name(UiSplitPaneArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_splitpane_args_set_style_class(UiSplitPaneArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_splitpane_args_set_margin_left(UiSplitPaneArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_splitpane_args_set_margin_right(UiSplitPaneArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_splitpane_args_set_margin_top(UiSplitPaneArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_splitpane_args_set_margin_bottom(UiSplitPaneArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+
+void ui_splitpane_args_set_spacing(UiSplitPaneArgs *args, int value) {
+ args->spacing = value;
+}
+
+
+void ui_splitpane_args_set_columnspacing(UiSplitPaneArgs *args, int value) {
+ args->columnspacing = value;
+}
+
+
+void ui_splitpane_args_set_rowspacing(UiSplitPaneArgs *args, int value) {
+ args->rowspacing = value;
+}
+
+
+void ui_splitpane_args_set_initial_position(UiSplitPaneArgs *args, int pos) {
+ args->initial_position = pos;
+}
+
+void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname) {
+ args->position_property = strdup(propname);
+}
+
+void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+
+void ui_splitpane_args_set_value(UiSplitPaneArgs *args, UiInteger *value) {
+ args->value = value;
+}
+
+void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max) {
+ args->max_panes = max;
+}
+
+void ui_splitpane_args_free(UiSplitPaneArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free((void*)args->position_property);
+ free(args);
+}
+
+
+/* ---------------------------- UiTabViewArgs ---------------------------- */
+
+UiTabViewArgs* ui_tabview_args_new(void) {
+ UiTabViewArgs *args = malloc(sizeof(UiTabViewArgs));
+ memset(args, 0, sizeof(UiTabViewArgs));
+ return args;
+}
+
+void ui_tabview_args_set_fill(UiTabViewArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_tabview_args_set_hexpand(UiTabViewArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+void ui_tabview_args_set_vexpand(UiTabViewArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+void ui_tabview_args_set_hfill(UiTabViewArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+void ui_tabview_args_set_vfill(UiTabViewArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+void ui_tabview_args_set_override_defaults(UiTabViewArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_tabview_args_set_colspan(UiTabViewArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+void ui_tabview_args_set_rowspan(UiTabViewArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+void ui_tabview_args_set_name(UiTabViewArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_tabview_args_set_style_class(UiTabViewArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_tabview_args_set_margin(UiTabViewArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_tabview_args_set_margin_left(UiTabViewArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_tabview_args_set_margin_right(UiTabViewArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_tabview_args_set_margin_top(UiTabViewArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_tabview_args_set_margin_bottom(UiTabViewArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_tabview_args_set_padding(UiTabViewArgs *args, int value) {
+ args->padding = value;
+}
+
+void ui_tabview_args_set_spacing(UiTabViewArgs *args, int value) {
+ args->spacing = value;
+}
+
+void ui_tabview_args_set_columnspacing(UiTabViewArgs *args, int value) {
+ args->columnspacing = value;
+}
+
+
+void ui_tabview_args_set_rowspacing(UiTabViewArgs *args, int value) {
+ args->rowspacing = value;
+}
+
+void ui_tabview_args_set_type(UiTabViewArgs *args, UiTabViewType tabview) {
+ args->tabview = tabview;
+}
+
+void ui_tabview_args_set_onchange(UiTabViewArgs *args, ui_callback cb) {
+ args->onchange = cb;
+}
+
+void ui_tabview_args_set_onchangedata(UiTabViewArgs *args, void *userdata) {
+ args->onchangedata = userdata;
+}
+
+void ui_tabview_args_set_varname(UiTabViewArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_tabview_args_set_value(UiTabViewArgs *args, UiInteger *value) {
+ args->value = value;
+}
+
+void ui_tabview_args_set_subcontainer(UiTabViewArgs *args, UiSubContainerType subcontainer) {
+ args->subcontainer = subcontainer;
+}
+
+void ui_tabview_args_free(UiTabViewArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free(args);
+}
+
+
+/* ------------------------- UiWidgetArgs ----------------------------*/
+
+UiWidgetArgs* ui_widget_args_new(void) {
+ UiWidgetArgs *args = malloc(sizeof(UiWidgetArgs));
+ memset(args, 0, sizeof(UiWidgetArgs));
+ return args;
+}
+
+
+void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_widget_args_set_hexpand(UiWidgetArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_widget_args_set_vexpand(UiWidgetArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_widget_args_set_hfill(UiWidgetArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_widget_args_set_vfill(UiWidgetArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_widget_args_set_margin(UiWidgetArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_widget_args_set_margin_left(UiWidgetArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_widget_args_set_margin_right(UiWidgetArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_widget_args_set_margin_top(UiWidgetArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_widget_args_set_margin_bottom(UiWidgetArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_widget_args_set_rowspan(UiWidgetArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_widget_args_set_name(UiWidgetArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_widget_args_set_style_class(UiWidgetArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_widget_args_free(UiWidgetArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free(args);
+}
+
+
+/* ------------------------- UiLabelArgs ----------------------------*/
+
+
+UiLabelArgs* ui_label_args_new(void) {
+ UiLabelArgs *args = malloc(sizeof(UiLabelArgs));
+ memset(args, 0, sizeof(UiLabelArgs));
+ return args;
+}
+
+void ui_label_args_set_fill(UiLabelArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_label_args_set_hexpand(UiLabelArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+void ui_label_args_set_vexpand(UiLabelArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+void ui_label_args_set_hfill(UiLabelArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+void ui_label_args_set_vfill(UiLabelArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+void ui_label_args_set_override_defaults(UiLabelArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_label_args_set_margin(UiLabelArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_label_args_set_margin_left(UiLabelArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_label_args_set_margin_right(UiLabelArgs *args, int value){
+ args->margin_right = value;
+}
+
+void ui_label_args_set_margin_top(UiLabelArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_label_args_set_margin_bottom(UiLabelArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_label_args_set_colspan(UiLabelArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_label_args_set_rowspan(UiLabelArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+void ui_label_args_set_name(UiLabelArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_label_args_set_style_class(UiLabelArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_label_args_set_label(UiLabelArgs *args, const char *label){
+ args->label = strdup(label);
+}
+
+void ui_label_args_set_align(UiLabelArgs *args, UiAlignment align) {
+ args->align = align;
+}
+
+void ui_label_args_set_style(UiLabelArgs *args, UiLabelStyle style) {
+ args->style = style;
+}
+
+void ui_label_args_set_varname(UiLabelArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_label_args_set_value(UiLabelArgs *args, UiString *value) {
+ args->value = value;
+}
+
+void ui_label_args_free(UiLabelArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->label);
+ free((void*)args->varname);
+ free(args);
+}
+
+
+/* ------------------------- UiProgressbarArgs ----------------------------*/
+
+
+UiProgressbarArgs* ui_progressbar_args_new(void) {
+ UiProgressbarArgs *args = malloc(sizeof(UiProgressbarArgs));
+ memset(args, 0, sizeof(UiProgressbarArgs));
+ return args;
+}
+
+
+void ui_progressbar_args_set_fill(UiProgressbarArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_progressbar_args_set_hexpand(UiProgressbarArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_progressbar_args_set_vexpand(UiProgressbarArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_progressbar_args_set_hfill(UiProgressbarArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_progressbar_args_set_vfill(UiProgressbarArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_progressbar_args_set_override_defaults(UiProgressbarArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_progressbar_args_set_margin(UiProgressbarArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_progressbar_args_set_margin_left(UiProgressbarArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_progressbar_args_set_margin_right(UiProgressbarArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_progressbar_args_set_margin_top(UiProgressbarArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_progressbar_args_set_margin_bottom(UiProgressbarArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_progressbar_args_set_colspan(UiProgressbarArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_progressbar_args_set_rowspan(UiProgressbarArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_progressbar_args_set_name(UiProgressbarArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_progressbar_args_set_style_class(UiProgressbarArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_progressbar_args_set_min(UiProgressbarArgs *args, double min) {
+ args->min = min;
+}
+
+void ui_progressbar_args_set_max(UiProgressbarArgs *args, double max) {
+ args->max = max;
+}
+
+void ui_progressbar_args_set_varname(UiProgressbarArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_progressbar_args_set_value(UiProgressbarArgs *args, UiDouble *value) {
+ args->value = value;
+}
+
+void ui_progressbar_args_free(UiProgressbarArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free(args);
+}
+
+
+/* ------------------------- UiProgressbarSpinnerArgs ----------------------------*/
+
+UiProgressbarSpinnerArgs* ui_progress_spinner_args_new(void) {
+ UiProgressbarSpinnerArgs *args = malloc(sizeof(UiProgressbarSpinnerArgs));
+ memset(args, 0, sizeof(UiProgressbarSpinnerArgs));
+ return args;
+}
+
+void ui_progress_spinner_args_set_fill(UiProgressbarSpinnerArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_progress_spinner_args_set_hexpand(UiProgressbarSpinnerArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+void ui_progress_spinner_args_set_vexpand(UiProgressbarSpinnerArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+void ui_progress_spinner_args_set_hfill(UiProgressbarSpinnerArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+void ui_progress_spinner_args_set_vfill(UiProgressbarSpinnerArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+void ui_progress_spinner_args_set_override_defaults(UiProgressbarSpinnerArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_progress_spinner_args_set_margin(UiProgressbarSpinnerArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_progress_spinner_args_set_margin_left(UiProgressbarSpinnerArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_progress_spinner_args_set_margin_right(UiProgressbarSpinnerArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_progress_spinner_args_set_margin_top(UiProgressbarSpinnerArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_progress_spinner_args_set_margin_bottom(UiProgressbarSpinnerArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_progress_spinner_args_set_colspan(UiProgressbarSpinnerArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+void ui_progress_spinner_args_set_rowspan(UiProgressbarSpinnerArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+void ui_progress_spinner_args_set_name(UiProgressbarSpinnerArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_progress_spinner_args_set_style_class(UiProgressbarSpinnerArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_progress_spinner_args_set_varname(UiProgressbarSpinnerArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_progress_spinner_args_set_value(UiProgressbarSpinnerArgs *args, UiInteger *value) {
+ args->value = value;
+}
+
+void ui_progress_spinner_args_free(UiProgressbarSpinnerArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free(args);
+}
+
+
+/* ---------------------------- UiButtonArgs -------------------------------*/
+
+UiButtonArgs* ui_button_args_new(void) {
+ UiButtonArgs *args = malloc(sizeof(UiButtonArgs));
+ memset(args, 0, sizeof(UiButtonArgs));
+ return args;
+}
+
+
+void ui_button_args_set_fill(UiButtonArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_button_args_set_hexpand(UiButtonArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_button_args_set_vexpand(UiButtonArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_button_args_set_hfill(UiButtonArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_button_args_set_vfill(UiButtonArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_button_args_set_override_defaults(UiButtonArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_button_args_set_margin(UiButtonArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_button_args_set_margin_left(UiButtonArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_button_args_set_margin_right(UiButtonArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_button_args_set_margin_top(UiButtonArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_button_args_set_margin_bottom(UiButtonArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_button_args_set_colspan(UiButtonArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_button_args_set_rowspan(UiButtonArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+void ui_button_args_set_name(UiButtonArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_button_args_set_style_class(UiButtonArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_button_args_set_label(UiButtonArgs *args, const char *label){
+ args->label = strdup(label);
+}
+
+void ui_button_args_set_icon(UiButtonArgs *args, const char *icon){
+ args->icon = strdup(icon);
+}
+
+void ui_button_args_set_tooltip(UiButtonArgs *args, const char *tooltip) {
+ args->tooltip = strdup(tooltip);
+}
+
+void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype){
+ args->labeltype = labeltype;
+}
+
+void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback){
+ args->onclick = callback;
+}
+
+void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata){
+ args->onclickdata = onclickdata;
+}
+
+void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_button_args_free(UiButtonArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->label);
+ free((void*)args->icon);
+ free((void*)args->tooltip);
+ free((void*)args->groups);
+ free(args);
+}
+
+
+/* ------------------------- UiToggleArgs ----------------------------*/
+
+
+UiToggleArgs* ui_toggle_args_new(void) {
+ UiToggleArgs *args = malloc(sizeof(UiToggleArgs));
+ memset(args, 0, sizeof(UiToggleArgs));
+ return args;
+}
+
+void ui_toggle_args_set_fill(UiToggleArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_toggle_args_set_hexpand(UiToggleArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+void ui_toggle_args_set_vexpand(UiToggleArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+void ui_toggle_args_set_hfill(UiToggleArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+void ui_toggle_args_set_vfill(UiToggleArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+void ui_toggle_args_set_override_defaults(UiToggleArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_toggle_args_set_margin(UiToggleArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_toggle_args_set_margin_left(UiToggleArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_toggle_args_set_margin_right(UiToggleArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_toggle_args_set_margin_top(UiToggleArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_toggle_args_set_margin_bottom(UiToggleArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_toggle_args_set_colspan(UiToggleArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+void ui_toggle_args_set_rowspan(UiToggleArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_toggle_args_set_name(UiToggleArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_toggle_args_set_style_class(UiToggleArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_toggle_args_set_label(UiToggleArgs *args, const char *label){
+ args->label = strdup(label);
+}
+
+void ui_toggle_args_set_icon(UiToggleArgs *args, const char *icon){
+ args->icon = strdup(icon);
+}
+
+void ui_toggle_args_set_tooltip(UiToggleArgs *args, const char *tooltip) {
+ args->tooltip = strdup(tooltip);
+}
+
+void ui_toggle_args_set_labeltype(UiToggleArgs *args, int labeltype){
+ args->labeltype = labeltype;
+}
+
+void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callback){
+ args->onchange = callback;
+}
+
+void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata){
+ args->onchangedata = onchangedata;
+}
+
+void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value) {
+ args->value = value;
+}
+
+void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group) {
+ args->enable_group = group;
+}
+
+void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_toggle_args_free(UiToggleArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->label);
+ free((void*)args->icon);
+ free((void*)args->tooltip);
+ free((void*)args->varname);
+ free((void*)args->groups);
+ free(args);
+}
+
+/* ------------------------- UiLinkButtonArgs ----------------------------*/
+
+
+UiLinkButtonArgs* ui_linkbutton_args_new(void) {
+ UiLinkButtonArgs *args = malloc(sizeof(UiLinkButtonArgs));
+ memset(args, 0, sizeof(UiLinkButtonArgs));
+ return args;
+}
+
+
+void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_linkbutton_args_set_hexpand(UiLinkButtonArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_linkbutton_args_set_vexpand(UiLinkButtonArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_linkbutton_args_set_hfill(UiLinkButtonArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_linkbutton_args_set_vfill(UiLinkButtonArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_linkbutton_args_set_margin(UiLinkButtonArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_linkbutton_args_set_margin_left(UiLinkButtonArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_linkbutton_args_set_margin_right(UiLinkButtonArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_linkbutton_args_set_margin_top(UiLinkButtonArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_linkbutton_args_set_margin_bottom(UiLinkButtonArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_linkbutton_args_set_rowspan(UiLinkButtonArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_linkbutton_args_set_name(UiLinkButtonArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_linkbutton_args_set_style_class(UiLinkButtonArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_linkbutton_args_set_label(UiLinkButtonArgs *args, const char *label){
+ args->label = strdup(label);
+}
+
+void ui_linkbutton_args_set_uri(UiLinkButtonArgs *args, const char *uri) {
+ args->uri = strdup(uri);
+}
+
+void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback callback) {
+ args->onclick = callback;
+}
+
+void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata) {
+ args->onclickdata = userdata;
+}
+
+void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value) {
+ args->nofollow = value;
+}
+
+void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type) {
+ args->type = type;
+}
+
+void ui_linkbutton_args_set_varname(UiLinkButtonArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value) {
+ args->value = value;
+}
+
+void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_linkbutton_args_free(UiLinkButtonArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->label);
+ free((void*)args->uri);
+ free((void*)args->varname);
+ free((void*)args->groups);
+ free(args);
+}
+
+
+/* ------------------------- UiListArgs ----------------------------*/
+
+UiListArgs* ui_list_args_new(void) {
+ UiListArgs *args = malloc(sizeof(UiListArgs));
+ memset(args, 0, sizeof(UiListArgs));
+ return args;
+}
+
+void ui_list_args_set_fill(UiListArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_list_args_set_hexpand(UiListArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+void ui_list_args_set_vexpand(UiListArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+void ui_list_args_set_hfill(UiListArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+void ui_list_args_set_vfill(UiListArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+void ui_list_args_set_override_defaults(UiListArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_list_args_set_margin(UiListArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_list_args_set_margin_left(UiListArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_list_args_set_margin_right(UiListArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_list_args_set_margin_top(UiListArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_list_args_set_margin_bottom(UiListArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_list_args_set_colspan(UiListArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+void ui_list_args_set_rowspan(UiListArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+void ui_list_args_set_name(UiListArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_list_args_set_style_class(UiListArgs *args, const char *classname) {
+ args->style_class = classname;
+}
+
+void ui_list_args_set_varname(UiListArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_list_args_set_value(UiListArgs *args, UiList *value) {
+ args->list = value;
+}
+
+void ui_list_args_set_model(UiListArgs *args, UiModel *model) {
+ args->model = model;
+}
+
+void ui_list_args_set_static_elements(UiListArgs *args, char **strarray, size_t nelm) {
+ char **array = calloc(nelm, sizeof(char*));
+ for(int i=0;i<nelm;i++) {
+ array[i] = strdup(strarray[i]);
+ }
+ args->static_elements = array;
+ args->static_nelm = nelm;
+}
+
+void ui_list_args_set_getvalue_func(UiListArgs *args, ui_getvaluefunc getvalue) {
+ args->getvalue = getvalue;
+}
+
+void ui_list_args_set_getvalue_func2(UiListArgs *args, ui_getvaluefunc2 getvalue) {
+ args->getvalue2 = getvalue;
+}
+
+void ui_list_args_set_getvalue_data(UiListArgs *args, void *userdata) {
+ args->getvalue2data = userdata;
+}
+
+void ui_list_args_set_getstyle_func(UiListArgs *args, ui_getstylefunc getstyle) {
+ args->getstyle = getstyle;
+}
+
+void ui_list_args_set_getstyle_data(UiListArgs *args, void *userdata) {
+ args->getstyledata = userdata;
+}
+
+void ui_list_args_set_onactivate(UiListArgs *args, ui_callback callback) {
+ args->onactivate = callback;
+}
+
+void ui_list_args_set_onactivatedata(UiListArgs *args, void *userdata) {
+ args->onactivatedata = userdata;
+}
+
+void ui_list_args_set_onselection(UiListArgs *args, ui_callback callback) {
+ args->onselection = callback;
+}
+
+void ui_list_args_set_onselectiondata(UiListArgs *args, void *userdata) {
+ args->onselectiondata = userdata;
+}
+
+void ui_list_args_set_ondragstart(UiListArgs *args, ui_callback callback) {
+ args->ondragstart = callback;
+}
+
+void ui_list_args_set_ondragstartdata(UiListArgs *args, void *userdata) {
+ args->ondragstartdata = userdata;
+}
+
+void ui_list_args_set_ondragcomplete(UiListArgs *args, ui_callback callback) {
+ args->ondragcomplete = callback;
+}
+
+void ui_list_args_set_ondragcompletedata(UiListArgs *args, void *userdata) {
+ args->ondragcompletedata = userdata;
+}
+
+void ui_list_args_set_ondrop(UiListArgs *args, ui_callback callback) {
+ args->ondrop = callback;
+}
+
+void ui_list_args_set_ondropdata(UiListArgs *args, void *userdata) {
+ args->ondropdata = userdata;
+}
+
+void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave) {
+ args->onsave = onsave;
+}
+
+void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata) {
+ args->onsavedata = userdata;
+}
+
+void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection) {
+ args->multiselection = multiselection;
+}
+
+void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder) {
+ args->contextmenu = menubuilder;
+}
+
+void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_list_args_free(UiListArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ if(args->static_elements) {
+ for(int i=0;i<args->static_nelm;i++) {
+ free(args->static_elements[i]);
+ }
+ free(args->static_elements);
+ }
+ free((void*)args->groups);
+ free(args);
+}
+
+
+
+/* ---------------------- SurceList ------------------------------------- */
+
+UiSourceListArgs* ui_sourcelist_args_new(void) {
+ UiSourceListArgs *args = malloc(sizeof(UiSourceListArgs));
+ memset(args, 0, sizeof(UiSourceListArgs));
+ return args;
+}
+
+
+void ui_sourcelist_args_set_fill(UiSourceListArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_sourcelist_args_set_hexpand(UiSourceListArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_sourcelist_args_set_vexpand(UiSourceListArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_sourcelist_args_set_hfill(UiSourceListArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_sourcelist_args_set_vfill(UiSourceListArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_sourcelist_args_set_override_defaults(UiSourceListArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_sourcelist_args_set_margin(UiSourceListArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_sourcelist_args_set_margin_left(UiSourceListArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_sourcelist_args_set_margin_right(UiSourceListArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_sourcelist_args_set_margin_top(UiSourceListArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_sourcelist_args_set_margin_bottom(UiSourceListArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_sourcelist_args_set_colspan(UiSourceListArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_sourcelist_args_set_rowspan(UiSourceListArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_sourcelist_args_set_name(UiSourceListArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_sourcelist_args_set_style_class(UiSourceListArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+UIEXPORT void ui_sourcelist_args_set_static_sublists(UiSourceListArgs *args, UiSubList *sublists, size_t numsublists) {
+ args->sublists = calloc(numsublists, sizeof(UiSubList));
+ memcpy(args->sublists, sublists, numsublists * sizeof(UiSubList));
+ args->numsublists = numsublists;
+}
+
+void ui_sourcelist_args_set_varname(UiSourceListArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+
+void ui_sourcelist_args_set_dynamic_sublists(UiSourceListArgs *args, UiList *value) {
+ args->dynamic_sublist = value;
+}
+
+
+void ui_sourcelist_args_set_getvalue_func(UiSourceListArgs *args, ui_sublist_getvalue_func getvalue) {
+ args->getvalue = getvalue;
+}
+
+void ui_sourcelist_args_set_getvalue_userdata(UiSourceListArgs *args, void *userdata) {
+ args->getvaluedata = userdata;
+}
+
+void ui_sourcelist_args_set_onactivate(UiSourceListArgs *args, ui_callback callback) {
+ args->onactivate = callback;
+}
+
+
+void ui_sourcelist_args_set_onactivatedata(UiSourceListArgs *args, void *userdata) {
+ args->onactivatedata = userdata;
+}
+
+
+void ui_sourcelist_args_set_onbuttonclick(UiSourceListArgs *args, ui_callback callback) {
+ args->onbuttonclick = callback;
+
+}
+
+
+void ui_sourcelist_args_set_onbuttonclickdata(UiSourceListArgs *args, void *userdata) {
+ args->onbuttonclickdata = userdata;
+}
+
+void ui_sourcelist_args_set_contextmenu(UiSourceListArgs *args, UiMenuBuilder *menubuilder) {
+ args->contextmenu = menubuilder;
+}
+
+void ui_sourcelist_args_set_header_is_item(UiSourceListArgs *args, UiBool value) {
+ args->header_is_item = value;
+}
+
+void ui_sourcelist_args_free(UiSourceListArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free((void*)args->sublists);
+ free((void*)args->groups);
+ free(args);
+}
+
+
+/* ------------------------- UiTextAreaArgs ----------------------------*/
+
+UiTextAreaArgs* ui_textarea_args_new(void) {
+ UiTextAreaArgs *args = malloc(sizeof(UiTextAreaArgs));
+ memset(args, 0, sizeof(UiTextAreaArgs));
+ return args;
+}
+
+
+void ui_textarea_args_set_fill(UiTextAreaArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_textarea_args_set_hexpand(UiTextAreaArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_textarea_args_set_vexpand(UiTextAreaArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_textarea_args_set_hfill(UiTextAreaArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_textarea_args_set_vfill(UiTextAreaArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_textarea_args_set_override_defaults(UiTextAreaArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_textarea_args_set_margin(UiTextAreaArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_textarea_args_set_margin_left(UiTextAreaArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_textarea_args_set_margin_right(UiTextAreaArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_textarea_args_set_margin_top(UiTextAreaArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_textarea_args_set_margin_bottom(UiTextAreaArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+
+void ui_textarea_args_set_colspan(UiTextAreaArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_textarea_args_set_rowspan(UiTextAreaArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_textarea_args_set_name(UiTextAreaArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_textarea_args_set_style_class(UiTextAreaArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_textarea_args_set_onchange(UiTextAreaArgs *args, ui_callback callback){
+ args->onchange = callback;
+}
+
+
+void ui_textarea_args_set_onchangedata(UiTextAreaArgs *args, void *onchangedata){
+ args->onchangedata = onchangedata;
+}
+
+void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value) {
+ args->value = value;
+}
+
+void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_textarea_args_free(UiTextAreaArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free((void*)args->groups);
+ free(args);
+}
+
+
+
+/* ------------------------- UiTextFieldArgs ----------------------------*/
+
+UiTextFieldArgs* ui_textfield_args_new(void) {
+ UiTextFieldArgs *args = malloc(sizeof(UiTextFieldArgs));
+ memset(args, 0, sizeof(UiTextFieldArgs));
+ return args;
+}
+
+
+void ui_textfield_args_set_fill(UiTextFieldArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_textfield_args_set_hexpand(UiTextFieldArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_textfield_args_set_vexpand(UiTextFieldArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_textfield_args_set_hfill(UiTextFieldArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_textfield_args_set_vfill(UiTextFieldArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_textfield_args_set_override_defaults(UiTextFieldArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_textfield_args_set_margin(UiTextFieldArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_textfield_args_set_margin_left(UiTextFieldArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_textfield_args_set_margin_right(UiTextFieldArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_textfield_args_set_margin_top(UiTextFieldArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_textfield_args_set_margin_bottom(UiTextFieldArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+
+void ui_textfield_args_set_colspan(UiTextFieldArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_textfield_args_set_rowspan(UiTextFieldArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_textfield_args_set_name(UiTextFieldArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_textfield_args_set_style_class(UiTextFieldArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_textfield_args_set_onchange(UiTextFieldArgs *args, ui_callback callback){
+ args->onchange = callback;
+}
+
+
+void ui_textfield_args_set_onchangedata(UiTextFieldArgs *args, void *onchangedata){
+ args->onchangedata = onchangedata;
+}
+
+void ui_textfield_args_set_onactivate(UiTextFieldArgs *args, ui_callback callback){
+ args->onactivate = callback;
+}
+
+
+void ui_textfield_args_set_onactivatedata(UiTextFieldArgs *args, void *onactivatedata){
+ args->onactivatedata = onactivatedata;
+}
+
+void ui_textfield_args_set_varname(UiTextFieldArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value) {
+ args->value = value;
+}
+
+void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_textfield_args_free(UiTextFieldArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free((void*)args->groups);
+ free(args);
+}
+
+
+/* ------------------------- UiSpinBoxArgs ----------------------------*/
+
+UiSpinBoxArgs* ui_spinbox_args_new(void) {
+ UiSpinBoxArgs *args = malloc(sizeof(UiSpinBoxArgs));
+ memset(args, 0, sizeof(UiSpinBoxArgs));
+ return args;
+}
+
+void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback) {
+ args->onchange = callback;
+}
+
+void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata) {
+ args->onchangedata = onchangedata;
+}
+
+void ui_spinbox_args_set_margin(UiSpinBoxArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_spinbox_args_set_margin_left(UiSpinBoxArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_spinbox_args_set_margin_right(UiSpinBoxArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_spinbox_args_set_margin_top(UiSpinBoxArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_spinbox_args_set_margin_bottom(UiSpinBoxArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min) {
+ args->min = min;
+}
+
+void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max) {
+ args->max = max;
+}
+
+void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step) {
+ args->step = step;
+}
+
+void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits) {
+ args->digits = digits;
+}
+
+void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value) {
+ args->intvalue = value;
+}
+
+void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value) {
+ args->doublevalue = value;
+}
+
+void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value) {
+ args->rangevalue = value;
+}
+
+void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_spinbox_args_free(UiSpinBoxArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free((void*)args->groups);
+ free(args);
+}
+
+
+/* ------------------------- UiWebviewArgs ----------------------------*/
+
+UiWebviewArgs* ui_webview_args_new(void) {
+ UiWebviewArgs *args = malloc(sizeof(UiWebviewArgs));
+ memset(args, 0, sizeof(UiWebviewArgs));
+ return args;
+}
+
+
+void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill) {
+ args->fill = fill;
+}
+
+
+void ui_webview_args_set_hexpand(UiWebviewArgs *args, UiBool value) {
+ args->hexpand = value;
+}
+
+
+void ui_webview_args_set_vexpand(UiWebviewArgs *args, UiBool value) {
+ args->vexpand = value;
+}
+
+
+void ui_webview_args_set_hfill(UiWebviewArgs *args, UiBool value) {
+ args->hfill = value;
+}
+
+
+void ui_webview_args_set_vfill(UiWebviewArgs *args, UiBool value) {
+ args->vfill = value;
+}
+
+
+void ui_webview_args_set_override_defaults(UiWebviewArgs *args, UiBool value) {
+ args->override_defaults = value;
+}
+
+void ui_webview_args_set_margin(UiWebviewArgs *args, int value) {
+ args->margin = value;
+}
+
+void ui_webview_args_set_margin_left(UiWebviewArgs *args, int value) {
+ args->margin_left = value;
+}
+
+void ui_webview_args_set_margin_right(UiWebviewArgs *args, int value) {
+ args->margin_right = value;
+}
+
+void ui_webview_args_set_margin_top(UiWebviewArgs *args, int value) {
+ args->margin_top = value;
+}
+
+void ui_webview_args_set_margin_bottom(UiWebviewArgs *args, int value) {
+ args->margin_bottom = value;
+}
+
+void ui_webview_args_set_colspan(UiWebviewArgs *args, int colspan) {
+ args->colspan = colspan;
+}
+
+
+void ui_webview_args_set_rowspan(UiWebviewArgs *args, int rowspan) {
+ args->rowspan = rowspan;
+}
+
+
+void ui_webview_args_set_name(UiWebviewArgs *args, const char *name) {
+ args->name = strdup(name);
+}
+
+
+void ui_webview_args_set_style_class(UiWebviewArgs *args, const char *classname) {
+ args->style_class = strdup(classname);
+}
+
+void ui_webview_args_set_varname(UiWebviewArgs *args, const char *varname) {
+ args->varname = strdup(varname);
+}
+
+void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value) {
+ args->value = value;
+}
+
+void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates) {
+ args->groups = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->groups, states, numstates * sizeof(int));
+ ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_webview_args_free(UiWebviewArgs *args) {
+ free((void*)args->name);
+ free((void*)args->style_class);
+ free((void*)args->varname);
+ free((void*)args->groups);
+ free(args);
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_ARGS_H
+#define UIC_ARGS_H
+
+#include "../ui/window.h"
+#include "../ui/container.h"
+#include "../ui/display.h"
+#include "../ui/button.h"
+#include "../ui/entry.h"
+#include "../ui/menu.h"
+#include "../ui/toolbar.h"
+#include "../ui/tree.h"
+#include "../ui/text.h"
+#include "../ui/webview.h"
+#include "../ui/widget.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+UIEXPORT UiDialogArgs* ui_dialog_args_new(void);
+UIEXPORT void ui_dialog_args_set_title(UiDialogArgs *args, const char *title);
+UIEXPORT void ui_dialog_args_set_content(UiDialogArgs *args, const char *str);
+UIEXPORT void ui_dialog_args_set_button1_label(UiDialogArgs *args, const char *label);
+UIEXPORT void ui_dialog_args_set_button2_label(UiDialogArgs *args, const char *label);
+UIEXPORT void ui_dialog_args_set_closebutton_label(UiDialogArgs *args, const char *label);
+UIEXPORT void ui_dialog_args_set_input_value(UiDialogArgs *args, const char *value);
+UIEXPORT void ui_dialog_args_set_input(UiDialogArgs *args, UiBool input);
+UIEXPORT void ui_dialog_args_set_password(UiDialogArgs *args, UiBool password);
+UIEXPORT void ui_dialog_args_set_result(UiDialogArgs *args, ui_callback cb);
+UIEXPORT void ui_dialog_args_set_resultdata(UiDialogArgs *args, void *userdata);
+UIEXPORT void ui_dialog_args_free(UiDialogArgs *args);
+
+UIEXPORT UiDialogWindowArgs* ui_dialogwindow_args_new(void);
+UIEXPORT void ui_dialogwindow_args_set_modal(UiDialogWindowArgs *args, UiTri value);
+UIEXPORT void ui_dialogwindow_args_set_titlebar_buttons(UiDialogWindowArgs *args, UiTri value);
+UIEXPORT void ui_dialogwindow_args_set_show_closebutton(UiDialogWindowArgs *args, UiTri value);
+UIEXPORT void ui_dialogwindow_args_set_title(UiDialogWindowArgs *args, const char *title);
+UIEXPORT void ui_dialogwindow_args_set_lbutton1(UiDialogWindowArgs *args, const char *label);
+UIEXPORT void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label);
+UIEXPORT void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label);
+UIEXPORT void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label);
+UIEXPORT void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states);
+UIEXPORT void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states);
+UIEXPORT void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states);
+UIEXPORT void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states);
+UIEXPORT void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button);
+UIEXPORT void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width);
+UIEXPORT void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height);
+UIEXPORT void ui_dialogwindow_args_set_onclick(UiDialogWindowArgs *args, ui_callback cb);
+UIEXPORT void ui_dialogwindow_args_set_onclickdata(UiDialogWindowArgs *args, void *userdata);
+UIEXPORT void ui_dialogwindow_args_free(UiDialogWindowArgs *args);
+
+UIEXPORT UiMenuItemArgs* ui_menuitem_args_new(void);
+UIEXPORT void ui_menuitem_args_set_label(UiMenuItemArgs *args, const char *label);
+UIEXPORT void ui_menuitem_args_set_icon(UiMenuItemArgs *args, const char *icon);
+UIEXPORT void ui_menuitem_args_set_onclick(UiMenuItemArgs *args, ui_callback callback);
+UIEXPORT void ui_menuitem_args_set_onclickdata(UiMenuItemArgs *args, void *onclickdata);
+UIEXPORT void ui_menuitem_args_free(UiMenuItemArgs *args);
+
+UIEXPORT UiMenuToggleItemArgs* ui_menutoggleitem_args_new(void);
+UIEXPORT void ui_menutoggleitem_args_set_label(UiMenuToggleItemArgs *args, const char *label);
+UIEXPORT void ui_menutoggleitem_args_set_icon(UiMenuToggleItemArgs *args, const char *icon);
+UIEXPORT void ui_menutoggleitem_args_set_varname(UiMenuToggleItemArgs *args, const char *varname);
+UIEXPORT void ui_menutoggleitem_args_set_onchange(UiMenuToggleItemArgs *args, ui_callback callback);
+UIEXPORT void ui_menutoggleitem_args_set_onchangedata(UiMenuToggleItemArgs *args, void *onchangedata);
+UIEXPORT void ui_menutoggleitem_args_free(UiMenuToggleItemArgs *args);
+
+UIEXPORT UiMenuItemListArgs* ui_menuitemlist_args_new(void);
+UIEXPORT void ui_menuitemlist_args_set_varname(UiMenuItemListArgs *args, const char *varname);
+UIEXPORT void ui_menuitemlist_args_set_getvalue(UiMenuItemListArgs *args, ui_getvaluefunc func);
+UIEXPORT void ui_menuitemlist_args_set_onselect(UiMenuItemListArgs *args, ui_callback callback);
+UIEXPORT void ui_menuitemlist_args_set_onselectdata(UiMenuItemListArgs *args, void *data);
+UIEXPORT void ui_menuitemlist_args_set_addseparator(UiMenuItemListArgs *args, UiBool value);
+UIEXPORT void ui_menuitemlist_args_free(UiMenuItemListArgs *args);
+
+UIEXPORT UiToolbarItemArgs* ui_toolbar_item_args_new(void);
+UIEXPORT void ui_toolbar_item_args_set_label(UiToolbarItemArgs *args, const char *label);
+UIEXPORT void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char *icon);
+UIEXPORT void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip);
+UIEXPORT void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback);
+UIEXPORT void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclickdata);
+UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates);
+UIEXPORT void ui_toolbar_item_args_free(UiToolbarItemArgs *args);
+
+UIEXPORT UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void);
+UIEXPORT void ui_toolbar_toggleitem_args_set_label(UiToolbarToggleItemArgs *args, const char *label);
+UIEXPORT void ui_toolbar_toggleitem_args_set_icon(UiToolbarToggleItemArgs *args, const char *icon);
+UIEXPORT void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *args, const char *tooltip);
+UIEXPORT void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname);
+UIEXPORT void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback);
+UIEXPORT void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata);
+UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *states, int numstates);
+UIEXPORT void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args);
+
+UIEXPORT UiToolbarMenuArgs* ui_toolbar_menu_args_new(void);
+UIEXPORT void ui_toolbar_menu_args_set_label(UiToolbarMenuArgs *args, const char *label);
+UIEXPORT void ui_toolbar_menu_args_set_icon(UiToolbarMenuArgs *args, const char *icon);
+UIEXPORT void ui_toolbar_menu_args_set_tooltip(UiToolbarMenuArgs *args, const char *tooltip);
+UIEXPORT void ui_toolbar_menu_args_free(UiToolbarMenuArgs *args);
+
+UIEXPORT UiContainerArgs* ui_container_args_new(void);
+UIEXPORT void ui_container_args_set_fill(UiContainerArgs *args, UiBool fill);
+UIEXPORT void ui_container_args_set_hexpand(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_vexpand(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_hfill(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_vfill(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_override_defaults(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_margin(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_left(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_top(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_colspan(UiContainerArgs *args, int colspan);
+UIEXPORT void ui_container_args_set_rowspan(UiContainerArgs *args, int rowspan);
+UIEXPORT void ui_container_args_set_def_hexpand(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_def_vexpand(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_def_hfill(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_def_vfill(UiContainerArgs *args, UiBool value);
+UIEXPORT void ui_container_args_set_name(UiContainerArgs *args, const char *name);
+UIEXPORT void ui_container_args_set_style_class(UiContainerArgs *args, const char *classname);
+UIEXPORT void ui_container_args_set_spacing(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_columnspacing(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_rowspacing(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_free(UiContainerArgs *args);
+
+UIEXPORT UiFrameArgs* ui_frame_args_new(void);
+UIEXPORT void ui_frame_args_set_fill(UiFrameArgs *args, UiBool fill);
+UIEXPORT void ui_frame_args_set_hexpand(UiFrameArgs *args, UiBool value);
+UIEXPORT void ui_frame_args_set_vexpand(UiFrameArgs *args, UiBool value);
+UIEXPORT void ui_frame_args_set_hfill(UiFrameArgs *args, UiBool value);
+UIEXPORT void ui_frame_args_set_vfill(UiFrameArgs *args, UiBool value);
+UIEXPORT void ui_frame_args_set_override_defaults(UiFrameArgs *args, UiBool value);
+UIEXPORT void ui_frame_args_set_margin(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_left(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_right(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_top(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_margin_bottom(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_colspan(UiFrameArgs *args, int colspan);
+UIEXPORT void ui_frame_args_set_rowspan(UiFrameArgs *args, int rowspan);
+UIEXPORT void ui_frame_args_set_name(UiFrameArgs *args, const char *name);
+UIEXPORT void ui_frame_args_set_style_class(UiFrameArgs *args, const char *classname);
+UIEXPORT void ui_frame_args_set_subcontainer(UiFrameArgs *args, UiSubContainerType subcontainer);
+UIEXPORT void ui_frame_args_set_padding(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_spacing(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_columnspacing(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_rowspacing(UiFrameArgs *args, int value);
+UIEXPORT void ui_frame_args_set_expanded(UiFrameArgs *args, UiBool value);
+UIEXPORT void ui_frame_args_set_label(UiFrameArgs *args, const char *label);
+UIEXPORT void ui_frame_args_free(UiFrameArgs *args);
+
+UIEXPORT UiSidebarArgs* ui_sidebar_args_new(void);
+UIEXPORT void ui_sidebar_args_set_name(UiSidebarArgs *args, const char *name);
+UIEXPORT void ui_sidebar_args_set_style_class(UiSidebarArgs *args, const char *classname);
+UIEXPORT void ui_sidebar_args_set_margin(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_left(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_right(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_top(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_margin_bottom(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_set_spacing(UiSidebarArgs *args, int value);
+UIEXPORT void ui_sidebar_args_free(UiSidebarArgs *args);
+
+UIEXPORT UiSplitPaneArgs* ui_splitpane_args_new(void);
+UIEXPORT void ui_splitpane_args_set_fill(UiSplitPaneArgs *args, UiBool fill);
+UIEXPORT void ui_splitpane_args_set_hexpand(UiSplitPaneArgs *args, UiBool value);
+UIEXPORT void ui_splitpane_args_set_vexpand(UiSplitPaneArgs *args, UiBool value);
+UIEXPORT void ui_splitpane_args_set_hfill(UiSplitPaneArgs *args, UiBool value);
+UIEXPORT void ui_splitpane_args_set_vfill(UiSplitPaneArgs *args, UiBool value);
+UIEXPORT void ui_splitpane_args_set_override_defaults(UiSplitPaneArgs *args, UiBool value);
+UIEXPORT void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_left(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_right(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_top(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_margin_bottom(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_colspan(UiSplitPaneArgs *args, int colspan);
+UIEXPORT void ui_splitpane_args_set_rowspan(UiSplitPaneArgs *args, int rowspan);
+UIEXPORT void ui_splitpane_args_set_name(UiSplitPaneArgs *args, const char *name);
+UIEXPORT void ui_splitpane_args_set_style_class(UiSplitPaneArgs *args, const char *classname);
+UIEXPORT void ui_splitpane_args_set_spacing(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_columnspacing(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_rowspacing(UiSplitPaneArgs *args, int value);
+UIEXPORT void ui_splitpane_args_set_initial_position(UiSplitPaneArgs *args, int pos);
+UIEXPORT void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname);
+UIEXPORT void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname);
+UIEXPORT void ui_splitpane_args_set_value(UiSplitPaneArgs *args, UiInteger *value);
+UIEXPORT void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max);
+UIEXPORT void ui_splitpane_args_free(UiSplitPaneArgs *args);
+
+UIEXPORT UiTabViewArgs* ui_tabview_args_new(void);
+UIEXPORT void ui_tabview_args_set_fill(UiTabViewArgs *args, UiBool fill);
+UIEXPORT void ui_tabview_args_set_hexpand(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_vexpand(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_hfill(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_vfill(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_override_defaults(UiTabViewArgs *args, UiBool value);
+UIEXPORT void ui_tabview_args_set_margin(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_left(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_right(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_top(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_margin_bottom(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_colspan(UiTabViewArgs *args, int colspan);
+UIEXPORT void ui_tabview_args_set_rowspan(UiTabViewArgs *args, int rowspan);
+UIEXPORT void ui_tabview_args_set_name(UiTabViewArgs *args, const char *name);
+UIEXPORT void ui_tabview_args_set_style_class(UiTabViewArgs *args, const char *classname);
+UIEXPORT void ui_tabview_args_set_padding(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_spacing(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_columnspacing(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_rowspacing(UiTabViewArgs *args, int value);
+UIEXPORT void ui_tabview_args_set_type(UiTabViewArgs *args, UiTabViewType tabview);
+UIEXPORT void ui_tabview_args_set_onchange(UiTabViewArgs *args, ui_callback cb);
+UIEXPORT void ui_tabview_args_set_onchangedata(UiTabViewArgs *args, void *userdata);
+UIEXPORT void ui_tabview_args_set_varname(UiTabViewArgs *args, const char *varname);
+UIEXPORT void ui_tabview_args_set_value(UiTabViewArgs *args, UiInteger *value);
+UIEXPORT void ui_tabview_args_set_subcontainer(UiTabViewArgs *args, UiSubContainerType subcontainer);
+UIEXPORT void ui_tabview_args_free(UiTabViewArgs *args);
+
+UIEXPORT UiWidgetArgs* ui_widget_args_new(void);
+UIEXPORT void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill);
+UIEXPORT void ui_widget_args_set_hexpand(UiWidgetArgs *args, UiBool value);
+UIEXPORT void ui_widget_args_set_vexpand(UiWidgetArgs *args, UiBool value);
+UIEXPORT void ui_widget_args_set_hfill(UiWidgetArgs *args, UiBool value);
+UIEXPORT void ui_widget_args_set_vfill(UiWidgetArgs *args, UiBool value);
+UIEXPORT void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value);
+UIEXPORT void ui_widget_args_set_margin(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_left(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_right(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_top(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_margin_bottom(UiWidgetArgs *args, int value);
+UIEXPORT void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan);
+UIEXPORT void ui_widget_args_set_rowspan(UiWidgetArgs *args, int rowspan);
+UIEXPORT void ui_widget_args_set_name(UiWidgetArgs *args, const char *name);
+UIEXPORT void ui_widget_args_set_style_class(UiWidgetArgs *args, const char *classname);
+UIEXPORT void ui_widget_args_free(UiWidgetArgs *args);
+
+UIEXPORT UiLabelArgs* ui_label_args_new(void);
+UIEXPORT void ui_label_args_set_fill(UiLabelArgs *args, UiBool fill);
+UIEXPORT void ui_label_args_set_hexpand(UiLabelArgs *args, UiBool value);
+UIEXPORT void ui_label_args_set_vexpand(UiLabelArgs *args, UiBool value);
+UIEXPORT void ui_label_args_set_hfill(UiLabelArgs *args, UiBool value);
+UIEXPORT void ui_label_args_set_vfill(UiLabelArgs *args, UiBool value);
+UIEXPORT void ui_label_args_set_override_defaults(UiLabelArgs *args, UiBool value);
+UIEXPORT void ui_label_args_set_margin(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_left(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_right(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_top(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_margin_bottom(UiLabelArgs *args, int value);
+UIEXPORT void ui_label_args_set_colspan(UiLabelArgs *args, int colspan);
+UIEXPORT void ui_label_args_set_rowspan(UiLabelArgs *args, int rowspan);
+UIEXPORT void ui_label_args_set_name(UiLabelArgs *args, const char *name);
+UIEXPORT void ui_label_args_set_style_class(UiLabelArgs *args, const char *classname);
+UIEXPORT void ui_label_args_set_label(UiLabelArgs *args, const char *label);
+UIEXPORT void ui_label_args_set_align(UiLabelArgs *args, UiAlignment align);
+UIEXPORT void ui_label_args_set_style(UiLabelArgs *args, UiLabelStyle style);
+UIEXPORT void ui_label_args_set_value(UiLabelArgs *args, UiString *value);
+UIEXPORT void ui_label_args_set_varname(UiLabelArgs *args, const char *varname);
+UIEXPORT void ui_label_args_free(UiLabelArgs *args);
+
+UIEXPORT UiProgressbarArgs* ui_progressbar_args_new(void);
+UIEXPORT void ui_progressbar_args_set_fill(UiProgressbarArgs *args, UiBool fill);
+UIEXPORT void ui_progressbar_args_set_hexpand(UiProgressbarArgs *args, UiBool value);
+UIEXPORT void ui_progressbar_args_set_vexpand(UiProgressbarArgs *args, UiBool value);
+UIEXPORT void ui_progressbar_args_set_hfill(UiProgressbarArgs *args, UiBool value);
+UIEXPORT void ui_progressbar_args_set_vfill(UiProgressbarArgs *args, UiBool value);
+UIEXPORT void ui_progressbar_args_set_override_defaults(UiProgressbarArgs *args, UiBool value);
+UIEXPORT void ui_progressbar_args_set_margin(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_left(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_right(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_top(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_margin_bottom(UiProgressbarArgs *args, int value);
+UIEXPORT void ui_progressbar_args_set_colspan(UiProgressbarArgs *args, int colspan);
+UIEXPORT void ui_progressbar_args_set_rowspan(UiProgressbarArgs *args, int rowspan);
+UIEXPORT void ui_progressbar_args_set_name(UiProgressbarArgs *args, const char *name);
+UIEXPORT void ui_progressbar_args_set_style_class(UiProgressbarArgs *args, const char *classname);
+UIEXPORT void ui_progressbar_args_set_min(UiProgressbarArgs *args, double min);
+UIEXPORT void ui_progressbar_args_set_max(UiProgressbarArgs *args, double max);
+UIEXPORT void ui_progressbar_args_set_value(UiProgressbarArgs *args, UiDouble *value);
+UIEXPORT void ui_progressbar_args_set_varname(UiProgressbarArgs *args, const char *varname);
+UIEXPORT void ui_progressbar_args_free(UiProgressbarArgs *args);
+
+UIEXPORT UiProgressbarSpinnerArgs* ui_progress_spinner_args_new(void);
+UIEXPORT void ui_progress_spinner_args_set_fill(UiProgressbarSpinnerArgs *args, UiBool fill);
+UIEXPORT void ui_progress_spinner_args_set_hexpand(UiProgressbarSpinnerArgs *args, UiBool value);
+UIEXPORT void ui_progress_spinner_args_set_vexpand(UiProgressbarSpinnerArgs *args, UiBool value);
+UIEXPORT void ui_progress_spinner_args_set_hfill(UiProgressbarSpinnerArgs *args, UiBool value);
+UIEXPORT void ui_progress_spinner_args_set_vfill(UiProgressbarSpinnerArgs *args, UiBool value);
+UIEXPORT void ui_progress_spinner_args_set_override_defaults(UiProgressbarSpinnerArgs *args, UiBool value);
+UIEXPORT void ui_progress_spinner_args_set_margin(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_left(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_right(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_top(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_margin_bottom(UiProgressbarSpinnerArgs *args, int value);
+UIEXPORT void ui_progress_spinner_args_set_colspan(UiProgressbarSpinnerArgs *args, int colspan);
+UIEXPORT void ui_progress_spinner_args_set_rowspan(UiProgressbarSpinnerArgs *args, int rowspan);
+UIEXPORT void ui_progress_spinner_args_set_name(UiProgressbarSpinnerArgs *args, const char *name);
+UIEXPORT void ui_progress_spinner_args_set_style_class(UiProgressbarSpinnerArgs *args, const char *classname);
+UIEXPORT void ui_progress_spinner_args_set_value(UiProgressbarSpinnerArgs *args, UiInteger *value);
+UIEXPORT void ui_progress_spinner_args_set_varname(UiProgressbarSpinnerArgs *args, const char *varname);
+UIEXPORT void ui_progress_spinner_args_free(UiProgressbarSpinnerArgs *args);
+
+UIEXPORT UiButtonArgs* ui_button_args_new(void);
+UIEXPORT void ui_button_args_set_fill(UiButtonArgs *args, UiBool fill);
+UIEXPORT void ui_button_args_set_hexpand(UiButtonArgs *args, UiBool value);
+UIEXPORT void ui_button_args_set_vexpand(UiButtonArgs *args, UiBool value);
+UIEXPORT void ui_button_args_set_hfill(UiButtonArgs *args, UiBool value);
+UIEXPORT void ui_button_args_set_vfill(UiButtonArgs *args, UiBool value);
+UIEXPORT void ui_button_args_set_override_defaults(UiButtonArgs *args, UiBool value);
+UIEXPORT void ui_button_args_set_margin(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_left(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_right(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_top(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_margin_bottom(UiButtonArgs *args, int value);
+UIEXPORT void ui_button_args_set_colspan(UiButtonArgs *args, int colspan);
+UIEXPORT void ui_button_args_set_rowspan(UiButtonArgs *args, int rowspan);
+UIEXPORT void ui_button_args_set_name(UiButtonArgs *args, const char *name);
+UIEXPORT void ui_button_args_set_style_class(UiButtonArgs *args, const char *classname);
+UIEXPORT void ui_button_args_set_label(UiButtonArgs *args, const char *label);
+UIEXPORT void ui_button_args_set_icon(UiButtonArgs *args, const char *icon);
+UIEXPORT void ui_button_args_set_tooltip(UiButtonArgs *args, const char *tooltip);
+UIEXPORT void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype);
+UIEXPORT void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback);
+UIEXPORT void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata);
+UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates);
+UIEXPORT void ui_button_args_free(UiButtonArgs *args);
+
+UIEXPORT UiToggleArgs* ui_toggle_args_new(void);
+UIEXPORT void ui_toggle_args_set_fill(UiToggleArgs *args, UiBool fill);
+UIEXPORT void ui_toggle_args_set_hexpand(UiToggleArgs *args, UiBool value);
+UIEXPORT void ui_toggle_args_set_vexpand(UiToggleArgs *args, UiBool value);
+UIEXPORT void ui_toggle_args_set_hfill(UiToggleArgs *args, UiBool value);
+UIEXPORT void ui_toggle_args_set_vfill(UiToggleArgs *args, UiBool value);
+UIEXPORT void ui_toggle_args_set_override_defaults(UiToggleArgs *args, UiBool value);
+UIEXPORT void ui_toggle_args_set_margin(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_left(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_right(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_top(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_margin_bottom(UiToggleArgs *args, int value);
+UIEXPORT void ui_toggle_args_set_colspan(UiToggleArgs *args, int colspan);
+UIEXPORT void ui_toggle_args_set_rowspan(UiToggleArgs *args, int rowspan);
+UIEXPORT void ui_toggle_args_set_name(UiToggleArgs *args, const char *name);
+UIEXPORT void ui_toggle_args_set_style_class(UiToggleArgs *args, const char *classname);
+UIEXPORT void ui_toggle_args_set_label(UiToggleArgs *args, const char *label);
+UIEXPORT void ui_toggle_args_set_icon(UiToggleArgs *args, const char *icon);
+UIEXPORT void ui_toggle_args_set_tooltip(UiToggleArgs *args, const char *tooltip);
+UIEXPORT void ui_toggle_args_set_labeltype(UiToggleArgs *args, int labeltype);
+UIEXPORT void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callback);
+UIEXPORT void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata);
+UIEXPORT void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname);
+UIEXPORT void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value);
+UIEXPORT void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group);
+UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates);
+UIEXPORT void ui_toggle_args_free(UiToggleArgs *args);
+
+UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void);
+UIEXPORT void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill);
+UIEXPORT void ui_linkbutton_args_set_hexpand(UiLinkButtonArgs *args, UiBool value);
+UIEXPORT void ui_linkbutton_args_set_vexpand(UiLinkButtonArgs *args, UiBool value);
+UIEXPORT void ui_linkbutton_args_set_hfill(UiLinkButtonArgs *args, UiBool value);
+UIEXPORT void ui_linkbutton_args_set_vfill(UiLinkButtonArgs *args, UiBool value);
+UIEXPORT void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool value);
+UIEXPORT void ui_linkbutton_args_set_margin(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_left(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_right(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_top(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_margin_bottom(UiLinkButtonArgs *args, int value);
+UIEXPORT void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan);
+UIEXPORT void ui_linkbutton_args_set_rowspan(UiLinkButtonArgs *args, int rowspan);
+UIEXPORT void ui_linkbutton_args_set_name(UiLinkButtonArgs *args, const char *name);
+UIEXPORT void ui_linkbutton_args_set_style_class(UiLinkButtonArgs *args, const char *classname);
+UIEXPORT void ui_linkbutton_args_set_varname(UiLinkButtonArgs *args, const char *varname);
+UIEXPORT void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value);
+UIEXPORT void ui_linkbutton_args_set_label(UiLinkButtonArgs *args, const char *label);
+UIEXPORT void ui_linkbutton_args_set_uri(UiLinkButtonArgs *args, const char *uri);
+UIEXPORT void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback callback);
+UIEXPORT void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata);
+UIEXPORT void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value);
+UIEXPORT void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type);
+UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates);
+UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args);
+
+UIEXPORT UiListArgs* ui_list_args_new(void);
+UIEXPORT void ui_list_args_set_fill(UiListArgs *args, UiBool fill);
+UIEXPORT void ui_list_args_set_hexpand(UiListArgs *args, UiBool value);
+UIEXPORT void ui_list_args_set_vexpand(UiListArgs *args, UiBool value);
+UIEXPORT void ui_list_args_set_hfill(UiListArgs *args, UiBool value);
+UIEXPORT void ui_list_args_set_vfill(UiListArgs *args, UiBool value);
+UIEXPORT void ui_list_args_set_override_defaults(UiListArgs *args, UiBool value);
+UIEXPORT void ui_list_args_set_margin(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_left(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_right(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_top(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_margin_bottom(UiListArgs *args, int value);
+UIEXPORT void ui_list_args_set_colspan(UiListArgs *args, int colspan);
+UIEXPORT void ui_list_args_set_rowspan(UiListArgs *args, int rowspan);
+UIEXPORT void ui_list_args_set_name(UiListArgs *args, const char *name);
+UIEXPORT void ui_list_args_set_style_class(UiListArgs *args, const char *classname);
+UIEXPORT void ui_list_args_set_varname(UiListArgs *args, const char *varname);
+UIEXPORT void ui_list_args_set_value(UiListArgs *args, UiList *value);
+UIEXPORT void ui_list_args_set_model(UiListArgs *args, UiModel *model);
+UIEXPORT void ui_list_args_set_static_elements(UiListArgs *args, char **strarray, size_t nelm);
+UIEXPORT void ui_list_args_set_getvalue_func(UiListArgs *args, ui_getvaluefunc getvalue);
+UIEXPORT void ui_list_args_set_getvalue_func2(UiListArgs *args, ui_getvaluefunc2 getvalue);
+UIEXPORT void ui_list_args_set_getvalue_data(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_getstyle_func(UiListArgs *args, ui_getstylefunc getstyle);
+UIEXPORT void ui_list_args_set_getstyle_data(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_onactivate(UiListArgs *args, ui_callback callback);
+UIEXPORT void ui_list_args_set_onactivatedata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_onselection(UiListArgs *args, ui_callback callback);
+UIEXPORT void ui_list_args_set_onselectiondata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_ondragstart(UiListArgs *args, ui_callback callback);
+UIEXPORT void ui_list_args_set_ondragstartdata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_ondragcomplete(UiListArgs *args, ui_callback callback);
+UIEXPORT void ui_list_args_set_ondragcompletedata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_ondrop(UiListArgs *args, ui_callback callback);
+UIEXPORT void ui_list_args_set_ondropdata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave);
+UIEXPORT void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection);
+UIEXPORT void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder);
+UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates);
+UIEXPORT void ui_list_args_free(UiListArgs *args);
+
+UIEXPORT UiSourceListArgs* ui_sourcelist_args_new(void);
+UIEXPORT void ui_sourcelist_args_set_fill(UiSourceListArgs *args, UiBool fill);
+UIEXPORT void ui_sourcelist_args_set_hexpand(UiSourceListArgs *args, UiBool value);
+UIEXPORT void ui_sourcelist_args_set_vexpand(UiSourceListArgs *args, UiBool value);
+UIEXPORT void ui_sourcelist_args_set_hfill(UiSourceListArgs *args, UiBool value);
+UIEXPORT void ui_sourcelist_args_set_vfill(UiSourceListArgs *args, UiBool value);
+UIEXPORT void ui_sourcelist_args_set_override_defaults(UiSourceListArgs *args, UiBool value);
+UIEXPORT void ui_sourcelist_args_set_margin(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_left(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_right(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_top(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_margin_bottom(UiSourceListArgs *args, int value);
+UIEXPORT void ui_sourcelist_args_set_colspan(UiSourceListArgs *args, int colspan);
+UIEXPORT void ui_sourcelist_args_set_rowspan(UiSourceListArgs *args, int rowspan);
+UIEXPORT void ui_sourcelist_args_set_name(UiSourceListArgs *args, const char *name);
+UIEXPORT void ui_sourcelist_args_set_style_class(UiSourceListArgs *args, const char *classname);
+UIEXPORT void ui_sourcelist_args_set_static_sublists(UiSourceListArgs *args, UiSubList *sublists, size_t numsublists);
+UIEXPORT void ui_sourcelist_args_set_varname(UiSourceListArgs *args, const char *varname);
+UIEXPORT void ui_sourcelist_args_set_dynamic_sublists(UiSourceListArgs *args, UiList *value);
+UIEXPORT void ui_sourcelist_args_set_getvalue_func(UiSourceListArgs *args, ui_sublist_getvalue_func getvalue);
+UIEXPORT void ui_sourcelist_args_set_getvalue_userdata(UiSourceListArgs *args, void *userdata);
+UIEXPORT void ui_sourcelist_args_set_onactivate(UiSourceListArgs *args, ui_callback callback);
+UIEXPORT void ui_sourcelist_args_set_onactivatedata(UiSourceListArgs *args, void *userdata);
+UIEXPORT void ui_sourcelist_args_set_onbuttonclick(UiSourceListArgs *args, ui_callback callback);
+UIEXPORT void ui_sourcelist_args_set_onbuttonclickdata(UiSourceListArgs *args, void *userdata);
+UIEXPORT void ui_sourcelist_args_set_contextmenu(UiSourceListArgs *args, UiMenuBuilder *menubuilder);
+UIEXPORT void ui_sourcelist_args_set_header_is_item(UiSourceListArgs *args, UiBool value);
+UIEXPORT void ui_sourcelist_args_free(UiSourceListArgs *args);
+
+UIEXPORT UiTextAreaArgs* ui_textarea_args_new(void);
+UIEXPORT void ui_textarea_args_set_fill(UiTextAreaArgs *args, UiBool fill);
+UIEXPORT void ui_textarea_args_set_hexpand(UiTextAreaArgs *args, UiBool value);
+UIEXPORT void ui_textarea_args_set_vexpand(UiTextAreaArgs *args, UiBool value);
+UIEXPORT void ui_textarea_args_set_hfill(UiTextAreaArgs *args, UiBool value);
+UIEXPORT void ui_textarea_args_set_vfill(UiTextAreaArgs *args, UiBool value);
+UIEXPORT void ui_textarea_args_set_override_defaults(UiTextAreaArgs *args, UiBool value);
+UIEXPORT void ui_textarea_args_set_margin(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_left(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_right(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_top(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_margin_bottom(UiTextAreaArgs *args, int value);
+UIEXPORT void ui_textarea_args_set_colspan(UiTextAreaArgs *args, int colspan);
+UIEXPORT void ui_textarea_args_set_rowspan(UiTextAreaArgs *args, int rowspan);
+UIEXPORT void ui_textarea_args_set_name(UiTextAreaArgs *args, const char *name);
+UIEXPORT void ui_textarea_args_set_style_class(UiTextAreaArgs *args, const char *classname);
+UIEXPORT void ui_textarea_args_set_onchange(UiTextAreaArgs *args, ui_callback callback);
+UIEXPORT void ui_textarea_args_set_onchangedata(UiTextAreaArgs *args, void *onchangedata);
+UIEXPORT void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname);
+UIEXPORT void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value);
+UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates);
+UIEXPORT void ui_textarea_args_free(UiTextAreaArgs *args);
+
+UIEXPORT UiTextFieldArgs* ui_textfield_args_new(void);
+UIEXPORT void ui_textfield_args_set_fill(UiTextFieldArgs *args, UiBool fill);
+UIEXPORT void ui_textfield_args_set_hexpand(UiTextFieldArgs *args, UiBool value);
+UIEXPORT void ui_textfield_args_set_vexpand(UiTextFieldArgs *args, UiBool value);
+UIEXPORT void ui_textfield_args_set_hfill(UiTextFieldArgs *args, UiBool value);
+UIEXPORT void ui_textfield_args_set_vfill(UiTextFieldArgs *args, UiBool value);
+UIEXPORT void ui_textfield_args_set_override_defaults(UiTextFieldArgs *args, UiBool value);
+UIEXPORT void ui_textfield_args_set_margin(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_left(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_right(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_top(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_margin_bottom(UiTextFieldArgs *args, int value);
+UIEXPORT void ui_textfield_args_set_colspan(UiTextFieldArgs *args, int colspan);
+UIEXPORT void ui_textfield_args_set_rowspan(UiTextFieldArgs *args, int rowspan);
+UIEXPORT void ui_textfield_args_set_name(UiTextFieldArgs *args, const char *name);
+UIEXPORT void ui_textfield_args_set_style_class(UiTextFieldArgs *args, const char *classname);
+UIEXPORT void ui_textfield_args_set_onchange(UiTextFieldArgs *args, ui_callback callback);
+UIEXPORT void ui_textfield_args_set_onchangedata(UiTextFieldArgs *args, void *onchangedata);
+UIEXPORT void ui_textfield_args_set_onactivate(UiTextFieldArgs *args, ui_callback callback);
+UIEXPORT void ui_textfield_args_set_onactivatedata(UiTextFieldArgs *args, void *onactivatedata);
+UIEXPORT void ui_textfield_args_set_varname(UiTextFieldArgs *args, const char *varname);
+UIEXPORT void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value);
+UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates);
+UIEXPORT void ui_textfield_args_free(UiTextFieldArgs *args);
+
+UIEXPORT UiSpinBoxArgs* ui_spinbox_args_new(void);
+UIEXPORT void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill);
+UIEXPORT void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_margin(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_left(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_right(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_top(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_margin_bottom(UiSpinBoxArgs *args, int value);
+UIEXPORT void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan);
+UIEXPORT void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan);
+UIEXPORT void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name);
+UIEXPORT void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname);
+UIEXPORT void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback);
+UIEXPORT void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata);
+UIEXPORT void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min);
+UIEXPORT void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max);
+UIEXPORT void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step);
+UIEXPORT void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits);
+UIEXPORT void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname);
+UIEXPORT void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value);
+UIEXPORT void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value);
+UIEXPORT void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value);
+UIEXPORT void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates);
+UIEXPORT void ui_spinbox_args_free(UiSpinBoxArgs *args);
+
+UIEXPORT UiWebviewArgs* ui_webview_args_new(void);
+UIEXPORT void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill);
+UIEXPORT void ui_webview_args_set_hexpand(UiWebviewArgs *args, UiBool value);
+UIEXPORT void ui_webview_args_set_vexpand(UiWebviewArgs *args, UiBool value);
+UIEXPORT void ui_webview_args_set_hfill(UiWebviewArgs *args, UiBool value);
+UIEXPORT void ui_webview_args_set_vfill(UiWebviewArgs *args, UiBool value);
+UIEXPORT void ui_webview_args_set_override_defaults(UiWebviewArgs *args, UiBool value);
+UIEXPORT void ui_webview_args_set_margin(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_left(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_right(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_top(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_margin_bottom(UiWebviewArgs *args, int value);
+UIEXPORT void ui_webview_args_set_colspan(UiWebviewArgs *args, int colspan);
+UIEXPORT void ui_webview_args_set_rowspan(UiWebviewArgs *args, int rowspan);
+UIEXPORT void ui_webview_args_set_name(UiWebviewArgs *args, const char *name);
+UIEXPORT void ui_webview_args_set_style_class(UiWebviewArgs *args, const char *classname);
+UIEXPORT void ui_webview_args_set_varname(UiWebviewArgs *args, const char *varname);
+UIEXPORT void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value);
+UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates);
+UIEXPORT void ui_webview_args_free(UiWebviewArgs *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_ARGS_H */
+
--- /dev/null
+/*
+ * 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 _WIN32
+
+#include "condvar.h"
+
+#include <stdlib.h>
+
+UiCondVar* ui_condvar_create(void) {
+ UiPosixCondVar *var = malloc(sizeof(UiPosixCondVar));
+ var->var.data = NULL;
+ var->var.intdata = 0;
+ var->set = 0;
+ pthread_mutex_init(&var->lock, NULL);
+ pthread_cond_init(&var->cond, NULL);
+ return (UiCondVar*)var;
+}
+
+void ui_condvar_wait(UiCondVar *var) {
+ UiPosixCondVar *p = (UiPosixCondVar*)var;
+ pthread_mutex_lock(&p->lock);
+ if(!p->set) {
+ pthread_cond_wait(&p->cond, &p->lock);
+ }
+ p->set = 0;
+ pthread_mutex_unlock(&p->lock);
+
+}
+
+void ui_condvar_signal(UiCondVar *var, void *data, int intdata) {
+ UiPosixCondVar *p = (UiPosixCondVar*)var;
+ pthread_mutex_lock(&p->lock);
+ p->var.data = data;
+ p->var.intdata = intdata;
+ p->set = 1;
+ pthread_cond_signal(&p->cond);
+ pthread_mutex_unlock(&p->lock);
+}
+
+void ui_condvar_destroy(UiCondVar *var) {
+ UiPosixCondVar *p = (UiPosixCondVar*)var;
+ pthread_mutex_destroy(&p->lock);
+ pthread_cond_destroy(&p->cond);
+ free(p);
+
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+/*
+ * 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_CONDVAR_H
+#define UIC_CONDVAR_H
+
+#include "../ui/toolkit.h"
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiPosixCondVar {
+ UiCondVar var;
+ int set;
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+} UiPosixCondVar;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_CONDVAR_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "container.h"
+#include "object.h"
+
+void ui_end_new(UiObject *obj) {
+ if(!obj->container_end) {
+ return;
+ }
+ UiContainerX *rm = obj->container_end;
+ uic_object_pop_container(obj);
+ ui_free(obj->ctx, rm);
+}
+
+void ui_newline(UiObject *obj) {
+ UiContainerX *container = obj->container_end;
+ if(container) {
+ container->newline = TRUE;
+ }
+}
+
+void uic_layout_setup_expand_fill(
+ UiLayout *layout,
+ UiBool def_hexpand,
+ UiBool def_vexpand,
+ UiBool def_hfill,
+ UiBool def_vfill)
+{
+ if(layout->fill) {
+ layout->hfill = TRUE;
+ layout->vfill = TRUE;
+ layout->hexpand = TRUE;
+ layout->vexpand = TRUE;
+ return;
+ }
+
+ if(!layout->override_defaults) {
+ if(def_hexpand) {
+ layout->hexpand = TRUE;
+ }
+ if(def_hfill) {
+ layout->hfill = TRUE;
+ }
+ if(def_vexpand) {
+ layout->vexpand = TRUE;
+ }
+ if(def_vfill) {
+ layout->vfill = TRUE;
+ }
+ }
+}
+
+void uic_layout_setup_margin(UiLayout *layout) {
+ int margin = layout->margin;
+ if(margin > 0) {
+ layout->margin_left = margin;
+ layout->margin_right = margin;
+ layout->margin_top = margin;
+ layout->margin_bottom = margin;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_CONTAINER_H
+#define UIC_CONTAINER_H
+
+#include "../ui/container.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * prepares the layout horizontal and vertical fill/expand settings
+ * based on fill and defaults
+ */
+void uic_layout_setup_expand_fill(
+ UiLayout *layout,
+ UiBool def_hexpand,
+ UiBool def_vexpand,
+ UiBool def_hfill,
+ UiBool def_vfill);
+
+/*
+ * adjusts margin_* if margin > 0
+ */
+void uic_layout_setup_margin(UiLayout *layout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_CONTAINER_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+#include <cx/array_list.h>
+#include <cx/compare.h>
+#include <cx/mempool.h>
+
+#include "context.h"
+#include "../ui/window.h"
+#include "../ui/widget.h"
+#include "document.h"
+#include "types.h"
+
+
+static UiContext* global_context;
+
+void uic_init_global_context(void) {
+ CxMempool *mp = cxMempoolCreateSimple(32);
+ global_context = uic_context(NULL, mp);
+}
+
+UiContext* ui_global_context(void) {
+ return global_context;
+}
+
+UiContext* uic_context(UiObject *toplevel, CxMempool *mp) {
+ UiContext *ctx = cxMalloc(mp->allocator, sizeof(UiContext));
+ memset(ctx, 0, sizeof(UiContext));
+ ctx->mp = mp;
+ ctx->allocator = mp->allocator;
+ ctx->destroy_handler = cxArrayListCreate(ctx->allocator, NULL, sizeof(UiDestroyHandler), 16);
+ ctx->obj = toplevel;
+ ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16);
+
+ ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, CX_STORE_POINTERS);
+ ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget));
+ ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32);
+
+ ctx->attach_document = uic_context_attach_document;
+ ctx->detach_document2 = uic_context_detach_document;
+
+#if UI_GTK2 || UI_GTK3
+ if(toplevel && toplevel->widget) {
+ ctx->accel_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(toplevel->widget), ctx->accel_group);
+ }
+#endif
+
+ return ctx;
+}
+
+UiContext* uic_root_context(UiContext *ctx) {
+ return ctx->parent ? uic_root_context(ctx->parent) : ctx;
+}
+
+void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *data) {
+ UiDestroyHandler handler;
+ handler.destructor = func;
+ handler.data = data;
+ cxListAdd(ctx->destroy_handler, &handler);
+}
+
+void uic_context_prepare_close(UiContext *ctx) {
+ cxListClear(ctx->groups);
+ cxListClear(ctx->group_widgets);
+}
+
+void uic_context_attach_document(UiContext *ctx, void *document) {
+ cxListAdd(ctx->documents, document);
+ ctx->document = document;
+
+ UiContext *doc_ctx = ui_document_context(document);
+ doc_ctx->parent = ctx;
+
+ // if a document variable has the same name as a parent variable,
+ // move the bindings to the document
+ UiContext *var_ctx = ctx;
+ while(var_ctx) {
+ CxMapIterator i = cxMapIterator(var_ctx->vars);
+ cx_foreach(CxMapEntry*, entry, i) {
+ printf("attach %.*s\n", (int)entry->key->len, (char*)entry->key->data);
+ UiVar *var = entry->value;
+ UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key);
+ if(docvar) {
+ // bind var to document var
+ uic_copy_binding(var, docvar, TRUE);
+ cxIteratorFlagRemoval(i);
+ }
+ }
+
+ var_ctx = var_ctx->parent;
+ }
+}
+
+static void uic_context_unbind_vars(UiContext *ctx) {
+ CxMapIterator mi = cxMapIterator(ctx->vars);
+ cx_foreach(CxMapEntry*, entry, mi) {
+ UiVar *var = entry->value;
+ // var->from && var->from_ctx && var->from_ctx != ctx
+ uic_save_var(var);
+ if(var->from) {
+ uic_copy_binding(var, var->from, FALSE);
+ cxMapPut(var->from->from_ctx->vars, *entry->key, var->from);
+ var->from = NULL;
+ }
+ }
+
+ if(ctx->documents) {
+ CxIterator i = cxListIterator(ctx->documents);
+ cx_foreach(void *, doc, i) {
+ UiContext *subctx = ui_document_context(doc);
+ uic_context_unbind_vars(subctx);
+ }
+ }
+}
+
+void uic_context_detach_document(UiContext *ctx, void *document) {
+ // find the document in the documents list
+ size_t docIndex = cxListFind(ctx->documents, document);
+ if(!cxListIndexValid(ctx->documents, docIndex)) {
+ return;
+ }
+
+ cxListRemove(ctx->documents, docIndex);
+ ctx->document = cxListAt(ctx->documents, 0);
+
+ UiContext *docctx = ui_document_context(document);
+ uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent
+ docctx->parent = NULL;
+}
+
+void uic_context_detach_all(UiContext *ctx) {
+ // copy list
+ CxList *ls = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ CxIterator i = cxListIterator(ctx->documents);
+ cx_foreach(void *, doc, i) {
+ cxListAdd(ls, doc);
+ }
+
+ // detach documents
+ i = cxListIterator(ls);
+ cx_foreach(void *, doc, i) {
+ ctx->detach_document2(ctx, doc);
+ }
+
+ cxListFree(ls);
+}
+
+static UiVar* ctx_getvar(UiContext *ctx, CxHashKey key) {
+ UiVar *var = cxMapGet(ctx->vars, key);
+ if(!var && ctx->documents) {
+ CxIterator i = cxListIterator(ctx->documents);
+ cx_foreach(void *, doc, i) {
+ UiContext *subctx = ui_document_context(doc);
+ var = ctx_getvar(subctx, key);
+ if(var) {
+ break;
+ }
+ }
+ }
+ return var;
+}
+
+UiVar* uic_get_var(UiContext *ctx, const char *name) {
+ CxHashKey key = cx_hash_key(name, strlen(name));
+ return ctx_getvar(ctx, key);
+}
+
+UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
+ UiVar *var = uic_get_var(ctx, name);
+ if(var) {
+ if(var->type == type) {
+ return var;
+ } else {
+ fprintf(stderr, "UiError: var '%s' already bound with different type\n", name);
+ }
+ }
+
+ var = ui_malloc(ctx, sizeof(UiVar));
+ var->type = type;
+ var->value = uic_create_value(ctx, type);
+ var->original_value = NULL;
+ var->from = NULL;
+ var->from_ctx = ctx;
+
+ cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var);
+
+ cxMapPut(ctx->vars, name, var);
+
+ return var;
+}
+
+UiVar* uic_create_value_var(UiContext* ctx, void* value) {
+ UiVar *var = (UiVar*)ui_malloc(ctx, sizeof(UiVar));
+ var->from = NULL;
+ var->from_ctx = ctx;
+ var->value = value;
+ var->original_value = NULL;
+ var->type = UI_VAR_SPECIAL;
+ return var;
+}
+
+void* uic_create_value(UiContext *ctx, UiVarType type) {
+ void *val = NULL;
+ switch(type) {
+ case UI_VAR_SPECIAL: break;
+ case UI_VAR_INTEGER: {
+ val = ui_int_new(ctx, NULL);
+ break;
+ }
+ case UI_VAR_DOUBLE: {
+ val = ui_double_new(ctx, NULL);
+ break;
+ }
+ case UI_VAR_STRING: {
+ val = ui_string_new(ctx, NULL);
+ break;
+ }
+ case UI_VAR_TEXT: {
+ val = ui_text_new(ctx, NULL);
+ break;
+ }
+ case UI_VAR_LIST: {
+ val = ui_list_new(ctx, NULL);
+ break;
+ }
+ case UI_VAR_RANGE: {
+ val = ui_range_new(ctx, NULL);
+ break;
+ }
+ case UI_VAR_GENERIC: {
+ val = ui_generic_new(ctx, NULL);
+ }
+ }
+ return val;
+}
+
+
+UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type) {
+ if (value) {
+ return uic_create_value_var(current, value);
+ }
+ if (varname) {
+ return uic_create_var(toplevel, varname, type);
+ }
+ return NULL;
+}
+
+
+void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
+ // check type
+ if(from->type != to->type) {
+ fprintf(stderr, "UI Error: var has incompatible type.\n");
+ return;
+ }
+
+ void *fromvalue = from->value;
+ void *tovalue = to->value;
+ // update var
+ if(copytodoc) {
+ to->from = from; // from which UiVar are the bindings copied
+ from->original_value = fromvalue; // save original value otherwise it would be lost
+ // widgets store a reference to the UiVar with their value
+ // the UiVar object must be updated to contain the current value object
+ from->value = tovalue;
+ } else {
+ if(to->original_value) {
+ to->value = to->original_value;
+ tovalue = to->value;
+ }
+ }
+
+ ui_setop_enable(TRUE);
+
+ // copy binding
+ // we don't copy the observer, because the from var has never one
+ switch(from->type) {
+ default: fprintf(stderr, "uic_copy_binding: wtf!\n"); break;
+ case UI_VAR_SPECIAL: break;
+ case UI_VAR_INTEGER: {
+ UiInteger *f = fromvalue;
+ UiInteger *t = tovalue;
+ if(!f->obj) break;
+ uic_int_copy(f, t);
+ t->set(t, t->value);
+ break;
+ }
+ case UI_VAR_DOUBLE: {
+ UiDouble *f = fromvalue;
+ UiDouble *t = tovalue;
+ if(!f->obj) break;
+ uic_double_copy(f, t);
+ t->set(t, t->value);
+ break;
+ }
+ case UI_VAR_STRING: {
+ UiString *f = fromvalue;
+ UiString *t = tovalue;
+ if(!f->obj) break;
+ uic_string_copy(f, t);
+ char *tvalue = t->value.ptr ? t->value.ptr : "";
+ char *fvalue = f->value.ptr ? f->value.ptr : "";
+ t->set(t, tvalue);
+ break;
+ }
+ case UI_VAR_TEXT: {
+ UiText *f = fromvalue;
+ UiText *t = tovalue;
+ if(!f->obj) break;
+ uic_text_copy(f, t);
+ t->restore(t);
+ break;
+ }
+ case UI_VAR_LIST: {
+ UiList *f = fromvalue;
+ UiList *t = tovalue;
+ uic_list_copy(f, t);
+ ui_list_update(t);
+ break;
+ }
+ case UI_VAR_RANGE: {
+ UiRange *f = fromvalue;
+ UiRange *t = tovalue;
+ if(!f->obj) break;
+ uic_range_copy(f, t);
+ t->setextent(t, t->extent);
+ t->setrange(t, t->min, t->max);
+ t->set(t, t->value);
+ break;
+ }
+ case UI_VAR_GENERIC: {
+ UiGeneric *f = fromvalue;
+ UiGeneric *t = tovalue;
+ if(!f->obj) break;
+ uic_generic_copy(f, t);
+ t->set(t, t->value, t->type);
+ break;
+ }
+ }
+
+ ui_setop_enable(FALSE);
+}
+
+void uic_save_var(UiVar *var) {
+ switch(var->type) {
+ case UI_VAR_SPECIAL: break;
+ case UI_VAR_INTEGER: uic_int_save(var->value); break;
+ case UI_VAR_DOUBLE: uic_double_save(var->value); break;
+ case UI_VAR_STRING: uic_string_save(var->value); break;
+ case UI_VAR_TEXT: uic_text_save(var->value); break;
+ case UI_VAR_LIST: break;
+ case UI_VAR_RANGE: uic_range_save(var->value); break;
+ case UI_VAR_GENERIC: uic_generic_save(var->value); break;
+ }
+}
+
+void uic_unbind_var(UiVar *var) {
+ switch(var->type) {
+ case UI_VAR_SPECIAL: break;
+ case UI_VAR_INTEGER: uic_int_unbind(var->value); break;
+ case UI_VAR_DOUBLE: uic_double_unbind(var->value); break;
+ case UI_VAR_STRING: uic_string_unbind(var->value); break;
+ case UI_VAR_TEXT: uic_text_unbind(var->value); break;
+ case UI_VAR_LIST: uic_list_unbind(var->value); break;
+ case UI_VAR_RANGE: uic_range_unbind(var->value); break;
+ case UI_VAR_GENERIC: uic_generic_unbind(var->value); break;
+ }
+}
+
+void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value) {
+ // TODO: do we need/want this? Why adding vars to a context after
+ // widgets reference these? Workarounds:
+ // 1. add vars to ctx before creating ui
+ // 2. create ui, create new document with vars, attach doc
+ // also it would be possible to create a function, that scans unbound vars
+ // and connects them to available vars
+ /*
+ UiContext *rootctx = uic_root_context(ctx);
+ UiVar *b = NULL;
+ if(rootctx->bound) {
+ // some widgets are already bound to some vars
+ b = ucx_map_cstr_get(rootctx->bound, name);
+ if(b) {
+ // a widget is bound to a var with this name
+ // if ctx is the root context we can remove the var from bound
+ // because set_doc or detach can't fuck things up
+ if(ctx == rootctx) {
+ ucx_map_cstr_remove(ctx->bound, name);
+ // TODO: free stuff
+ }
+ }
+ }
+ */
+
+ // create new var and add it to doc's vars
+ UiVar *var = ui_malloc(ctx, sizeof(UiVar));
+ var->type = type;
+ var->value = value;
+ var->from = NULL;
+ var->from_ctx = ctx;
+ size_t oldcount = cxMapSize(ctx->vars);
+ cxMapPut(ctx->vars, name, var);
+ if(cxMapSize(ctx->vars) != oldcount + 1) {
+ fprintf(stderr, "UiError: var '%s' already exists\n", name);
+ }
+
+ // TODO: remove?
+ // a widget is already bound to a var with this name
+ // copy the binding (like uic_context_set_document)
+ /*
+ if(b) {
+ uic_copy_binding(b, var, TRUE);
+ }
+ */
+}
+
+void uic_remove_bound_var(UiContext *ctx, UiVar *var) {
+ // TODO
+}
+
+
+// public API
+
+void ui_attach_document(UiContext *ctx, void *document) {
+ uic_context_attach_document(ctx, document);
+}
+
+void ui_detach_document(UiContext *ctx, void *document) {
+ uic_context_detach_document(ctx, document);
+}
+
+void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) {
+ ctx->close_callback = fnc;
+ ctx->close_data = udata;
+}
+
+void ui_context_destroy(UiContext *ctx) {
+ CxIterator i = cxListIterator(ctx->destroy_handler);
+ cx_foreach(UiDestroyHandler *, h, i) {
+ h->destructor(h->data);
+ }
+ cxMempoolFree(ctx->mp);
+}
+
+UiContext* ui_context_parent(UiContext *ctx) {
+ return ctx->parent;
+}
+
+
+void ui_set_group(UiContext *ctx, int group) {
+ if(!cxListIndexValid(ctx->groups, cxListFind(ctx->groups, &group))) {
+ cxListAdd(ctx->groups, &group);
+ }
+
+ // enable/disable group widgets
+ uic_check_group_widgets(ctx);
+}
+
+void ui_unset_group(UiContext *ctx, int group) {
+ int i = cxListFind(ctx->groups, &group);
+ if(i != -1) {
+ cxListRemove(ctx->groups, i);
+ }
+
+ // enable/disable group widgets
+ uic_check_group_widgets(ctx);
+}
+
+int* ui_active_groups(UiContext *ctx, int *ngroups) {
+ *ngroups = cxListSize(ctx->groups);
+ return cxListAt(ctx->groups, 0);
+}
+
+void uic_check_group_widgets(UiContext *ctx) {
+ int ngroups = 0;
+ int *groups = ui_active_groups(ctx, &ngroups);
+
+ CxIterator i = cxListIterator(ctx->group_widgets);
+ cx_foreach(UiGroupWidget *, gw, i) {
+ char *check = calloc(1, gw->numgroups);
+
+ for(int i=0;i<ngroups;i++) {
+ for(int k=0;k<gw->numgroups;k++) {
+ if(groups[i] == gw->groups[k]) {
+ check[k] = 1;
+ }
+ }
+ }
+
+ int enable = 1;
+ for(int i=0;i<gw->numgroups;i++) {
+ if(check[i] == 0) {
+ enable = 0;
+ break;
+ }
+ }
+ free(check);
+ gw->enable(gw->widget, enable);
+ }
+}
+
+void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
+ if(enable == NULL) {
+ enable = (ui_enablefunc)ui_set_enabled;
+ }
+ // get groups
+ CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
+ va_list ap;
+ va_start(ap, enable);
+ int group;
+ while((group = va_arg(ap, int)) != -1) {
+ cxListAdd(groups, &group);
+ }
+ va_end(ap);
+
+ uic_add_group_widget(ctx, widget, enable, groups);
+
+ cxListFree(groups);
+}
+
+void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups) {
+ if(enable == NULL) {
+ enable = (ui_enablefunc)ui_set_enabled;
+ }
+ CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups);
+ for(int i=0;i<ngroups;i++) {
+ cxListAdd(ls, groups+i);
+ }
+ uic_add_group_widget(ctx, widget, enable, ls);
+ cxListFree(ls);
+}
+
+void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates) {
+ ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
+}
+
+size_t uic_group_array_size(const int *groups) {
+ int i;
+ for(i=0;groups[i] >= 0;i++) { }
+ return i;
+}
+
+void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups) {
+ uic_add_group_widget_i(ctx, widget, enable, cxListAt(groups, 0), cxListSize(groups));
+}
+
+void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups) {
+ const CxAllocator *a = ctx->allocator;
+ UiGroupWidget gw;
+
+ gw.widget = widget;
+ gw.enable = enable;
+ gw.numgroups = numgroups;
+ gw.groups = cxCalloc(a, numgroups, sizeof(int));
+
+ // copy groups
+ if(groups) {
+ memcpy(gw.groups, groups, gw.numgroups * sizeof(int));
+ }
+
+ cxListAdd(ctx->group_widgets, &gw);
+}
+
+void uic_remove_group_widget(UiContext *ctx, void *widget) {
+ (void)cxListFindRemove(ctx->group_widgets, widget);
+}
+
+UIEXPORT void *ui_allocator(UiContext *ctx) {
+ return (void*)ctx->allocator;
+}
+
+void* ui_cx_mempool(UiContext *ctx) {
+ return ctx->mp;
+}
+
+void* ui_malloc(UiContext *ctx, size_t size) {
+ return ctx ? cxMalloc(ctx->allocator, size) : NULL;
+}
+
+void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize) {
+ return ctx ? cxCalloc(ctx->allocator, nelem, elsize) : NULL;
+}
+
+void ui_free(UiContext *ctx, void *ptr) {
+ if(ctx && ptr) {
+ cxFree(ctx->allocator, ptr);
+ }
+}
+
+void* ui_realloc(UiContext *ctx, void *ptr, size_t size) {
+ return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL;
+}
+
+char* ui_strdup(UiContext *ctx, const char *str) {
+ if(!ctx) {
+ return NULL;
+ }
+ cxstring s = cx_str(str);
+ cxmutstr d = cx_strdup_a(ctx->allocator, s);
+ return d.ptr;
+}
+
+void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr) {
+ cxMempoolRegister(ctx->mp, data, (cx_destructor_func)destr);
+}
+
+void ui_set_destructor(void *mem, ui_destructor_func destr) {
+ cxMempoolSetDestructor(mem, (cx_destructor_func)destr);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_CONTEXT_H
+#define UIC_CONTEXT_H
+
+#include "../ui/toolkit.h"
+#include <cx/map.h>
+#include <cx/hash_map.h>
+#include <cx/mempool.h>
+#include <cx/list.h>
+#include <cx/linked_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiVar UiVar;
+typedef struct UiListPtr UiListPtr;
+typedef struct UiListVar UiListVar;
+typedef struct UiGroupWidget UiGroupWidget;
+typedef struct UiDestroyHandler UiDestroyHandler;
+
+typedef enum UiVarType {
+ UI_VAR_SPECIAL = 0,
+ UI_VAR_INTEGER,
+ UI_VAR_DOUBLE,
+ UI_VAR_STRING,
+ UI_VAR_TEXT,
+ UI_VAR_LIST,
+ UI_VAR_RANGE,
+ UI_VAR_GENERIC
+} UiVarType;
+
+struct UiContext {
+ UiContext *parent;
+ UiObject *obj;
+ CxMempool *mp;
+ const CxAllocator *allocator;
+ CxList *destroy_handler;
+
+ void *document;
+ CxList *documents;
+
+ CxMap *vars;
+
+ CxList *groups; // int list
+ CxList *group_widgets; // UiGroupWidget list
+
+ void (*attach_document)(UiContext *ctx, void *document);
+ void (*detach_document2)(UiContext *ctx, void *document);
+
+ char *title;
+
+#ifdef UI_GTK
+#if GTK_CHECK_VERSION(4, 0, 0)
+ GActionMap *action_map;
+#elif UI_GTK2 || UI_GTK3
+ GtkAccelGroup *accel_group;
+#endif
+#endif
+
+
+
+ ui_callback close_callback;
+ void *close_data;
+};
+
+struct UiVar {
+ void *value;
+ void *original_value;
+ UiVarType type;
+ UiVar *from;
+ UiContext *from_ctx;
+};
+
+struct UiGroupWidget {
+ void *widget;
+ ui_enablefunc enable;
+ int *groups;
+ int numgroups;
+};
+
+struct UiDestroyHandler {
+ cx_destructor_func destructor;
+ void *data;
+};
+
+
+void uic_init_global_context(void);
+
+UiContext* uic_context(UiObject *toplevel, CxMempool *mp);
+UiContext* uic_root_context(UiContext *ctx);
+void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *data);
+
+void uic_context_prepare_close(UiContext *ctx);
+
+void uic_context_attach_document(UiContext *ctx, void *document);
+void uic_context_detach_document(UiContext *ctx, void *document);
+void uic_context_attach_context(UiContext *ctx, UiContext *doc_ctx); // TODO
+void uic_context_detach_context(UiContext *ctx, UiContext *doc_ctx); // TODO
+void uic_context_detach_all(UiContext *ctx);
+
+UiVar* uic_get_var(UiContext *ctx, const char *name);
+UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type);
+UiVar* uic_create_value_var(UiContext *ctx, void *value);
+void* uic_create_value(UiContext *ctx, UiVarType type);
+
+UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type);
+
+void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc);
+void uic_save_var(UiVar *var);
+void uic_unbind_var(UiVar *var);
+
+void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value);
+
+void uic_remove_bound_var(UiContext *ctx, UiVar *var);
+
+size_t uic_group_array_size(const int *groups);
+void uic_check_group_widgets(UiContext *ctx);
+void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups);
+void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups);
+void uic_remove_group_widget(UiContext *ctx, void *widget);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_CONTEXT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "document.h"
+
+#include <cx/hash_map.h>
+#include <cx/mempool.h>
+
+
+
+
+void* ui_document_new(size_t size) {
+ CxMempool *mp = cxMempoolCreateSimple(256);
+ const CxAllocator *a = mp->allocator;
+ UiContext *ctx = uic_context(NULL, mp);
+
+ UiDoc *document = cxCalloc(a, sizeof(UiDoc) + size, 1);
+ document->ctx = ctx;
+ return &document->doc;
+}
+
+void ui_document_destroy(void *doc) {
+ UiContext *ctx = ui_document_context(doc);
+ if(ctx) {
+ UiEvent ev;
+ ev.window = NULL;
+ ev.document = doc;
+ ev.obj = NULL;
+ ev.eventdata = NULL;
+ ev.eventdatatype = 0;
+ ev.intval = 0;
+
+ if(ctx->close_callback) {
+ ctx->close_callback(&ev, ctx->close_data);
+ }
+ cxMempoolFree(ctx->mp);
+ }
+}
+
+UiContext* ui_document_context(void *doc) {
+ if(doc) {
+ char *docPtr = doc;
+ UiDoc *document = (UiDoc*)(docPtr - sizeof(void*));
+ return document->ctx;
+ } else {
+ return NULL;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_DOCUMENT_H
+#define UIC_DOCUMENT_H
+
+#include "../ui/toolkit.h"
+#include "context.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiDoc {
+ UiContext *ctx;
+ char doc[];
+} UiDoc;
+
+void uic_document_addvar(void *doc, char *name, int type, size_t vs);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_DOCUMENT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 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 "menu.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <cx/linked_list.h>
+#include <cx/array_list.h>
+
+
+static UiMenuBuilder *current_builder;
+static UiMenuBuilder global_builder;
+
+static int menu_item_counter = 0;
+
+void uic_menu_init(void) {
+ global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ current_builder = &global_builder;
+}
+
+static void add_menu(UiMenu *menu) {
+ cx_linked_list_add(
+ (void**)¤t_builder->menus_begin,
+ (void**)¤t_builder->menus_end,
+ offsetof(UiMenu, item.prev),
+ offsetof(UiMenu, item.next),
+ menu);
+}
+
+static void add_item(UiMenuItemI *item) {
+ UiMenu *menu = cxListAt(current_builder->current, 0);
+ cx_linked_list_add(
+ (void**)&menu->items_begin,
+ (void**)&menu->items_end,
+ offsetof(UiMenu, item.prev),
+ offsetof(UiMenu, item.next),
+ item);
+}
+
+static void mitem_set_id(UiMenuItemI *item) {
+ snprintf(item->id, 8, "%x", menu_item_counter++);
+}
+
+static char* nl_strdup(const char* s) {
+ return s ? strdup(s) : NULL;
+}
+
+int* uic_copy_groups(const int* groups, size_t *ngroups) {
+ *ngroups = 0;
+ if (!groups) {
+ return NULL;
+ }
+
+ size_t n;
+ for (n = 0; groups[n] > -1; n++) { }
+
+ if (ngroups > 0) {
+ int* newarray = calloc(n+1, sizeof(int));
+ memcpy(newarray, groups, n * sizeof(int));
+ newarray[n] = -1;
+ *ngroups = n;
+ return newarray;
+ }
+ return NULL;
+}
+
+void ui_menu_create(const char *label) {
+ // create menu
+ UiMenu *menu = malloc(sizeof(UiMenu));
+ mitem_set_id(&menu->item);
+ menu->item.prev = NULL;
+ menu->item.next = NULL;
+ menu->item.type = UI_MENU;
+
+ menu->label = nl_strdup(label);
+ menu->items_begin = NULL;
+ menu->items_end = NULL;
+ menu->parent = NULL;
+
+ menu->end = 0;
+
+ if (cxListSize(current_builder->current) == 0) {
+ add_menu(menu);
+ }
+ else {
+ add_item((UiMenuItemI*)menu);
+ }
+ uic_add_menu_to_stack(menu);
+}
+
+UIEXPORT void ui_menu_end(void) {
+ cxListRemove(current_builder->current, 0);
+ if(cxListSize(current_builder->current) == 0) {
+ current_builder = &global_builder;
+ }
+}
+
+
+
+void ui_menuitem_create(UiMenuItemArgs *args) {
+ UiMenuItem* item = malloc(sizeof(UiMenuItem));
+ mitem_set_id(&item->item);
+ item->item.prev = NULL;
+ item->item.next = NULL;
+ item->item.type = UI_MENU_ITEM;
+
+ item->label = nl_strdup(args->label);
+ item->icon = nl_strdup(args->icon);
+ item->userdata = args->onclickdata;
+ item->callback = args->onclick;
+ item->groups = uic_copy_groups(args->groups, &item->ngroups);
+
+ add_item((UiMenuItemI*)item);
+}
+
+void ui_menuseparator() {
+ UiMenuItemI *item = malloc(sizeof(UiMenuItemI));
+ item->id[0] = 0;
+ item->prev = NULL;
+ item->next = NULL;
+ item->type = UI_MENU_SEPARATOR;
+
+ add_item((UiMenuItemI*)item);
+}
+
+void ui_menu_toggleitem_create(UiMenuToggleItemArgs *args) {
+ UiMenuCheckItem *item = malloc(sizeof(UiMenuCheckItem));
+ mitem_set_id(&item->item);
+ item->item.prev = NULL;
+ item->item.next = NULL;
+ item->item.type = UI_MENU_CHECK_ITEM;
+
+ item->label = nl_strdup(args->label);
+ item->icon = nl_strdup(args->icon);
+ item->varname = nl_strdup(args->varname);
+ item->userdata = args->onchangedata;
+ item->callback = args->onchange;
+ item->groups = uic_copy_groups(args->groups, &item->ngroups);
+
+ add_item((UiMenuItemI*)item);
+}
+
+void ui_menu_radioitem_create(UiMenuToggleItemArgs *args) {
+ UiMenuCheckItem* item = malloc(sizeof(UiMenuCheckItem));
+ mitem_set_id(&item->item);
+ item->item.prev = NULL;
+ item->item.next = NULL;
+ item->item.type = UI_MENU_RADIO_ITEM;
+
+ item->label = nl_strdup(args->label);
+ item->icon = nl_strdup(args->icon);
+ item->varname = nl_strdup(args->varname);
+ item->userdata = args->onchangedata;
+ item->callback = args->onchange;
+ item->groups = uic_copy_groups(args->groups, &item->ngroups);
+
+ add_item((UiMenuItemI*)item);
+}
+
+void ui_menu_itemlist_create(UiMenuItemListArgs *args) {
+ UiMenuItemList*item = malloc(sizeof(UiMenuItemList));
+ mitem_set_id(&item->item);
+ item->item.prev = NULL;
+ item->item.next = NULL;
+ item->item.type = UI_MENU_ITEM_LIST;
+ item->getvalue = args->getvalue;
+ item->callback = args->onselect;
+ item->userdata = args->onselectdata;
+ item->varname = nl_strdup(args->varname);
+ item->addseparator = args->addseparator;
+
+ add_item((UiMenuItemI*)item);
+}
+
+void ui_menu_checkitemlist_create(UiMenuItemListArgs *args) {
+ UiMenuItemList* item = malloc(sizeof(UiMenuItemList));
+ mitem_set_id(&item->item);
+ item->item.prev = NULL;
+ item->item.next = NULL;
+ item->item.type = UI_MENU_CHECKITEM_LIST;
+ item->callback = args->onselect;
+ item->userdata = args->onselectdata;
+ item->varname = nl_strdup(args->varname);
+
+ add_item((UiMenuItemI*)item);
+}
+
+void ui_menu_radioitemlist_create(UiMenuItemListArgs *args) {
+ UiMenuItemList* item = malloc(sizeof(UiMenuItemList));
+ mitem_set_id(&item->item);
+ item->item.prev = NULL;
+ item->item.next = NULL;
+ item->item.type = UI_MENU_RADIOITEM_LIST;
+ item->callback = args->onselect;
+ item->userdata = args->onselectdata;
+ item->varname = nl_strdup(args->varname);
+
+ add_item((UiMenuItemI*)item);
+}
+
+
+void uic_add_menu_to_stack(UiMenu* menu) {
+ cxListInsert(current_builder->current, 0, menu);
+}
+
+UiMenu* uic_get_menu_list(void) {
+ return current_builder->menus_begin;
+}
+
+UIEXPORT void ui_menu_close(void) {
+ UiMenu* menu = cxListAt(current_builder->current, 0);
+ menu->end = 1;
+}
+
+UIEXPORT int ui_menu_is_open(void) {
+ UiMenu* menu = cxListAt(current_builder->current, 0);
+ if (menu->end) {
+ ui_menu_end();
+ return 0;
+ }
+ return 1;
+}
+
+
+void ui_contextmenu_builder(UiMenuBuilder **out_builder) {
+ UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder));
+ builder->menus_begin = NULL;
+ builder->menus_end = NULL;
+ builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ builder->ref = 1;
+ current_builder = builder;
+ *out_builder = builder;
+
+ ui_menu_create(NULL);
+}
+
+
+
+static void free_menuitem(UiMenuItemI *item) {
+ switch(item->type) {
+ default: break;
+ case UI_MENU: {
+ UiMenu *menu = (UiMenu*)item;
+ free(menu->label);
+ UiMenuItemI *m = menu->items_begin;
+ while(m) {
+ UiMenuItemI *next = m->next;
+ free_menuitem(m);
+ m = next;
+ }
+ break;
+ }
+ case UI_MENU_ITEM: {
+ UiMenuItem *i = (UiMenuItem*)item;
+ free(i->groups);
+ free(i->label);
+ free(i->icon);
+ break;
+ }
+ case UI_MENU_CHECK_ITEM: {
+ UiMenuCheckItem *i = (UiMenuCheckItem*)item;
+ free(i->groups);
+ free(i->label);
+ free(i->icon);
+ free(i->varname);
+ break;
+ }
+ case UI_MENU_RADIO_ITEM: {
+ UiMenuRadioItem *i = (UiMenuRadioItem*)item;
+ free(i->groups);
+ free(i->label);
+ free(i->icon);
+ free(i->varname);
+ break;
+ }
+ case UI_MENU_ITEM_LIST: {
+ break;
+ }
+ case UI_MENU_CHECKITEM_LIST: {
+ break;
+ }
+ case UI_MENU_RADIOITEM_LIST: {
+ break;
+ }
+ }
+
+ free(item);
+}
+
+void ui_menubuilder_free(UiMenuBuilder *builder) {
+ UiMenuItemI *m = &builder->menus_begin->item;
+ while(m) {
+ UiMenuItemI *next = m->next;
+ free_menuitem(m);
+ m = next;
+ }
+ cxListFree(builder->current);
+ free(builder);
+}
+
+void ui_menubuilder_ref(UiMenuBuilder *builder) {
+ builder->ref++;
+}
+
+void ui_menubuilder_unref(UiMenuBuilder *builder) {
+ if(--builder->ref <= 0) {
+ ui_menubuilder_free(builder);
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 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_MENU_H
+#define UIC_MENU_H
+
+#include "../ui/menu.h"
+
+#include <cx/linked_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiMenuItemI UiMenuItemI;
+typedef struct UiMenu UiMenu;
+typedef struct UiMenuItem UiMenuItem;
+typedef struct UiMenuCheckItem UiMenuCheckItem;
+typedef struct UiMenuRadioItem UiMenuRadioItem;
+typedef struct UiMenuItemList UiMenuItemList;
+
+enum UiMenuItemType {
+ UI_MENU = 0,
+ UI_MENU_ITEM,
+ UI_MENU_CHECK_ITEM,
+ UI_MENU_RADIO_ITEM,
+ UI_MENU_ITEM_LIST,
+ UI_MENU_CHECKITEM_LIST,
+ UI_MENU_RADIOITEM_LIST,
+ UI_MENU_SEPARATOR
+};
+
+typedef enum UiMenuItemType UiMenuItemType;
+
+struct UiMenuItemI {
+ UiMenuItemI *prev;
+ UiMenuItemI *next;
+ UiMenuItemType type;
+ char id[8];
+};
+
+struct UiMenu {
+ UiMenuItemI item;
+ char *label;
+ UiMenuItemI *items_begin;
+ UiMenuItemI *items_end;
+ UiMenu *parent;
+ int end;
+};
+
+struct UiMenuItem {
+ UiMenuItemI item;
+ ui_callback callback;
+ char *label;
+ char *icon;
+ void *userdata;
+ int *groups;
+ size_t ngroups;
+};
+
+struct UiMenuCheckItem {
+ UiMenuItemI item;
+ char *label;
+ char *icon;
+ char *varname;
+ ui_callback callback;
+ void *userdata;
+ int *groups;
+ size_t ngroups;
+};
+
+struct UiMenuRadioItem {
+ UiMenuItemI item;
+ char *label;
+ char *icon;
+ char *varname;
+ ui_callback callback;
+ void *userdata;
+ int *groups;
+ size_t ngroups;
+};
+
+struct UiMenuItemList {
+ UiMenuItemI item;
+ ui_getvaluefunc getvalue;
+ ui_callback callback;
+ void *userdata;
+ char *varname;
+ UiBool addseparator;
+};
+
+
+
+struct UiMenuBuilder {
+ UiMenu *menus_begin;
+ UiMenu *menus_end;
+ CxList *current;
+ int ref;
+};
+
+void uic_menu_init(void);
+
+UiMenu* uic_get_menu_list(void);
+
+void uic_add_menu_to_stack(UiMenu* menu);
+
+int* uic_copy_groups(const int* groups, size_t *ngroups);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_MENU_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "object.h"
+#include "context.h"
+
+#include <cx/linked_list.h>
+
+#include "../ui/container.h"
+
+static CxList *creation_callbacks;
+static CxList *destruction_callbacks;
+
+typedef struct objcallback {
+ ui_object_callback func;
+ void *userdata;
+} objcallback;
+
+void ui_register_object_creation_callback(ui_object_callback func, void *userdata) {
+ if(!creation_callbacks) {
+ creation_callbacks = cxLinkedListCreateSimple(sizeof(objcallback));
+ }
+ objcallback cb = { func, userdata };
+ cxListAdd(creation_callbacks, &cb);
+}
+
+void ui_register_object_destruction_callback(ui_object_callback func, void *userdata) {
+ if(!destruction_callbacks) {
+ destruction_callbacks = cxLinkedListCreateSimple(sizeof(objcallback));
+ }
+ objcallback cb = { func, userdata };
+ cxListAdd(destruction_callbacks, &cb);
+}
+
+void uic_object_created(UiObject *obj) {
+ CxIterator i = cxListIterator(creation_callbacks);
+ cx_foreach(objcallback *, cb, i) {
+ cb->func(obj, cb->userdata);
+ }
+}
+
+void uic_object_destroyed(UiObject *obj) {
+ CxIterator i = cxListIterator(destruction_callbacks);
+ cx_foreach(objcallback *, cb, i) {
+ cb->func(obj, cb->userdata);
+ }
+}
+
+void ui_object_ref(UiObject *obj) {
+ obj->ref++;
+}
+
+int ui_object_unref(UiObject *obj) {
+ // it is possible to have 0 references, in case
+ // a window was created but ui_show was never called
+ if(obj->ref == 0 || --obj->ref == 0) {
+ if(obj->destroy) {
+ obj->destroy(obj);
+ }
+ uic_object_destroy(obj);
+ return 0;
+ }
+ return 1;
+}
+
+void uic_object_destroy(UiObject *obj) {
+ if(obj->ctx->close_callback) {
+ UiEvent ev;
+ ev.window = obj->window;
+ ev.document = obj->ctx->document;
+ ev.obj = obj;
+ ev.eventdata = NULL;
+ ev.eventdatatype = 0;
+ ev.intval = 0;
+ obj->ctx->close_callback(&ev, obj->ctx->close_data);
+ }
+ uic_object_destroyed(obj);
+ cxMempoolFree(obj->ctx->mp);
+}
+
+UiObject* uic_object_new_toplevel(void) {
+ fflush(stdout);
+ CxMempool *mp = cxMempoolCreateSimple(256);
+ UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject));
+ fflush(stdout);
+ obj->ctx = uic_context(obj, mp);
+ obj->ctx->parent = ui_global_context();
+ fflush(stdout);
+ uic_object_created(obj);
+ fflush(stdout);
+ return obj;
+}
+
+UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) {
+ UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObject));
+ newobj->ctx = ctx;
+ newobj->widget = widget;
+ uic_object_created(newobj);
+ return newobj;
+}
+
+void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer) {
+ newcontainer->prev = toplevel->container_end;
+ if(toplevel->container_end) {
+ toplevel->container_end->next = newcontainer;
+ toplevel->container_end = newcontainer;
+ } else {
+ toplevel->container_begin = newcontainer;
+ toplevel->container_end = newcontainer;
+ }
+}
+
+void uic_object_pop_container(UiObject *toplevel) {
+ toplevel->container_end = toplevel->container_end->prev;
+ if(toplevel->container_end) {
+ toplevel->container_end->next = NULL;
+ } else {
+ toplevel->container_begin = NULL;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_OBJECT_H
+#define UIC_OBJECT_H
+
+#include "../ui/toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*ui_object_callback)(UiObject *obj, void *userdata);
+
+void ui_register_object_creation_callback(ui_object_callback func, void *userdata);
+void ui_register_object_destruction_callback(ui_object_callback func, void *userdata);
+
+void uic_object_created(UiObject *obj);
+void uic_object_destroyed(UiObject *obj);
+
+void uic_object_destroy(UiObject *obj);
+
+UiObject* uic_object_new_toplevel(void);
+UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget);
+UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget);
+
+void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer);
+void uic_object_pop_container(UiObject *toplevel);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_OBJECT_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2017 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+COMMON_SRC_DIR = ui/common/
+COMMON_OBJPRE = $(OBJ_DIR)$(COMMON_SRC_DIR)
+
+COMMON_OBJ = context$(OBJ_EXT)
+COMMON_OBJ += document$(OBJ_EXT)
+COMMON_OBJ += object$(OBJ_EXT)
+COMMON_OBJ += container$(OBJ_EXT)
+COMMON_OBJ += types$(OBJ_EXT)
+COMMON_OBJ += properties$(OBJ_EXT)
+COMMON_OBJ += menu$(OBJ_EXT)
+COMMON_OBJ += toolbar$(OBJ_EXT)
+COMMON_OBJ += ucx_properties$(OBJ_EXT)
+COMMON_OBJ += threadpool$(OBJ_EXT)
+COMMON_OBJ += condvar$(OBJ_EXT)
+COMMON_OBJ += args$(OBJ_EXT)
+COMMON_OBJ += wrapper$(OBJ_EXT)
+
+TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)uic_%)
+TOOLKITSOURCE += $(COMMON_OBJ:%$(OBJ_EXT)=common/%.c)
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#endif
+
+#include "../ui/toolkit.h"
+
+
+#include "properties.h"
+#include <cx/string.h>
+#include <cx/buffer.h>
+
+#include <cx/hash_map.h>
+#include "ucx_properties.h"
+
+static CxMap *application_properties;
+static CxMap *language;
+
+#ifndef UI_COCOA
+
+static char *locales_dir;
+static char *pixmaps_dir;
+
+#endif
+
+
+char* ui_getappdir(void) {
+ if(ui_appname() == NULL) {
+ return NULL;
+ }
+
+ return ui_configfile(NULL);
+}
+
+#ifndef _WIN32
+#define UI_PATH_SEPARATOR '/'
+#define UI_ENV_HOME "HOME"
+#else
+#define UI_PATH_SEPARATOR '\\'
+#define UI_ENV_HOME "USERPROFILE"
+#endif
+
+char* ui_configfile(const char *name) {
+ const char *appname = ui_appname();
+ if(!appname) {
+ return NULL;
+ }
+
+ CxBuffer buf;
+ cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+
+ // add base dir
+ char *homeenv = getenv(UI_ENV_HOME);
+ if(homeenv == NULL) {
+ cxBufferDestroy(&buf);
+ return NULL;
+ }
+ cxstring home = cx_str(homeenv);
+
+ cxBufferWrite(home.ptr, 1, home.length, &buf);
+ if(home.ptr[home.length-1] != UI_PATH_SEPARATOR) {
+ cxBufferPut(&buf, UI_PATH_SEPARATOR);
+ }
+
+#ifdef UI_COCOA
+ // on OS X the app dir is $HOME/Library/Application Support/$APPNAME/
+ cxBufferPutString(&buf, "Library/Application Support/");
+#elif defined(_WIN32)
+ // on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/
+ cxBufferPutString(&buf, "AppData\\Local\\");
+#else
+ // app dir is $HOME/.$APPNAME/
+ cxBufferPut(&buf, '.');
+#endif
+ cxBufferPutString(&buf, appname);
+ cxBufferPut(&buf, UI_PATH_SEPARATOR);
+
+ // add file name
+ if(name) {
+ cxBufferPutString(&buf, name);
+ }
+
+ cxBufferPut(&buf, 0);
+
+ return buf.space;
+}
+
+static int ui_mkdir(char *path) {
+#ifdef _WIN32
+ return _mkdir(path);
+#else
+ return mkdir(path, S_IRWXU);
+#endif
+}
+
+void uic_load_app_properties() {
+ application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128);
+ application_properties->collection.simple_destructor = free;
+
+ if(!ui_appname()) {
+ // applications without name cannot load app properties
+ return;
+ }
+
+ char *dir = ui_configfile(NULL);
+ if(!dir) {
+ return;
+ }
+ if(ui_mkdir(dir)) {
+ if(errno != EEXIST) {
+ fprintf(stderr, "Ui Error: Cannot create directory %s\n", dir);
+ free(dir);
+ return;
+ }
+ }
+ free(dir);
+
+ char *path = ui_configfile("application.properties");
+ if(!path) {
+ return;
+ }
+
+ FILE *file = fopen(path, "r");
+ if(!file) {
+ free(path);
+ return;
+ }
+
+ if(ucx_properties_load(application_properties, file)) {
+ fprintf(stderr, "Ui Error: Cannot load application properties.\n");
+ }
+
+ fclose(file);
+ free(path);
+}
+
+int uic_store_app_properties() {
+ char *path = ui_configfile("application.properties");
+ if(!path) {
+ return 1;
+ }
+
+ FILE *file = fopen(path, "w");
+ if(!file) {
+ fprintf(stderr, "Ui Error: Cannot open properties file: %s\n", path);
+ free(path);
+ return 1;
+ }
+
+ int ret = 0;
+ if(ucx_properties_store(application_properties, file)) {
+ fprintf(stderr, "Ui Error: Cannot store application properties.\n");
+ ret = 1;
+ }
+
+ fclose(file);
+ free(path);
+
+ return ret;
+}
+
+// public
+int ui_app_save_settings(void) {
+ return uic_store_app_properties();
+}
+
+
+const char* ui_get_property(const char *name) {
+ return cxMapGet(application_properties, name);
+}
+
+void ui_set_property(const char *name, const char *value) {
+ if(value) {
+ cxMapPut(application_properties, name, strdup(value));
+ } else {
+ cxMapRemove(application_properties, name);
+ }
+}
+
+const char* ui_set_default_property(const char *name, const char *value) {
+ const char *v = cxMapGet(application_properties, name);
+ if(!v) {
+ cxMapPut(application_properties, name, (char*)value);
+ v = value;
+ }
+ return v;
+}
+
+int ui_properties_store(void) {
+ return uic_store_app_properties();
+}
+
+
+static char* uic_concat_path(const char *base, const char *p, const char *ext) {
+ size_t baselen = strlen(base);
+
+ CxBuffer *buf = cxBufferCreate(NULL, 32, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ if(baselen > 0) {
+ cxBufferWrite(base, 1, baselen, buf);
+ if(base[baselen - 1] != '/') {
+ cxBufferPut(buf, '/');
+ }
+ }
+ cxBufferWrite(p, 1, strlen(p), buf);
+ if(ext) {
+ cxBufferWrite(ext, 1, strlen(ext), buf);
+ }
+
+ char *str = buf->space;
+ free(buf);
+ return str;
+}
+
+#ifndef UI_COCOA
+
+void ui_locales_dir(const char *path) {
+ free(locales_dir);
+ locales_dir = path ? strdup(path) : NULL;
+}
+
+void ui_pixmaps_dir(const char *path) {
+ free(pixmaps_dir);
+ pixmaps_dir = path ? strdup(path) : NULL;
+}
+
+char* uic_get_image_path(const char *imgfilename) {
+ if(pixmaps_dir) {
+ return uic_concat_path(pixmaps_dir, imgfilename, NULL);
+ } else {
+ return NULL;
+ }
+}
+
+void ui_load_lang(const char *locale) {
+ if(!locale) {
+ locale = "en_EN";
+ }
+
+ char *path = uic_concat_path(locales_dir, locale, ".properties");
+
+ uic_load_language_file(path);
+ free(path);
+}
+
+void ui_load_lang_def(char *locale, char *default_locale) {
+ char tmp[6];
+ if(!locale) {
+ char *lang = getenv("LANG");
+ if(lang && strlen(lang) >= 5) {
+ memcpy(tmp, lang, 5);
+ tmp[5] = '\0';
+ locale = tmp;
+ } else {
+ locale = default_locale;
+ }
+ }
+
+ char *path = uic_concat_path(locales_dir, locale, ".properties");
+
+ if(uic_load_language_file(path)) {
+ if(default_locale) {
+ ui_load_lang_def(default_locale, NULL);
+ } else {
+ // cannot find any language file
+ fprintf(stderr, "Ui Error: Cannot load language.\n");
+ free(path);
+ exit(-1);
+ }
+ }
+ free(path);
+}
+
+#endif
+
+int uic_load_language_file(const char *path) {
+ CxMap *lang = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 256);
+
+ FILE *file = fopen(path, "r");
+ if(!file) {
+ return 1;
+ }
+
+ if(ucx_properties_load(lang, file)) {
+ fprintf(stderr, "Ui Error: Cannot parse language file: %s.\n", path);
+ }
+
+ fclose(file);
+
+ cxMapRehash(lang);
+
+ language = lang;
+
+ return 0;
+}
+
+char* uistr(const char *name) {
+ char *value = uistr_n(name);
+ return value ? value : "missing string";
+}
+
+char* uistr_n(const char *name) {
+ if(!language) {
+ return NULL;
+ }
+ return cxMapGet(language, name);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_PROPERTIES_H
+#define UIC_PROPERTIES_H
+
+#include "../ui/properties.h"
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#define UI_HOME "USERPROFILE"
+#else
+#define UI_HOME "HOME"
+#endif
+
+void uic_load_app_properties();
+int uic_store_app_properties();
+
+int uic_load_language_file(const char *path);
+char* uic_get_image_path(const char *imgfilename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_PROPERTIES_H */
+
--- /dev/null
+/*
+ * 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 _WIN32
+
+#include "threadpool.h"
+#include "context.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+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) {
+ UiThreadpool *pool = threadpool_new(nthreads, nthreads);
+ threadpool_start(pool); // TODO: check return value
+ return pool;
+}
+
+void ui_threadpool_destroy(UiThreadpool* pool) {
+
+}
+
+static int ui_threadpool_job_finish(void *data) {
+ UiJob *job = data;
+ UiEvent event;
+ event.obj = job->obj;
+ event.window = job->obj->window;
+ event.document = job->obj->ctx->document;
+ event.intval = 0;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ job->finish_callback(&event, job->finish_data);
+ free(job);
+ return 0;
+}
+
+static void* ui_threadpool_job_func(void *data) {
+ UiJob *job = data;
+ if (!job->job_func(job->job_data) && job->finish_callback) {
+ ui_call_mainthread(ui_threadpool_job_finish, job);
+ } else {
+ 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
+/*
+ * 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"
+
+#ifndef _WIN32
+#include <pthread.h>
+#endif
+
+#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 */
+
--- /dev/null
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2023 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#include "toolbar.h"\r
+#include "menu.h"\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+static CxMap* toolbar_items;\r
+\r
+static CxList* toolbar_defaults[8]; // 0: left 1: center 2: right\r
+\r
+static UiToolbarMenuItem* ui_appmenu;\r
+\r
+\r
+void uic_toolbar_init(void) {\r
+ toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);\r
+ for(int i=0;i<8;i++) {\r
+ toolbar_defaults[i] = cxLinkedListCreateSimple(CX_STORE_POINTERS);\r
+ }\r
+}\r
+\r
+static char* nl_strdup(const char* str) {\r
+ return str ? strdup(str) : NULL;\r
+}\r
+\r
+static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups, size_t *nvstates) {\r
+ UiToolbarItemArgs newargs;\r
+ newargs.label = nl_strdup(args->label);\r
+ newargs.icon = nl_strdup(args->icon);\r
+ newargs.tooltip = nl_strdup(args->tooltip);\r
+ newargs.onclick = args->onclick;\r
+ newargs.onclickdata = args->onclickdata;\r
+ newargs.groups = uic_copy_groups(args->groups, ngroups);\r
+ newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+ return newargs;\r
+}\r
+\r
+void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args) {\r
+ UiToolbarItem* item = malloc(sizeof(UiToolbarItem));\r
+ item->item.type = UI_TOOLBAR_ITEM;\r
+ item->args = itemargs_copy(args, &item->ngroups, &item->nvstates);\r
+ cxMapPut(toolbar_items, name, item);\r
+}\r
+\r
+\r
+static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args, size_t *ngroups, size_t *nvstates) {\r
+ UiToolbarToggleItemArgs newargs;\r
+ newargs.label = nl_strdup(args->label);\r
+ newargs.icon = nl_strdup(args->icon);\r
+ newargs.tooltip = nl_strdup(args->tooltip);\r
+ newargs.varname = nl_strdup(args->varname);\r
+ newargs.onchange = args->onchange;\r
+ newargs.onchangedata = args->onchangedata;\r
+ newargs.groups = uic_copy_groups(args->groups, ngroups);\r
+ newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+ return newargs;\r
+}\r
+\r
+void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *args) {\r
+ UiToolbarToggleItem* item = malloc(sizeof(UiToolbarToggleItem));\r
+ item->item.type = UI_TOOLBAR_TOGGLEITEM;\r
+ item->args = toggleitemargs_copy(args, &item->ngroups, &item->nvstates);\r
+ cxMapPut(toolbar_items, name, item);\r
+}\r
+\r
+static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args, size_t *nvstates) {\r
+ UiToolbarMenuArgs newargs;\r
+ newargs.label = nl_strdup(args->label);\r
+ newargs.icon = nl_strdup(args->icon);\r
+ newargs.tooltip = nl_strdup(args->tooltip);\r
+ newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+ return newargs;\r
+}\r
+\r
+UIEXPORT void ui_toolbar_menu_create(const char* name, UiToolbarMenuArgs *args) {\r
+ UiToolbarMenuItem* item = malloc(sizeof(UiToolbarMenuItem));\r
+ item->item.type = UI_TOOLBAR_MENU;\r
+ memset(&item->menu, 0, sizeof(UiMenu));\r
+ item->args = menuargs_copy(args, &item->nvstates);\r
+\r
+ item->end = 0;\r
+\r
+ if (!name) {\r
+ // special appmenu\r
+ ui_appmenu = item;\r
+ } else {\r
+ // toplevel menu\r
+ cxMapPut(toolbar_items, name, item);\r
+ }\r
+\r
+ uic_add_menu_to_stack(&item->menu);\r
+}\r
+\r
+\r
+CxMap* uic_get_toolbar_items(void) {\r
+ return toolbar_items;\r
+}\r
+\r
+CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos) {\r
+ if (pos >= 0 && pos < 8) {\r
+ return toolbar_defaults[pos];\r
+ }\r
+ return NULL;\r
+}\r
+\r
+void ui_toolbar_add_default(const char* name, enum UiToolbarPos pos) {\r
+ char* cp = strdup(name);\r
+ if (pos >= 0 && pos < 8) {\r
+ cxListAdd(toolbar_defaults[pos], cp);\r
+ } else {\r
+ fprintf(stderr, "Error: illegal toolbar position: %d\n", (int)pos);\r
+ }\r
+}\r
+\r
+UiBool uic_toolbar_isenabled(void) {\r
+ size_t size = 0;\r
+ for(int i=0;i<8;i++) {\r
+ size += cxListSize(toolbar_defaults[i]);\r
+ }\r
+ return size > 0;\r
+}\r
+\r
+UiToolbarItemI* uic_toolbar_get_item(const char* name) {\r
+ return cxMapGet(toolbar_items, name);\r
+}\r
+\r
+UiToolbarMenuItem* uic_get_appmenu(void) {\r
+ return ui_appmenu;\r
+}\r
--- /dev/null
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2023 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#ifndef UIC_TOOLBAR_H\r
+#define UIC_TOOLBAR_H\r
+\r
+#include "../ui/toolbar.h"\r
+\r
+#include <cx/linked_list.h>\r
+#include <cx/hash_map.h>\r
+\r
+#include "menu.h"\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+typedef struct UiToolbarItemI UiToolbarItemI;\r
+\r
+typedef struct UiToolbarItem UiToolbarItem;\r
+typedef struct UiToolbarToggleItem UiToolbarToggleItem;\r
+\r
+typedef struct UiToolbarMenuItem UiToolbarMenuItem;\r
+\r
+enum UiToolbarItemType {\r
+ UI_TOOLBAR_ITEM = 0,\r
+ UI_TOOLBAR_TOGGLEITEM,\r
+ UI_TOOLBAR_MENU\r
+};\r
+\r
+typedef enum UiToolbarItemType UiToolbarItemType;\r
+\r
+struct UiToolbarItemI {\r
+ UiToolbarItemType type;\r
+};\r
+\r
+struct UiToolbarItem {\r
+ UiToolbarItemI item;\r
+ UiToolbarItemArgs args;\r
+ size_t ngroups;\r
+ size_t nvstates;\r
+};\r
+\r
+struct UiToolbarToggleItem {\r
+ UiToolbarItemI item;\r
+ UiToolbarToggleItemArgs args;\r
+ size_t ngroups;\r
+ size_t nvstates;\r
+};\r
+\r
+struct UiToolbarMenuItem {\r
+ UiToolbarItemI item;\r
+ UiMenu menu;\r
+ UiToolbarMenuArgs args;\r
+ size_t nvstates;\r
+ int end;\r
+};\r
+\r
+\r
+void uic_toolbar_init(void);\r
+\r
+CxMap* uic_get_toolbar_items(void);\r
+CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos);\r
+\r
+UiBool uic_toolbar_isenabled(void);\r
+\r
+UiToolbarItemI* uic_toolbar_get_item(const char* name);\r
+\r
+UiToolbarMenuItem* uic_get_appmenu(void);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif /* UIC_TOOLBAR_H */\r
+\r
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <cx/list.h>
+#include <cx/array_list.h>
+#include "../ui/tree.h"
+#include "types.h"
+#include "context.h"
+#include "../ui/image.h"
+
+static ui_list_init_func default_list_init;
+static void *default_list_init_userdata;
+
+UiObserver* ui_observer_new(ui_callback f, void *data) {
+ UiObserver *observer = malloc(sizeof(UiObserver));
+ observer->callback = f;
+ observer->data = data;
+ observer->next = NULL;
+ return observer;
+}
+
+UiObserver* ui_obsvlist_add(UiObserver *list, UiObserver *observer) {
+ if(!list) {
+ return observer;
+ } else {
+ UiObserver *l = list;
+ while(l->next) {
+ l = l->next;
+ }
+ l->next = observer;
+ return list;
+ }
+}
+
+UiObserver* ui_add_observer(UiObserver *list, ui_callback f, void *data) {
+ UiObserver *observer = ui_observer_new(f, data);
+ return ui_obsvlist_add(list, observer);
+}
+
+void ui_notify(UiObserver *observer, void *data) {
+ ui_notify_except(observer, NULL, data);
+}
+
+void ui_notify_except(UiObserver *observer, UiObserver *exc, void *data) {
+ UiEvent evt;
+ evt.obj = NULL;
+ evt.window = NULL;
+ evt.document = NULL;
+ evt.eventdata = data;
+ evt.eventdatatype = UI_EVENT_DATA_POINTER;
+ evt.intval = 0;
+
+ while(observer) {
+ if(observer != exc) {
+ observer->callback(&evt, observer->data);
+ }
+ observer = observer->next;
+ }
+}
+
+void ui_notify_evt(UiObserver *observer, UiEvent *event) {
+ while(observer) {
+ observer->callback(event, observer->data);
+ observer = observer->next;
+ }
+}
+
+/* --------------------------- UiList --------------------------- */
+
+void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) {
+ list->data = cxArrayListCreate(ctx->mp->allocator, NULL, CX_STORE_POINTERS, 32);
+ list->first = ui_list_first;
+ list->next = ui_list_next;
+ list->get = ui_list_get;
+ list->count = ui_list_count;
+}
+
+UiList* ui_list_new(UiContext *ctx, const char *name) {
+ return ui_list_new2(ctx, name, default_list_init ? default_list_init : uic_ucx_list_init, default_list_init_userdata);
+}
+
+UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func listinit, void *userdata) {
+ UiList *list = cxMalloc(ctx->mp->allocator, sizeof(UiList));
+ memset(list, 0, sizeof(UiList));
+ listinit(ctx, list, userdata);
+
+ if(name) {
+ uic_reg_var(ctx, name, UI_VAR_LIST, list);
+ }
+
+ return list;
+}
+
+void ui_list_free(UiList *list) {
+ cxListFree(list->data);
+ free(list);
+}
+
+void* ui_list_first(UiList *list) {
+ list->iter = (void*)(intptr_t)0;
+ return cxListAt(list->data, 0);
+}
+
+void* ui_list_next(UiList *list) {
+ intptr_t iter = (intptr_t)list->iter;
+ iter++;
+ void *elm = cxListAt(list->data, iter);
+ if(elm) {
+ list->iter = (void*)iter;
+ }
+ return elm;
+}
+
+void* ui_list_get(UiList *list, int i) {
+ return cxListAt(list->data, i);
+}
+
+int ui_list_count(UiList *list) {
+ return cxListSize(list->data);
+}
+
+void ui_list_append(UiList *list, void *data) {
+ cxListAdd(list->data, data);
+}
+
+void ui_list_prepend(UiList *list, void *data) {
+ cxListInsert(list->data, 0, data);
+}
+
+void ui_list_remove(UiList *list, int i) {
+ cxListRemove(list->data, i);
+}
+
+void ui_list_clear(UiList *list) {
+ cxListClear(list->data);
+}
+
+void ui_list_update(UiList *list) {
+ if(list->update) {
+ list->update(list, -1);
+ }
+}
+
+void ui_list_update_row(UiList *list, int row) {
+ if(list->update) {
+ list->update(list, row);
+ }
+}
+
+UiListSelection ui_list_get_selection(UiList *list) {
+ if(list->getselection) {
+ return list->getselection(list);
+ } else {
+ return (UiListSelection){0, NULL};
+ }
+}
+
+void ui_list_addobsv(UiList *list, ui_callback f, void *data) {
+ list->observers = ui_add_observer(list->observers, f, data);
+}
+
+void ui_list_notify(UiList *list) {
+ ui_notify(list->observers, list);
+}
+
+
+typedef struct {
+ int type;
+ char *name;
+} UiColumn;
+
+UiModel* ui_model(UiContext *ctx, ...) {
+ UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+
+ va_list ap;
+ va_start(ap, ctx);
+
+ CxList *cols = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(UiColumn), 32);
+ int type;
+ while((type = va_arg(ap, int)) != -1) {
+ char *name = va_arg(ap, char*);
+
+ UiColumn column;
+ column.type = type;
+ column.name = name;
+
+ cxListAdd(cols, &column);
+ }
+
+ va_end(ap);
+
+ size_t len = cxListSize(cols);
+ info->alloc = len;
+ info->columns = len;
+ info->types = ui_calloc(ctx, len, sizeof(UiModelType));
+ info->titles = ui_calloc(ctx, len, sizeof(char*));
+ info->columnsize = ui_calloc(ctx, len, sizeof(int));
+
+ int i = 0;
+ CxIterator iter = cxListIterator(cols);
+ cx_foreach(UiColumn*, c, iter) {
+ info->types[i] = c->type;
+ info->titles[i] = ui_strdup(ctx, c->name);
+ i++;
+ }
+ cxListFree(cols);
+
+ return info;
+}
+
+#define UI_MODEL_DEFAULT_ALLOC_SIZE 16
+
+UiModel* ui_model_new(UiContext *ctx) {
+ UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+ info->alloc = UI_MODEL_DEFAULT_ALLOC_SIZE;
+ info->types = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(UiModelType));
+ info->titles = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(char*));
+ info->columnsize = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(int));
+ return info;
+}
+
+void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width) {
+ if(model->columns >= model->alloc) {
+ model->alloc += UI_MODEL_DEFAULT_ALLOC_SIZE;
+ model->types = ui_realloc(ctx, model->types, model->alloc * sizeof(UiModelType));
+ model->titles = ui_realloc(ctx, model->titles, model->alloc * sizeof(char*));
+ model->columnsize = ui_realloc(ctx, model->columnsize, model->alloc * sizeof(int));
+ }
+ model->types[model->columns] = type;
+ model->titles[model->columns] = ui_strdup(ctx, title);
+ model->columnsize[model->columns] = width;
+ model->columns++;
+}
+
+UiModel* ui_model_copy(UiContext *ctx, UiModel* model) {
+ const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
+
+ UiModel* newmodel = cxMalloc(a, sizeof(UiModel));
+ *newmodel = *model;
+
+ newmodel->types = cxCalloc(a, model->columns, sizeof(UiModelType));
+ memcpy(newmodel->types, model->types, model->columns);
+
+ newmodel->titles = cxCalloc(a, model->columns, sizeof(char*));
+ for (int i = 0; i < model->columns; i++) {
+ newmodel->titles[i] = model->titles[i] ? cx_strdup_a(a, cx_str(model->titles[i])).ptr : NULL;
+ }
+ newmodel->columnsize = cxCalloc(a, model->columns, sizeof(int));
+ memcpy(newmodel->columnsize, model->columnsize, model->columns*sizeof(int));
+
+ return newmodel;
+}
+
+void ui_model_free(UiContext *ctx, UiModel *mi) {
+ const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
+ for(int i=0;i<mi->columns;i++) {
+ ui_free(ctx, mi->titles[i]);
+ }
+ cxFree(a, mi->types);
+ cxFree(a, mi->titles);
+ cxFree(a, mi->columnsize);
+ cxFree(a, mi);
+}
+
+// types
+
+// public functions
+UiInteger* ui_int_new(UiContext *ctx, const char *name) {
+ UiInteger *i = ui_malloc(ctx, sizeof(UiInteger));
+ memset(i, 0, sizeof(UiInteger));
+ if(name) {
+ uic_reg_var(ctx, name, UI_VAR_INTEGER, i);
+ }
+ return i;
+}
+
+UiDouble* ui_double_new(UiContext *ctx, const char *name) {
+ UiDouble *d = ui_malloc(ctx, sizeof(UiDouble));
+ memset(d, 0, sizeof(UiDouble));
+ if(name) {
+ uic_reg_var(ctx, name, UI_VAR_DOUBLE, d);
+ }
+ return d;
+}
+
+static void string_destroy(UiString *s) {
+ if(s->value.free && s->value.ptr) {
+ s->value.free(s->value.ptr);
+ }
+}
+
+UiString* ui_string_new(UiContext *ctx, const char *name) {
+ UiString *s = ui_malloc(ctx, sizeof(UiString));
+ memset(s, 0, sizeof(UiString));
+ ui_set_destructor(s, (ui_destructor_func)string_destroy);
+ if(name) {
+ uic_reg_var(ctx, name, UI_VAR_STRING, s);
+ }
+ return s;
+}
+
+static void text_destroy(UiText *t) {
+ if(t->destroy) {
+ t->destroy(t);
+ }
+}
+
+UiText* ui_text_new(UiContext *ctx, const char *name) {
+ UiText *t = ui_malloc(ctx, sizeof(UiText));
+ memset(t, 0, sizeof(UiText));
+ ui_set_destructor(t, (ui_destructor_func)text_destroy);
+ if(name) {
+ uic_reg_var(ctx, name, UI_VAR_TEXT, t);
+ }
+ return t;
+}
+
+UiRange* ui_range_new(UiContext *ctx, const char *name) {
+ UiRange *r = ui_malloc(ctx, sizeof(UiRange));
+ memset(r, 0, sizeof(UiRange));
+ if(name) {
+ uic_reg_var(ctx, name, UI_VAR_RANGE, r);
+ }
+ return r;
+}
+
+static void generic_destroy(UiGeneric *g) {
+ if(g->destroy) {
+ g->destroy(g);
+ }
+}
+
+UiGeneric* ui_generic_new(UiContext *ctx, const char *name) {
+ UiGeneric *g = ui_malloc(ctx, sizeof(UiGeneric));
+ memset(g, 0, sizeof(UiGeneric));
+ ui_set_destructor(g, (ui_destructor_func)generic_destroy);
+ if(name) {
+ uic_reg_var(ctx, name, UI_VAR_GENERIC, g);
+ }
+ return g;
+}
+
+
+void ui_int_set(UiInteger* i, int64_t value) {
+ if (i) {
+ if (i->set) {
+ ui_setop_enable(TRUE);
+ i->set(i, value);
+ ui_setop_enable(FALSE);
+ } else {
+ i->value = value;
+ }
+ }
+}
+
+int64_t ui_int_get(UiInteger* i) {
+ if (i) {
+ return i->get ? i->get(i) : i->value;
+ } else {
+ return 0;
+ }
+}
+
+void ui_double_set(UiDouble* d, double value) {
+ if (d) {
+ if (d->set) {
+ ui_setop_enable(TRUE);
+ d->set(d, value);
+ ui_setop_enable(FALSE);
+ } else {
+ d->value = value;
+ }
+ }
+}
+
+double ui_double_get(UiDouble* d) {
+ if (d) {
+ return d->get ? d->get(d) : d->value;
+ }
+ else {
+ return 0;
+ }
+}
+
+void ui_string_set(UiString* s, const char* value) {
+ if (s) {
+ if (s->set) {
+ ui_setop_enable(TRUE);
+ s->set(s, value);
+ ui_setop_enable(FALSE);
+ } else {
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ if (value) {
+ s->value.ptr = strdup(value);
+ s->value.free = free;
+ } else {
+ s->value.ptr = NULL;
+ s->value.free = NULL;
+ }
+ }
+ }
+}
+
+char* ui_string_get(UiString* s) {
+ if (s) {
+ return s->get ? s->get(s) : s->value.ptr;
+ }
+ else {
+ return NULL;
+ }
+}
+
+void ui_text_set(UiText* s, const char* value) {
+ if (s) {
+ if (s->set) {
+ ui_setop_enable(TRUE);
+ s->set(s, value);
+ ui_setop_enable(FALSE);
+ } else {
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ if (value) {
+ s->value.ptr = strdup(value);
+ s->value.free = free;
+ } else {
+ s->value.ptr = NULL;
+ s->value.free = NULL;
+ }
+ }
+ }
+}
+
+char* ui_text_get(UiText* s) {
+ if (s) {
+ return s->get ? s->get(s) : s->value.ptr;
+ }
+ else {
+ return NULL;
+ }
+}
+
+void ui_range_set(UiRange *r, double value) {
+ if (r) {
+ if (r->set) {
+ r->set(r, value);
+ } else {
+ r->value = value;
+ }
+ }
+}
+
+void ui_range_set_range(UiRange *r, double min, double max) {
+ if (r) {
+ if (r->setrange) {
+ r->setrange(r, min, max);
+ } else {
+ r->min = min;
+ r->max = max;
+ }
+ }
+}
+
+void ui_range_set_extent(UiRange *r, double extent) {
+ if (r) {
+ if (r->setextent) {
+ r->setextent(r, extent);
+ } else {
+ r->extent = extent;
+ }
+ }
+}
+
+double ui_range_get(UiRange *r) {
+ if (r) {
+ return r->get ? r->get(r) : r->value;
+ } else {
+ return 0;
+ }
+}
+
+double ui_range_get_min(UiRange *r) {
+ if (r) {
+ return r->min;
+ } else {
+ return 0;
+ }
+}
+
+double ui_range_get_max(UiRange *r) {
+ if (r) {
+ return r->max;
+ } else {
+ return 0;
+ }
+}
+
+double ui_range_get_extent(UiRange *r) {
+ if (r) {
+ return r->extent;
+ } else {
+ return 0;
+ }
+}
+
+void ui_generic_set_image(UiGeneric *g, void *img) {
+ if(g->set) {
+ g->set(g, img, UI_IMAGE_OBJECT_TYPE);
+ } else {
+ if(g->value) {
+ ui_image_unref(g->value);
+ }
+ ui_image_ref(img);
+ g->value = img;
+ g->type = UI_IMAGE_OBJECT_TYPE;
+ }
+}
+
+void* ui_generic_get_image(UiGeneric *g) {
+ if(g->type) {
+ if(!strcmp(g->type, UI_IMAGE_OBJECT_TYPE)) {
+ return g->value;
+ } else {
+ return NULL;
+ }
+ }
+ return g->value;
+}
+
+
+// private functions
+void uic_int_copy(UiInteger *from, UiInteger *to) {
+ to->get = from->get;
+ to->set = from->set;
+ to->obj = from->obj;
+}
+
+void uic_double_copy(UiDouble *from, UiDouble *to) {
+ to->get = from->get;
+ to->set = from->set;
+ to->obj = from->obj;
+}
+
+void uic_string_copy(UiString *from, UiString *to) {
+ to->get = from->get;
+ to->set = from->set;
+ to->obj = from->obj;
+}
+
+void uic_text_copy(UiText *from, UiText *to) {
+ to->get = from->get;
+ to->set = from->set;
+ to->getsubstr = from->getsubstr;
+ to->insert = from->insert;
+ to->setposition = from->setposition;
+ to->position = from->position;
+ to->selection = from->selection;
+ to->length = from->length;
+ to->remove = from->remove;
+ to->restore = from->restore;
+ to->save = from->save;
+ to->destroy = from->destroy;
+
+ to->obj = from->obj;
+ // do not copy the undo manager, data1, data2
+}
+
+void uic_range_copy(UiRange *from, UiRange *to) {
+ to->get = from->get;
+ to->set = from->set;
+ to->setrange = from->setrange;
+ to->setextent = from->setextent;
+ to->obj = from->obj;
+}
+
+void uic_list_copy(UiList *from, UiList *to) {
+ to->update = from->update;
+ to->getselection = from->getselection;
+ to->setselection = from->setselection;
+ to->obj = from->obj;
+}
+
+void uic_generic_copy(UiGeneric *from, UiGeneric *to) {
+ to->get = from->get;
+ to->get_type = from->get_type;
+ to->set = from->set;
+ to->obj = from->obj;
+}
+
+void uic_int_save(UiInteger *i) {
+ if(!i->obj) return;
+ i->value = i->get(i);
+}
+
+void uic_double_save(UiDouble *d) {
+ if(!d->obj) return;
+ d->value = d->get(d);
+}
+
+void uic_string_save(UiString *s) {
+ if(!s->obj) return;
+ s->get(s);
+}
+
+void uic_text_save(UiText *t) {
+ if(!t->obj) return;
+ t->save(t);
+ t->position(t);
+}
+
+void uic_range_save(UiRange *r) {
+ if(!r->obj) return;
+ r->get(r);
+}
+
+void uic_generic_save(UiGeneric *g) {
+ if(!g->obj) return;
+ g->get(g);
+}
+
+
+void uic_int_unbind(UiInteger *i) {
+ i->get = NULL;
+ i->set = NULL;
+ i->obj = NULL;
+}
+
+void uic_double_unbind(UiDouble *d) {
+ d->get = NULL;
+ d->set = NULL;
+ d->obj = NULL;
+}
+
+void uic_string_unbind(UiString *s) {
+ s->get = NULL;
+ s->set = NULL;
+ s->obj = NULL;
+}
+
+void uic_text_unbind(UiText *t) {
+ t->set = NULL;
+ t->get = NULL;
+ t->getsubstr = NULL;
+ t->insert = NULL;
+ t->setposition = NULL;
+ t->position = NULL;
+ t->selection = NULL;
+ t->length = NULL;
+ t->remove = NULL;
+ t->obj = NULL;
+}
+
+void uic_range_unbind(UiRange *r) {
+ r->get = NULL;
+ r->set = NULL;
+ r->setextent = NULL;
+ r->setrange = NULL;
+ r->obj = NULL;
+}
+
+void uic_list_unbind(UiList *l) {
+ l->update = NULL;
+ l->getselection = NULL;
+ l->setselection = NULL;
+ l->obj = NULL;
+}
+
+void uic_generic_unbind(UiGeneric *g) {
+ g->get = NULL;
+ g->get_type = NULL;
+ g->set = NULL;
+ g->obj = NULL;
+}
+
+
+UIEXPORT UiListSelection ui_list_getselection(UiList *list) {
+ if (list->getselection) {
+ return list->getselection(list);
+ }
+ return (UiListSelection){ 0, NULL };
+}
+
+UIEXPORT void ui_list_setselection(UiList *list, int index) {
+ if (list->setselection) {
+ UiListSelection sel = { 0, NULL };
+ if(index >= 0) {
+ sel.count = 1;
+ sel.rows = &index;
+ }
+ list->setselection(list, sel);
+ }
+}
+
+UIEXPORT void ui_listselection_free(UiListSelection selection) {
+ if (selection.rows) {
+ free(selection.rows);
+ }
+}
+
+UIEXPORT UiStr ui_str(char *cstr) {
+ return (UiStr) { cstr, NULL };
+}
+
+UIEXPORT UiStr ui_str_free(char *str, void (*freefunc)(void *v)) {
+ return (UiStr) { str, freefunc };
+}
+
+
+UIEXPORT UiFileList ui_filelist_copy(UiFileList list) {
+ char **newlist = calloc(sizeof(char*), list.nfiles);
+ for (int i = 0; i < list.nfiles; i++) {
+ newlist[i] = strdup(list.files[i]);
+ }
+ return (UiFileList) { newlist, list.nfiles };
+}
+
+UIEXPORT void ui_filelist_free(UiFileList list) {
+ for (int i = 0; i < list.nfiles; i++) {
+ free(list.files[i]);
+ }
+ free(list.files);
+}
+
+
+typedef struct UiObserverDestructor {
+ UiList *list;
+ UiObserver *observer;
+} UiObserverDestructor;
+
+static void observer_destructor(UiObserverDestructor *destr) {
+ UiObserver *remove_obs = destr->observer;
+ UiObserver *obs = destr->list->observers;
+ UiObserver *prev = NULL;
+ while(obs) {
+ if(obs == remove_obs) {
+ if(prev) {
+ prev->next = obs->next;
+ } else {
+ destr->list->observers = obs->next;
+ }
+ break;
+ }
+ prev = obs;
+ obs = obs->next;
+ }
+ free(remove_obs);
+}
+
+void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObserver *observer) {
+ CxMempool *mp = ctx->mp;
+ UiObserverDestructor *destr = cxMalloc(mp->allocator, sizeof(UiObserverDestructor));
+ destr->list = list;
+ destr->observer = observer;
+ cxMempoolSetDestructor(destr, (cx_destructor_func)observer_destructor);
+}
+
+static int ui_set_op = 0;
+
+void ui_setop_enable(int set) {
+ ui_set_op = set;
+}
+
+int ui_get_setop(void) {
+ return ui_set_op;
+}
+
+/* ---------------- List initializers and wrapper functions ---------------- */
+
+void ui_global_list_initializer(ui_list_init_func func, void *userdata) {
+ default_list_init = func;
+ default_list_init_userdata = userdata;
+}
+
+void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)) {
+ list->first = first;
+}
+
+void ui_list_class_set_next(UiList *list, void*(*next)(UiList *list)) {
+ list->next = next;
+}
+
+void ui_list_class_set_get(UiList *list, void*(*get)(UiList *list, int i)) {
+ list->get = get;
+}
+
+void ui_list_class_set_count(UiList *list, int(*count)(UiList *list)) {
+ list->count = count;
+}
+
+void ui_list_class_set_data(UiList *list, void *data) {
+ list->data = data;
+}
+
+void ui_list_class_set_iter(UiList *list, void *iter) {
+ list->iter = iter;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_TYPES_H
+#define UIC_TYPES_H
+
+#include "../ui/toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused);
+
+void uic_int_copy(UiInteger *from, UiInteger *to);
+void uic_double_copy(UiDouble *from, UiDouble *to);
+void uic_string_copy(UiString *from, UiString *to);
+void uic_text_copy(UiText *from, UiText *to);
+void uic_range_copy(UiRange *from, UiRange *to);
+void uic_list_copy(UiList *from, UiList *to);
+void uic_generic_copy(UiGeneric *from, UiGeneric *to);
+
+void uic_int_save(UiInteger *i);
+void uic_double_save(UiDouble *d);
+void uic_string_save(UiString *s);
+void uic_text_save(UiText *t);
+void uic_range_save(UiRange *r);
+void uic_generic_save(UiGeneric *g);
+
+void uic_int_unbind(UiInteger *i);
+void uic_double_unbind(UiDouble *d);
+void uic_string_unbind(UiString *s);
+void uic_text_unbind(UiText *t);
+void uic_range_unbind(UiRange *r);
+void uic_list_unbind(UiList *l);
+void uic_generic_unbind(UiGeneric *g);
+
+void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObserver *observer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_TYPES_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx_properties.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+UcxProperties *ucx_properties_new() {
+ UcxProperties *parser = (UcxProperties*)malloc(
+ sizeof(UcxProperties));
+ if(!parser) {
+ return NULL;
+ }
+
+ parser->buffer = NULL;
+ parser->buflen = 0;
+ parser->pos = 0;
+ parser->tmp = NULL;
+ parser->tmplen = 0;
+ parser->tmpcap = 0;
+ parser->error = 0;
+ parser->delimiter = '=';
+ parser->comment1 = '#';
+ parser->comment2 = 0;
+ parser->comment3 = 0;
+
+ return parser;
+}
+
+void ucx_properties_free(UcxProperties *parser) {
+ if(parser->tmp) {
+ free(parser->tmp);
+ }
+ free(parser);
+}
+
+void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) {
+ parser->buffer = buf;
+ parser->buflen = len;
+ parser->pos = 0;
+}
+
+static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) {
+ if(parser->tmpcap - parser->tmplen < len) {
+ size_t newcap = parser->tmpcap + len + 64;
+ parser->tmp = (char*)realloc(parser->tmp, newcap);
+ parser->tmpcap = newcap;
+ }
+ memcpy(parser->tmp + parser->tmplen, buf, len);
+ parser->tmplen += len;
+}
+
+int ucx_properties_next(UcxProperties *parser, cxstring *name, cxstring *value) {
+ if(parser->tmplen > 0) {
+ char *buf = parser->buffer + parser->pos;
+ size_t len = parser->buflen - parser->pos;
+ cxstring str = cx_strn(buf, len);
+ cxstring nl = cx_strchr(str, '\n');
+ if(nl.ptr) {
+ size_t newlen = (size_t)(nl.ptr - buf) + 1;
+ parser_tmp_append(parser, buf, newlen);
+ // the tmp buffer contains exactly one line now
+
+ char *orig_buf = parser->buffer;
+ size_t orig_len = parser->buflen;
+
+ parser->buffer = parser->tmp;
+ parser->buflen = parser->tmplen;
+ parser->pos = 0;
+ parser->tmp = NULL;
+ parser->tmpcap = 0;
+ parser->tmplen = 0;
+ // run ucx_properties_next with the tmp buffer as main buffer
+ int ret = ucx_properties_next(parser, name, value);
+
+ // restore original buffer
+ parser->tmp = parser->buffer;
+ parser->buffer = orig_buf;
+ parser->buflen = orig_len;
+ parser->pos = newlen;
+
+ /*
+ * if ret == 0 the tmp buffer contained just space or a comment
+ * we parse again with the original buffer to get a name/value
+ * or a new tmp buffer
+ */
+ return ret ? ret : ucx_properties_next(parser, name, value);
+ } else {
+ parser_tmp_append(parser, buf, len);
+ return 0;
+ }
+ } else if(parser->tmp) {
+ free(parser->tmp);
+ parser->tmp = NULL;
+ }
+
+ char comment1 = parser->comment1;
+ char comment2 = parser->comment2;
+ char comment3 = parser->comment3;
+ char delimiter = parser->delimiter;
+
+ // get one line and parse it
+ while(parser->pos < parser->buflen) {
+ char *buf = parser->buffer + parser->pos;
+ size_t len = parser->buflen - parser->pos;
+
+ /*
+ * First we check if we have at least one line. We also get indices of
+ * delimiter and comment chars
+ */
+ size_t delimiter_index = 0;
+ size_t comment_index = 0;
+ int has_comment = 0;
+
+ size_t i = 0;
+ char c = 0;
+ for(;i<len;i++) {
+ c = buf[i];
+ if(c == comment1 || c == comment2 || c == comment3) {
+ if(comment_index == 0) {
+ comment_index = i;
+ has_comment = 1;
+ }
+ } else if(c == delimiter) {
+ if(delimiter_index == 0 && !has_comment) {
+ delimiter_index = i;
+ }
+ } else if(c == '\n') {
+ break;
+ }
+ }
+
+ if(c != '\n') {
+ // we don't have enough data for a line
+ // store remaining bytes in temporary buffer for next round
+ parser->tmpcap = len + 128;
+ parser->tmp = (char*)malloc(parser->tmpcap);
+ parser->tmplen = len;
+ memcpy(parser->tmp, buf, len);
+ return 0;
+ }
+
+ cxstring line = has_comment ? cx_strn(buf, comment_index) : cx_strn(buf, i);
+ // check line
+ if(delimiter_index == 0) {
+ line = cx_strtrim(line);
+ if(line.length != 0) {
+ parser->error = 1;
+ }
+ } else {
+ cxstring n = cx_strn(buf, delimiter_index);
+ cxstring v = cx_strn(
+ buf + delimiter_index + 1,
+ line.length - delimiter_index - 1);
+ n = cx_strtrim(n);
+ v = cx_strtrim(v);
+ if(n.length != 0 || v.length != 0) {
+ *name = n;
+ *value = v;
+ parser->pos += i + 1;
+ return 1;
+ } else {
+ parser->error = 1;
+ }
+ }
+
+ parser->pos += i + 1;
+ }
+
+ return 0;
+}
+
+int ucx_properties2map(UcxProperties *parser, CxMap *map) {
+ cxstring name;
+ cxstring value;
+ while(ucx_properties_next(parser, &name, &value)) {
+ cxmutstr mutvalue = cx_strdup_a(map->collection.allocator, value);
+ if(!mutvalue.ptr) {
+ return 1;
+ }
+ if(cxMapPut(map, cx_hash_key_cxstr(name), mutvalue.ptr)) {
+ cxFree(map->collection.allocator, mutvalue.ptr);
+ return 1;
+ }
+ }
+ if (parser->error) {
+ return parser->error;
+ } else {
+ return 0;
+ }
+}
+
+// buffer size is documented - change doc, when you change bufsize!
+#define UCX_PROPLOAD_BUFSIZE 1024
+int ucx_properties_load(CxMap *map, FILE *file) {
+ UcxProperties *parser = ucx_properties_new();
+ if(!(parser && map && file)) {
+ return 1;
+ }
+
+ int error = 0;
+ size_t r;
+ char buf[UCX_PROPLOAD_BUFSIZE];
+ while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) {
+ ucx_properties_fill(parser, buf, r);
+ error = ucx_properties2map(parser, map);
+ if (error) {
+ break;
+ }
+ }
+ ucx_properties_free(parser);
+ return error;
+}
+
+int ucx_properties_store(CxMap *map, FILE *file) {
+ CxMapIterator iter = cxMapIterator(map);
+ cxstring value;
+ size_t written;
+
+ cx_foreach(CxMapEntry *, v, iter) {
+ value = cx_str(v->value);
+
+ written = 0;
+ written += fwrite(v->key->data, 1, v->key->len, file);
+ written += fwrite(" = ", 1, 3, file);
+ written += fwrite(value.ptr, 1, value.length, file);
+ written += fwrite("\n", 1, 1, file);
+
+ if (written != v->key->len + value.length + 4) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file properties.h
+ *
+ * Load / store utilities for properties files.
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_PROPERTIES_H
+#define UCX_PROPERTIES_H
+
+#include <cx/hash_map.h>
+#include <cx/string.h>
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * UcxProperties object for parsing properties data.
+ * Most of the fields are for internal use only. You may configure the
+ * properties parser, e.g. by changing the used delimiter or specifying
+ * up to three different characters that shall introduce comments.
+ */
+typedef struct {
+ /**
+ * Input buffer (don't set manually).
+ * Automatically set by calls to ucx_properties_fill().
+ */
+ char *buffer;
+
+ /**
+ * Length of the input buffer (don't set manually).
+ * Automatically set by calls to ucx_properties_fill().
+ */
+ size_t buflen;
+
+ /**
+ * Current buffer position (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ size_t pos;
+
+ /**
+ * Internal temporary buffer (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ char *tmp;
+
+ /**
+ * Internal temporary buffer length (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ size_t tmplen;
+
+ /**
+ * Internal temporary buffer capacity (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ size_t tmpcap;
+
+ /**
+ * Parser error code.
+ * This is always 0 on success and a nonzero value on syntax errors.
+ * The value is set by ucx_properties_next().
+ */
+ int error;
+
+ /**
+ * The delimiter that shall be used.
+ * This is '=' by default.
+ */
+ char delimiter;
+
+ /**
+ * The first comment character.
+ * This is '#' by default.
+ */
+ char comment1;
+
+ /**
+ * The second comment character.
+ * This is not set by default.
+ */
+ char comment2;
+
+ /**
+ * The third comment character.
+ * This is not set by default.
+ */
+ char comment3;
+} UcxProperties;
+
+
+/**
+ * Constructs a new UcxProperties object.
+ * @return a pointer to the new UcxProperties object
+ */
+UcxProperties *ucx_properties_new();
+
+/**
+ * Destroys a UcxProperties object.
+ * @param prop the UcxProperties object to destroy
+ */
+void ucx_properties_free(UcxProperties *prop);
+
+/**
+ * Sets the input buffer for the properties parser.
+ *
+ * After calling this function, you may parse the data by calling
+ * ucx_properties_next() until it returns 0. The function ucx_properties2map()
+ * is a convenience function that reads as much data as possible by using this
+ * function.
+ *
+ *
+ * @param prop the UcxProperties object
+ * @param buf a pointer to the new buffer
+ * @param len the payload length of the buffer
+ * @see ucx_properties_next()
+ * @see ucx_properties2map()
+ */
+void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len);
+
+/**
+ * Retrieves the next key/value-pair.
+ *
+ * This function returns a nonzero value as long as there are key/value-pairs
+ * found. If no more key/value-pairs are found, you may refill the input buffer
+ * with ucx_properties_fill().
+ *
+ * <b>Attention:</b> the cxstring.ptr pointers of the output parameters point to
+ * memory within the input buffer of the parser and will get invalid some time.
+ * If you want long term copies of the key/value-pairs, use sstrdup() after
+ * calling this function.
+ *
+ * @param prop the UcxProperties object
+ * @param name a pointer to the cxstring that shall contain the property name
+ * @param value a pointer to the cxstring that shall contain the property value
+ * @return Nonzero, if a key/value-pair was successfully retrieved
+ * @see ucx_properties_fill()
+ */
+int ucx_properties_next(UcxProperties *prop, cxstring *name, cxstring *value);
+
+/**
+ * Retrieves all available key/value-pairs and puts them into a UcxMap.
+ *
+ * This is done by successive calls to ucx_properties_next() until no more
+ * key/value-pairs can be retrieved.
+ *
+ * The memory for the map values is allocated by the map's own allocator.
+ *
+ * @param prop the UcxProperties object
+ * @param map the target map
+ * @return The UcxProperties.error code (i.e. 0 on success).
+ * @see ucx_properties_fill()
+ * @see UcxMap.allocator
+ */
+int ucx_properties2map(UcxProperties *prop, CxMap *map);
+
+/**
+ * Loads a properties file to a UcxMap.
+ *
+ * This is a convenience function that reads data from an input
+ * stream until the end of the stream is reached.
+ *
+ * @param map the map object to write the key/value-pairs to
+ * @param file the <code>FILE*</code> stream to read from
+ * @return 0 on success, or a non-zero value on error
+ *
+ * @see ucx_properties_fill()
+ * @see ucx_properties2map()
+ */
+int ucx_properties_load(CxMap *map, FILE *file);
+
+/**
+ * Stores a UcxMap to a file.
+ *
+ * The key/value-pairs are written by using the following format:
+ *
+ * <code>[key] = [value]\\n</code>
+ *
+ * @param map the map to store
+ * @param file the <code>FILE*</code> stream to write to
+ * @return 0 on success, or a non-zero value on error
+ */
+int ucx_properties_store(CxMap *map, FILE *file);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_PROPERTIES_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "wrapper.h"
+#include "types.h"
+#include <cx/list.h>
+#include <string.h>
+
+/* ---------------------------- UiObject ---------------------------- */
+
+UiContext* ui_object_get_context(UiObject *obj) {
+ return obj->ctx;
+}
+
+void* ui_object_get_windowdata(UiObject *obj) {
+ return obj->window;
+}
+
+void ui_object_set_windowdata(UiObject *obj, void *windowdata) {
+ obj->window = windowdata;
+}
+
+
+/* ---------------------------- UiList ---------------------------- */
+
+void* ui_list_get_data(UiList *list) {
+ return list->data;
+}
+
+void ui_list_set_data(UiList *list, void *data) {
+ list->data = data;
+}
+
+void* ui_list_get_iter(UiList *list) {
+ return list->iter;
+}
+
+void ui_list_set_iter(UiList *list, void *iter) {
+ list->iter = iter;
+}
+
+
+/* ------------------------------ UiSublist ------------------------------ */
+
+UiSubList* ui_sublist_new(void) {
+ UiSubList *sublist = malloc(sizeof(UiSubList));
+ memset(sublist, 0, sizeof(UiSubList));
+ return sublist;
+}
+
+void ui_sublist_set_value(UiSubList *sublist, UiList *value) {
+ sublist->value = value;
+}
+
+void ui_sublist_set_varname(UiSubList *sublist, const char *varname) {
+ free((void*)sublist->varname);
+ sublist->varname = strdup(varname);
+}
+
+void ui_sublist_set_header(UiSubList *sublist, const char *header) {
+ free((void*)sublist->header);
+ sublist->header = strdup(header);
+}
+
+void ui_sublist_set_separator(UiSubList *sublist, UiBool separator) {
+ sublist->separator = separator;
+}
+
+void ui_sublist_set_userdata(UiSubList *sublist, void *userdata) {
+ sublist->userdata = userdata;
+}
+
+void ui_sublist_free(UiSubList *sublist) {
+ free((void*)sublist->varname);
+ free((void*)sublist->header);
+ free(sublist);
+}
+
+
+/* -------------------- Source list (UiList<UiSublist>) -------------------- */
+
+UiList* ui_srclist_new(UiContext *ctx, const char *name) {
+ UiList *list = ui_list_new2(ctx, name, uic_ucx_list_init, NULL);
+ CxList *cxlist = list->data;
+ cxlist->collection.simple_destructor = (cx_destructor_func)ui_sublist_free;
+ return list;
+}
+
+void ui_srclist_add(UiList *list, UiSubList *item) {
+ ui_list_append(list, item);
+}
+
+void ui_srclist_insert(UiList *list, int index, UiSubList *item) {
+ CxList *cxlist = list->data;
+ cxListInsert(cxlist, index, item);
+}
+
+void ui_srclist_remove(UiList *list, int index) {
+ CxList *cxlist = list->data;
+ cxListRemove(cxlist, index);
+}
+
+void ui_srclist_clear(UiList *list) {
+ CxList *cxlist = list->data;
+ cxListClear(cxlist);
+}
+
+int ui_srclist_size(UiList *list) {
+ return ui_list_count(list);
+}
+
+/*
+ * numerates all sublists and sets the sublist index as userdata
+ */
+void ui_srclist_generate_sublist_num_data(UiList *list) {
+ CxList *cxlist = list->data;
+ CxIterator i = cxListIterator(cxlist);
+ cx_foreach(UiSubList *, sublist, i) {
+ sublist->userdata = (void*)i.index;
+ }
+}
+
+
+/* ---------------------------- UiSubListEventData ---------------------------- */
+
+UiList* ui_sublist_event_get_list(UiSubListEventData *event) {
+ return event->list;
+}
+
+int ui_sublist_event_get_sublist_index(UiSubListEventData *event) {
+ return event->sublist_index;
+}
+
+int ui_sublist_event_get_row_index(UiSubListEventData *event) {
+ return event->row_index;
+}
+
+void* ui_sublist_event_get_row_data(UiSubListEventData *event) {
+ return event->row_data;
+}
+
+void* ui_sublist_event_get_sublist_userdata(UiSubListEventData *event) {
+ return event->sublist_userdata;
+}
+
+void* ui_sublist_event_get_event_data(UiSubListEventData *event) {
+ return event->event_data;
+}
+
+
+/* ---------------------------- UiEvent ---------------------------- */
+
+UiObject* ui_event_get_obj(UiEvent *event) {
+ return event->obj;
+}
+
+void* ui_event_get_document(UiEvent *event) {
+ return event->document;
+}
+
+void* ui_event_get_windowdata(UiEvent *event) {
+ return event->window;
+}
+
+void* ui_event_get_eventdata(UiEvent *event) {
+ return event->eventdata;
+}
+
+int ui_event_get_eventdatatype(UiEvent *event) {
+ return event->eventdatatype;
+}
+
+int ui_event_get_int(UiEvent *event) {
+ return event->intval;
+}
+
+int ui_event_get_set(UiEvent *event) {
+ return event->set;
+}
+
+
+/* ------------------------- SubListItem (public) ------------------------- */
+
+void ui_sublist_item_set_icon(UiSubListItem *item, const char *icon) {
+ item->icon = icon ? strdup(icon) : NULL;
+}
+
+void ui_sublist_item_set_label(UiSubListItem *item, const char *label) {
+ item->label = label ? strdup(label) : NULL;
+}
+
+void ui_sublist_item_set_button_icon(UiSubListItem *item, const char *button_icon) {
+ item->button_icon = button_icon ? strdup(button_icon) : NULL;
+}
+
+void ui_sublist_item_set_button_label(UiSubListItem *item, const char *button_label) {
+ item->button_label = button_label ? strdup(button_label) : NULL;
+}
+
+void ui_sublist_item_set_button_menu(UiSubListItem *item, UiMenuBuilder *menu) {
+ item->button_menu = menu;
+}
+
+void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge) {
+ item->badge = badge ? strdup(badge) : NULL;
+}
+
+void ui_sublist_item_set_eventdata(UiSubListItem *item, void *eventdata) {
+ item->eventdata = NULL;
+}
+
+/* ---------------------------- UiListSelection ---------------------------- */
+
+UiListSelection* ui_list_get_selection_allocated(UiList *list) {
+ UiListSelection *sel = malloc(sizeof(UiListSelection));
+ *sel = ui_list_get_selection(list);
+ return sel;
+
+}
+
+int ui_list_selection_get_count(UiListSelection *sel) {
+ return sel->count;
+}
+
+int* ui_list_selection_get_rows(UiListSelection *sel) {
+ return sel->rows;
+}
+
+UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num) {
+ UiListSelection sel;
+ sel.rows = indices;
+ sel.count = num;
+ if(list->setselection) {
+ list->setselection(list, sel);
+ }
+}
+
+void ui_list_selection_free(UiListSelection *sel) {
+ ui_listselection_free(*sel);
+ free(sel);
+}
+
+/* ---------------------------- UiFileList ---------------------------- */
+
+int ui_filelist_count(UiFileList *flist) {
+ return flist->nfiles;
+}
+
+char* ui_filelist_get(UiFileList *flist, int index) {
+ if(index >= 0 && index < flist->nfiles) {
+ return flist->files[index];
+ }
+ return NULL;
+}
+
+/* ---------------------------- UiTextStyle ---------------------------- */
+
+void ui_textstyle_set_bold(UiTextStyle *style, UiBool set) {
+ if(set) {
+ style->text_style |= UI_TEXT_STYLE_BOLD;
+ } else {
+ style->text_style &= ~UI_TEXT_STYLE_BOLD;
+ }
+}
+
+void ui_textstyle_set_underline(UiTextStyle *style, UiBool set) {
+ if(set) {
+ style->text_style |= UI_TEXT_STYLE_UNDERLINE;
+ } else {
+ style->text_style &= ~UI_TEXT_STYLE_UNDERLINE;
+ }
+}
+
+void ui_textstyle_set_italic(UiTextStyle *style, UiBool set) {
+ if(set) {
+ style->text_style |= UI_TEXT_STYLE_ITALIC;
+ } else {
+ style->text_style &= ~UI_TEXT_STYLE_ITALIC;
+ }
+}
+
+void ui_textstyle_set_color(UiTextStyle *style, int r, int g, int b) {
+ style->fg_set = TRUE;
+ style->fg.red = r;
+ style->fg.green = g;
+ style->fg.blue = b;
+}
+
+void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable) {
+ style->fg_set = enable;
+}
+
+
+/* ---------------------------- UiCellValue ---------------------------- */
+
+UiBool ui_cell_value_is_string(UiCellValue *value) {
+ return value->type == UI_STRING_EDITABLE;
+}
+
+UiBool ui_cell_value_is_int(UiCellValue *value) {
+ return FALSE; // TODO
+}
+
+const char* ui_cell_value_get_string(UiCellValue *value) {
+ return value->string;
+}
+
+int64_t ui_cell_value_get_int(UiCellValue *value) {
+ return value->i;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_WRAPPER_H
+#define UIC_WRAPPER_H
+
+#include "../ui/toolkit.h"
+#include "../ui/tree.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+UIEXPORT UiContext* ui_object_get_context(UiObject *obj);
+UIEXPORT void* ui_object_get_windowdata(UiObject *obj);
+UIEXPORT void ui_object_set_windowdata(UiObject *obj, void *windowdata);
+
+UIEXPORT void* ui_list_get_data(UiList *list);
+UIEXPORT void* ui_list_get_iter(UiList *list);
+UIEXPORT void ui_list_set_iter(UiList *list, void *iter);
+
+UIEXPORT UiSubList* ui_sublist_new(void);
+UIEXPORT void ui_sublist_set_value(UiSubList *sublist, UiList *value);
+UIEXPORT void ui_sublist_set_varname(UiSubList *sublist, const char *varname);
+UIEXPORT void ui_sublist_set_header(UiSubList *sublist, const char *header);
+UIEXPORT void ui_sublist_set_separator(UiSubList *sublist, UiBool separator);
+UIEXPORT void ui_sublist_set_userdata(UiSubList *sublist, void *userdata);
+UIEXPORT void ui_sublist_free(UiSubList *sublist);
+
+UIEXPORT UiList* ui_srclist_new(UiContext *ctx, const char *name);
+UIEXPORT void ui_srclist_add(UiList *list, UiSubList *item);
+UIEXPORT void ui_srclist_insert(UiList *list, int index, UiSubList *item);
+UIEXPORT void ui_srclist_remove(UiList *list, int index);
+UIEXPORT void ui_srclist_clear(UiList *list);
+UIEXPORT int ui_srclist_size(UiList *list);
+UIEXPORT void ui_srclist_generate_sublist_num_data(UiList *list);
+
+UIEXPORT UiList* ui_sublist_event_get_list(UiSubListEventData *event);
+UIEXPORT int ui_sublist_event_get_sublist_index(UiSubListEventData *event);
+UIEXPORT int ui_sublist_event_get_row_index(UiSubListEventData *event);
+UIEXPORT void* ui_sublist_event_get_row_data(UiSubListEventData *event);
+UIEXPORT void* ui_sublist_event_get_sublist_userdata(UiSubListEventData *event);
+UIEXPORT void* ui_sublist_event_get_event_data(UiSubListEventData *event);
+
+UIEXPORT UiObject* ui_event_get_obj(UiEvent *event);
+UIEXPORT void* ui_event_get_document(UiEvent *event);
+UIEXPORT void* ui_event_get_windowdata(UiEvent *event);
+UIEXPORT void* ui_event_get_eventdata(UiEvent *event);
+UIEXPORT int ui_event_get_eventdatatype(UiEvent *event);
+UIEXPORT int ui_event_get_int(UiEvent *event);
+UIEXPORT int ui_event_get_set(UiEvent *event);
+
+UIEXPORT UiListSelection* ui_list_get_selection_allocated(UiList *list);
+UIEXPORT int ui_list_selection_get_count(UiListSelection *sel);
+UIEXPORT int* ui_list_selection_get_rows(UiListSelection *sel);
+UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num);
+UIEXPORT void ui_list_selection_free(UiListSelection *sel);
+
+UIEXPORT int ui_filelist_count(UiFileList *flist);
+UIEXPORT char* ui_filelist_get(UiFileList *flist, int index);
+
+UIEXPORT void ui_textstyle_set_bold(UiTextStyle *style, UiBool set);
+UIEXPORT void ui_textstyle_set_underline(UiTextStyle *style, UiBool set);
+UIEXPORT void ui_textstyle_set_italic(UiTextStyle *style, UiBool set);
+UIEXPORT void ui_textstyle_set_color(UiTextStyle *style, int r, int g, int b);
+UIEXPORT void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable);
+
+UIEXPORT UiBool ui_cell_value_is_string(UiCellValue *value);
+UIEXPORT UiBool ui_cell_value_is_int(UiCellValue *value);
+UIEXPORT const char* ui_cell_value_get_string(UiCellValue *value);
+UIEXPORT int64_t ui_cell_value_get_int(UiCellValue *value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_WRAPPER_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+$(GTK_OBJPRE)%.o: gtk/%.c
+ $(CC) -o $@ -c -I../ucx $(CFLAGS) $(SHLIB_CFLAGS) $(TK_CFLAGS) $<
+
+$(UI_LIB): $(OBJ)
+ $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ)
+
+$(UI_SHLIB): $(OBJ)
+ $(CC) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) -L../build/lib -lucx
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "button.h"
+#include "container.h"
+#include <cx/allocator.h>
+#include <cx/buffer.h>
+#include <cx/json.h>
+#include "../common/context.h"
+#include "../common/object.h"
+
+void ui_button_set_icon_name(GtkWidget *button, const char *icon) {
+ if(!icon) {
+ return;
+ }
+
+#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
+}
+
+GtkWidget* ui_create_button(
+ UiObject *obj,
+ const char *label,
+ const char *icon,
+ const char *tooltip,
+ ui_callback onclick,
+ void *userdata,
+ int event_value,
+ bool activate_event)
+{
+ GtkWidget *button = gtk_button_new_with_label(label);
+ ui_button_set_icon_name(button, icon);
+ if(tooltip) {
+ gtk_widget_set_tooltip_text(button, tooltip);
+ }
+
+ if(onclick) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = userdata;
+ event->callback = onclick;
+ event->value = event_value;
+ event->customdata = NULL;
+ event->customint = 0;
+
+ g_signal_connect(
+ button,
+ "clicked",
+ G_CALLBACK(ui_button_clicked),
+ event);
+ g_signal_connect(
+ button,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ if(activate_event) {
+ g_signal_connect(
+ button,
+ "activate",
+ G_CALLBACK(ui_button_clicked),
+ event);
+ }
+ }
+
+ return button;
+}
+
+UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) {
+ GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->tooltip, args->onclick, args->onclickdata, 0, FALSE);
+ ui_set_name_and_style(button, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, button, args->groups);
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, button, &layout);
+ return button;
+}
+
+
+void ui_button_clicked(GtkWidget *widget, UiEventData *event) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = event->value;
+ e.set = ui_get_setop();
+ event->callback(&e, event->userdata);
+}
+
+void ui_button_set_label(UIWIDGET button, const char *label) {
+ gtk_button_set_label(GTK_BUTTON(button), label);
+}
+
+void ui_button_set_icon(UIWIDGET button, const char *icon) {
+ ui_button_set_icon_name(button, icon);
+}
+
+int64_t ui_toggle_button_get(UiInteger *integer) {
+ GtkToggleButton *button = integer->obj;
+ integer->value = (int)gtk_toggle_button_get_active(button);
+ return integer->value;
+}
+
+void ui_toggle_button_set(UiInteger *integer, int64_t value) {
+ GtkToggleButton *button = integer->obj;
+ integer->value = value;
+ gtk_toggle_button_set_active(button, value != 0 ? TRUE : FALSE);
+}
+
+void ui_toggled_obs(void *widget, UiVarEventData *event) {
+ UiInteger *i = event->var->value;
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = event->var->value;
+ e.eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ e.intval = i->get(i);
+ e.set = ui_get_setop();
+
+ ui_notify_evt(i->observers, &e);
+}
+
+static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = gtk_toggle_button_get_active(widget);
+ e.set = ui_get_setop();
+ event->callback(&e, event->userdata);
+}
+
+static void ui_togglebutton_enable_state_callback(GtkToggleButton *widget, UiEventData *event) {
+ if(gtk_toggle_button_get_active(widget)) {
+ ui_set_group(event->obj->ctx, event->value);
+ } else {
+ ui_unset_group(event->obj->ctx, event->value);
+ }
+}
+
+void ui_setup_togglebutton(
+ UiObject *obj,
+ GtkWidget *togglebutton,
+ const char *label,
+ const char *icon,
+ const char *tooltip,
+ const char *varname,
+ UiInteger *value,
+ ui_callback onchange,
+ void *onchangedata,
+ int enable_state)
+{
+ if(label) {
+ gtk_button_set_label(GTK_BUTTON(togglebutton), label);
+ }
+ ui_button_set_icon_name(togglebutton, icon);
+ if(tooltip) {
+ gtk_widget_set_tooltip_text(togglebutton, tooltip);
+ }
+
+ ui_bind_togglebutton(
+ obj,
+ togglebutton,
+ ui_toggle_button_get,
+ ui_toggle_button_set,
+ varname,
+ value,
+ (ui_toggled_func)ui_toggled_callback,
+ onchange,
+ onchangedata,
+ (ui_toggled_func)ui_togglebutton_enable_state_callback,
+ enable_state
+ );
+}
+
+void ui_bind_togglebutton(
+ UiObject *obj,
+ GtkWidget *widget,
+ int64_t (*getfunc)(UiInteger*),
+ void (*setfunc)(UiInteger*, int64_t),
+ const char *varname,
+ UiInteger *value,
+ void (*toggled_callback)(void*, void*),
+ ui_callback onchange,
+ void *onchangedata,
+ void (*enable_state_func)(void*, void*),
+ int enable_state)
+{
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
+ if (var) {
+ UiInteger* value = (UiInteger*)var->value;
+ value->obj = widget;
+ value->get = getfunc;
+ value->set = setfunc;
+
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ event->obj = obj;
+ event->var = var;
+ event->observers = NULL;
+ event->callback = NULL;
+ event->userdata = NULL;
+
+ g_signal_connect(
+ widget,
+ "toggled",
+ G_CALLBACK(ui_toggled_obs),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_vardata),
+ event);
+ }
+
+ if(onchange) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = onchangedata;
+ event->callback = onchange;
+ event->value = 0;
+ event->customdata = NULL;
+ event->customint = 0;
+
+ g_signal_connect(
+ widget,
+ "toggled",
+ G_CALLBACK(toggled_callback),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+
+ if(enable_state > 0) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = NULL;
+ event->callback = NULL;
+ event->value = enable_state;
+ event->customdata = NULL;
+ event->customint = 0;
+
+ g_signal_connect(
+ widget,
+ "toggled",
+ G_CALLBACK(enable_state_func),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+}
+
+static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs *args) {
+ ui_setup_togglebutton(
+ obj,
+ widget,
+ args->label,
+ args->icon,
+ args->tooltip,
+ args->varname,
+ args->value,
+ args->onchange,
+ args->onchangedata,
+ args->enable_group);
+ ui_set_name_and_style(widget, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, widget, args->groups);
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ return widget;
+}
+
+UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
+ return togglebutton_create(obj, gtk_toggle_button_new(), args);
+}
+
+#if GTK_MAJOR_VERSION >= 4
+
+int64_t ui_check_button_get(UiInteger *integer) {
+ GtkCheckButton *button = integer->obj;
+ integer->value = (int)gtk_check_button_get_active(button);
+ return integer->value;
+}
+
+void ui_check_button_set(UiInteger *integer, int64_t value) {
+ GtkCheckButton *button = integer->obj;
+ integer->value = value;
+ gtk_check_button_set_active(button, value != 0 ? TRUE : FALSE);
+}
+
+static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = gtk_check_button_get_active(widget);
+ e.set = ui_get_setop();
+ event->callback(&e, event->userdata);
+}
+
+static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event) {
+ if(gtk_check_button_get_active(widget)) {
+ ui_set_group(event->obj->ctx, event->value);
+ } else {
+ ui_unset_group(event->obj->ctx, event->value);
+ }
+}
+
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
+ GtkWidget *widget = gtk_check_button_new_with_label(args->label);
+ ui_bind_togglebutton(
+ obj,
+ widget,
+ ui_check_button_get,
+ ui_check_button_set,
+ args->varname,
+ args->value,
+ (ui_toggled_func)ui_checkbox_callback,
+ args->onchange,
+ args->onchangedata,
+ (ui_toggled_func)ui_checkbox_enable_state,
+ args->enable_group);
+
+ ui_set_name_and_style(widget, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, widget, args->groups);
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ return widget;
+}
+
+#else
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
+ return togglebutton_create(obj, gtk_check_button_new(), args);
+}
+#endif
+
+
+#if GTK_MAJOR_VERSION >= 3
+
+static void switch_changed(
+ GObject *gobject,
+ GParamSpec *pspec,
+ UiVarEventData *event)
+{
+ GtkSwitch *sw = GTK_SWITCH (gobject);
+ gboolean active = gtk_switch_get_active (sw);
+
+ UiEvent e;
+ e.obj = event->obj;
+ e.document = e.obj->ctx->document;
+ e.window = e.obj->window;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.set = ui_get_setop();
+
+ if(event->callback) {
+ event->callback(&e, event->userdata);
+ }
+ if(event->var) {
+ UiInteger *i = event->var->value;
+ ui_notify_evt(i->observers, &e);
+ }
+}
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
+ GtkWidget *widget = gtk_switch_new();
+ ui_set_name_and_style(widget, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, widget, args->groups);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *value = var->value;
+ value->obj = widget;
+ value->get = ui_switch_get;
+ value->set = ui_switch_set;
+
+ if(value->value) {
+ gtk_switch_set_active(GTK_SWITCH(widget), TRUE);
+ }
+
+
+ }
+
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ event->obj = obj;
+ event->callback = args->onchange;
+ event->userdata = args->onchangedata;
+ event->var = var;
+ event->observers = NULL;
+
+ g_signal_connect(
+ widget,
+ "notify::active",
+ G_CALLBACK(switch_changed),
+ event);
+
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_vardata),
+ event);
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ return widget;
+}
+
+int64_t ui_switch_get(UiInteger *value) {
+ GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj);
+ value->value = gtk_switch_get_active(sw);
+ return value->value;
+}
+
+void ui_switch_set(UiInteger *value, int64_t i) {
+ GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj);
+ value->value = i;
+ gtk_switch_set_active(sw, i);
+}
+
+#else
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
+ return ui_checkbox_create(obj, args);
+}
+
+#endif
+
+#if GTK_MAJOR_VERSION >= 4
+#define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label)
+#define RADIOBUTTON_SET_GROUP(button, group)
+#define RADIOBUTTON_GET_GROUP(button) GTK_CHECK_BUTTON(button)
+#define RADIOBUTTON_GET_ACTIVE(button) gtk_check_button_get_active(GTK_CHECK_BUTTON(button))
+#else
+#define RADIOBUTTON_NEW(group, label) gtk_radio_button_new_with_label(group, label)
+#define RADIOBUTTON_SET_GROUP(button, group) /* noop */
+#define RADIOBUTTON_GET_GROUP(button) gtk_radio_button_get_group(GTK_RADIO_BUTTON(button))
+#define RADIOBUTTON_GET_ACTIVE(button) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))
+#endif
+
+static void radiobutton_toggled(void *widget, UiEventData *event) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = RADIOBUTTON_GET_ACTIVE(widget);
+ e.set = ui_get_setop();
+ event->callback(&e, event->userdata);
+}
+
+typedef struct UiRadioButtonData {
+ UiInteger *value;
+ UiVarEventData *eventdata;
+ UiBool first;
+} UiRadioButtonData;
+
+static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) {
+ if(data->first) {
+ ui_destroy_vardata(w, data->eventdata);
+ g_slist_free(data->value->obj);
+ data->value->obj = NULL;
+ data->value->get = NULL;
+ data->value->set = NULL;
+ } else {
+ free(data->eventdata);
+ }
+ free(data);
+}
+
+UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
+ GSList *rg = NULL;
+ UiInteger *rgroup;
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+
+ UiBool first = FALSE;
+ if(var) {
+ rgroup = var->value;
+ rg = rgroup->obj;
+ if(!rg) {
+ first = TRUE;
+ }
+ }
+
+ GtkWidget *rbutton = RADIOBUTTON_NEW(rg, args->label);
+ ui_set_name_and_style(rbutton, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, rbutton, args->groups);
+ if(rgroup) {
+#if GTK_MAJOR_VERSION >= 4
+ if(rg) {
+ gtk_check_button_set_group(GTK_CHECK_BUTTON(rbutton), rg->data);
+ }
+ rg = g_slist_prepend(rg, rbutton);
+#else
+ gtk_radio_button_set_group(GTK_RADIO_BUTTON(rbutton), rg);
+ rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton));
+#endif
+
+ rgroup->obj = rg;
+ rgroup->get = ui_radiobutton_get;
+ rgroup->set = ui_radiobutton_set;
+
+ ui_radiobutton_set(rgroup, rgroup->value);
+
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ event->obj = obj;
+ event->var = var;
+ event->observers = NULL;
+ event->callback = NULL;
+ event->userdata = NULL;
+
+ UiRadioButtonData *rbdata = malloc(sizeof(UiRadioButtonData));
+ rbdata->value = rgroup;
+ rbdata->eventdata = event;
+ rbdata->first = first;
+
+ g_signal_connect(
+ rbutton,
+ "toggled",
+ G_CALLBACK(ui_radio_obs),
+ event);
+ g_signal_connect(
+ rbutton,
+ "destroy",
+ G_CALLBACK(destroy_radiobutton),
+ rbdata);
+ }
+
+ if(args->onchange) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = args->onchangedata;
+ event->callback = args->onchange;
+ event->value = 0;
+ event->customdata = NULL;
+ event->customint = 0;
+
+ g_signal_connect(
+ rbutton,
+ "toggled",
+ G_CALLBACK(radiobutton_toggled),
+ event);
+ g_signal_connect(
+ rbutton,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, rbutton, &layout);
+
+ return rbutton;
+}
+
+void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event) {
+ UiInteger *i = event->var->value;
+
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = i->get(i);
+
+ ui_notify_evt(i->observers, &e);
+}
+
+#if GTK_MAJOR_VERSION >= 4
+int64_t ui_radiobutton_get(UiInteger *value) {
+ int selection = 0;
+ GSList *ls = value->obj;
+ int i = 0;
+ guint len = g_slist_length(ls);
+ while(ls) {
+ if(gtk_check_button_get_active(GTK_CHECK_BUTTON(ls->data))) {
+ selection = len - i - 1;
+ break;
+ }
+ ls = ls->next;
+ i++;
+ }
+
+ value->value = selection;
+ return selection;
+}
+
+void ui_radiobutton_set(UiInteger *value, int64_t i) {
+ GSList *ls = value->obj;
+ int s = g_slist_length(ls) - 1 - i;
+ int j = 0;
+ while(ls) {
+ if(j == s) {
+ gtk_check_button_set_active(GTK_CHECK_BUTTON(ls->data), TRUE);
+ break;
+ }
+ ls = ls->next;
+ j++;
+ }
+
+ value->value = i;
+}
+#else
+int64_t ui_radiobutton_get(UiInteger *value) {
+ int selection = 0;
+ GSList *ls = value->obj;
+ int i = 0;
+ guint len = g_slist_length(ls);
+ while(ls) {
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ls->data))) {
+ selection = len - i - 1;
+ break;
+ }
+ ls = ls->next;
+ i++;
+ }
+
+ value->value = selection;
+ return selection;
+}
+
+void ui_radiobutton_set(UiInteger *value, int64_t i) {
+ GSList *ls = value->obj;
+ int s = g_slist_length(ls) - 1 - i;
+ int j = 0;
+ while(ls) {
+ if(j == s) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ls->data), TRUE);
+ break;
+ }
+ ls = ls->next;
+ j++;
+ }
+
+ value->value = i;
+}
+#endif
+
+
+static void ui_destroy_linkbutton(GtkWidget *widget, UiLinkButton *data) {
+ free(data->link);
+ free(data);
+}
+
+static const char* linkbutton_get_uri(UiLinkButton *link) {
+ if(link->type == UI_LINK_BUTTON) {
+ return link->link;
+ } else {
+ return gtk_link_button_get_uri(GTK_LINK_BUTTON(link->widget));
+ }
+}
+
+static void linkbutton_set_uri(UiLinkButton *link, const char *uri) {
+ if(link->type == UI_LINK_BUTTON) {
+ free(link->link);
+ link->link = uri ? strdup(uri) : NULL;
+ } else {
+ gtk_link_button_set_uri(GTK_LINK_BUTTON(link->widget), uri);
+ }
+}
+
+static gboolean linkbutton_get_visited(UiLinkButton *link) {
+ if(link->type == UI_LINK_BUTTON) {
+ return FALSE;
+ } else {
+ gtk_link_button_get_visited(GTK_LINK_BUTTON(link->widget));
+ }
+}
+
+static void linkbutton_set_visited(UiLinkButton *link, gboolean visited) {
+ if(link->type != UI_LINK_BUTTON) {
+ gtk_link_button_set_visited(GTK_LINK_BUTTON(link->widget), visited);
+ }
+}
+
+/*
+ * Apply linkbutton settings from json. Expects jsonvalue to be a valid
+ * json object.
+ *
+ * {
+ * "label": "label text",
+ * "uri": "http://example.com",
+ * "visited": true
+ * }
+ *
+ */
+static void linkbutton_apply_value(UiLinkButton *link, const char *jsonvalue) {
+ CxJson json;
+ cxJsonInit(&json, NULL);
+ cxJsonFill(&json, jsonvalue);
+
+ CxJsonValue *value;
+ if(cxJsonNext(&json, &value) == CX_JSON_NO_ERROR) {
+ if(cxJsonIsObject(value)) {
+ CxJsonValue *label = cxJsonObjGet(value, "label");
+ CxJsonValue *uri = cxJsonObjGet(value, "uri");
+ CxJsonValue *visited = cxJsonObjGet(value, "visited");
+ if(label) {
+ gtk_button_set_label(GTK_BUTTON(link->widget), cxJsonIsString(label) ? cxJsonAsString(label) : NULL);
+ }
+ if(uri) {
+ linkbutton_set_uri(link, cxJsonIsString(uri) ? cxJsonAsString(uri) : NULL);
+
+ }
+ if(visited) {
+ linkbutton_set_visited(link, cxJsonIsBool(visited) ? cxJsonAsBool(visited) : FALSE);
+ }
+ }
+ cxJsonValueFree(value);
+ }
+ cxJsonDestroy(&json);
+}
+
+static char* create_linkbutton_jsonvalue(const char *label, const char *uri, gboolean include_null, gboolean visited, gboolean set_visited) {
+ CxJsonValue *obj = cxJsonCreateObj(NULL);
+ if(label) {
+ cxJsonObjPutString(obj, CX_STR("label"), label);
+ } else if(include_null) {
+ cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL);
+ }
+
+ if(uri) {
+ cxJsonObjPutString(obj, CX_STR("uri"), uri);
+ } else if(include_null) {
+ cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL);
+ }
+
+ if(set_visited) {
+ cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
+ }
+
+ CxJsonWriter writer = cxJsonWriterCompact();
+ CxBuffer buf;
+ cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer);
+ cxJsonValueFree(obj);
+ cxBufferTerminate(&buf);
+
+ return buf.space;
+}
+
+static char* linkbutton_get_value(UiLinkButton *link) {
+ const char *label = gtk_button_get_label(GTK_BUTTON(link->widget));
+ const char *uri = linkbutton_get_uri(link);
+ gboolean visited = linkbutton_get_visited(link);
+ return create_linkbutton_jsonvalue(label, uri, TRUE, visited, TRUE);
+}
+
+static void linkbutton_callback(GtkWidget *widget, UiLinkButton *data) {
+ if(data->onclick) {
+ UiEvent e;
+ e.obj = data->obj;
+ e.document = e.obj->ctx->document;
+ e.window = e.obj->window;
+ e.eventdata = (char*)linkbutton_get_uri(data);
+ e.eventdatatype = UI_EVENT_DATA_STRING;
+ e.intval = 0;
+ e.set = ui_get_setop();
+ data->onclick(&e, data->onclickdata);
+ }
+}
+
+static void linkbutton_clicked(GtkWidget *widget, UiLinkButton *data) {
+ linkbutton_callback(widget, data);
+ if(data->link) {
+#if GTK_CHECK_VERSION(4, 0, 0)
+ GtkUriLauncher *launcher = gtk_uri_launcher_new (data->link);
+ gtk_uri_launcher_launch (launcher, NULL, NULL, NULL, NULL);
+ g_object_unref (launcher);
+#elif GTK_CHECK_VERSION(3, 22, 0)
+ GError *error = NULL;
+ gtk_show_uri_on_window(NULL, data->link, GDK_CURRENT_TIME, &error);
+#elif
+ // TODO: call xdg-open
+#endif
+ }
+}
+
+static gboolean linkbutton_activate_link(GtkLinkButton *self, UiLinkButton *data) {
+ linkbutton_callback(data->widget, data);
+ return data->nofollow;
+}
+
+UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) {
+ UiLinkButton *data = malloc(sizeof(UiLinkButton));
+ memset(data, 0, sizeof(UiLinkButton));
+ data->obj = obj;
+ data->type = args->type;
+ data->nofollow = args->nofollow;
+ data->onclick = args->onclick;
+ data->onclickdata = args->onclickdata;
+
+ GtkWidget *button;
+ if(args->type == UI_LINK_BUTTON) {
+ button = gtk_button_new();
+ g_signal_connect(
+ button,
+ "clicked",
+ G_CALLBACK(linkbutton_clicked),
+ data);
+ } else {
+ button = gtk_link_button_new("file:///");
+ g_signal_connect(
+ button,
+ "activate-link",
+ G_CALLBACK(linkbutton_activate_link),
+ data);
+ }
+ gtk_button_set_label(GTK_BUTTON(button), args->label);
+#if GTK_CHECK_VERSION(4, 0, 0)
+ gtk_button_set_can_shrink(GTK_BUTTON(button), TRUE);
+#elif GTK_MAJOR_VERSION == 3
+ GtkWidget *child = gtk_bin_get_child(GTK_BIN(button));
+ gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_END);
+#endif
+ g_object_set_data(G_OBJECT(button), "ui_linkbutton", data);
+ g_signal_connect(
+ button,
+ "destroy",
+ G_CALLBACK(ui_destroy_linkbutton),
+ data);
+
+ data->widget = button;
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiString *str = var->value;
+ char *current_value = ui_get(str);
+ if(current_value) {
+ linkbutton_apply_value(data, current_value);
+ }
+
+ str->obj = data;
+ str->get = ui_linkbutton_get;
+ str->set = ui_linkbutton_set;
+ }
+
+ ui_set_name_and_style(button, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, button, args->groups);
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, button, &layout);
+
+ return button;
+}
+
+char* ui_linkbutton_get(UiString *s) {
+ UiLinkButton *link = s->obj;
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ s->value.ptr = linkbutton_get_value(link);
+ s->value.free = free;
+ return s->value.ptr;
+}
+
+void ui_linkbutton_set(UiString *s, const char *str) {
+ linkbutton_apply_value(s->obj, str);
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+#if GTK_MAJOR_VERSION == 3
+ UiLinkButton *data = s->obj;
+ GtkWidget *child = gtk_bin_get_child(GTK_BIN(data->widget));
+ gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_END);
+#endif
+}
+
+
+void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri) {
+ char *value = create_linkbutton_jsonvalue(label, uri, TRUE, FALSE, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+void ui_linkbutton_value_set_label(UiString *str, const char *label) {
+ char *value = create_linkbutton_jsonvalue(label, NULL, FALSE, FALSE, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+void ui_linkbutton_value_set_uri(UiString *str, const char *uri) {
+ char *value = create_linkbutton_jsonvalue(NULL, uri, FALSE, FALSE, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+void ui_linkbutton_value_set_visited(UiString *str, UiBool visited) {
+ char *value = create_linkbutton_jsonvalue(NULL, NULL, FALSE, visited, TRUE);
+ ui_set(str, value);
+ free(value);
+}
+
+
+void ui_linkbutton_set_label(UIWIDGET button, const char *label) {
+ gtk_button_set_label(GTK_BUTTON(button), label);
+}
+
+void ui_linkbutton_set_uri(UIWIDGET button, const char *label) {
+ UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton");
+ if(link) {
+ linkbutton_set_uri(link, label);
+ } else {
+ fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n");
+ }
+}
+
+void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited) {
+ UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton");
+ if(link) {
+ linkbutton_set_visited(link, visited);
+ } else {
+ fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n");
+ }
+}
+
+char* ui_linkbutton_get_label(UIWIDGET button) {
+ const char *label = gtk_button_get_label(GTK_BUTTON(button));
+ return label ? strdup(label) : NULL;
+}
+
+char* ui_linkbutton_get_uri(UIWIDGET button) {
+ UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton");
+ if(link) {
+ const char *uri = linkbutton_get_uri(link);
+ return uri ? strdup(uri) : NULL;
+ } else {
+ fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n");
+ }
+ return NULL;
+}
+
+UiBool ui_linkbutton_get_visited(UIWIDGET button) {
+ UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton");
+ if(link) {
+ return linkbutton_get_visited(link);
+ } else {
+ fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n");
+ }
+ return FALSE;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include "../ui/toolkit.h"
+#include "../ui/button.h"
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiLinkButton {
+ UiObject *obj;
+ GtkWidget *widget;
+ UiLinkType type;
+ UiBool nofollow;
+ char *link;
+ ui_callback onclick;
+ void *onclickdata;
+} UiLinkButton;
+
+void ui_button_set_icon_name(GtkWidget *button, const char *icon_name);
+
+typedef void (*ui_toggled_func)(void*, void*);
+
+GtkWidget* ui_create_button(
+ UiObject *obj,
+ const char *label,
+ const char *icon,
+ const char *tooltip,
+ ui_callback onclick,
+ void *userdata,
+ int event_value,
+ bool activate_event);
+
+void ui_setup_togglebutton(
+ UiObject *obj,
+ GtkWidget *togglebutton,
+ const char *label,
+ const char *icon,
+ const char *tooltip,
+ const char *varname,
+ UiInteger *value,
+ ui_callback onchange,
+ void *onchangedata,
+ int enable_state);
+
+void ui_bind_togglebutton(
+ UiObject *obj,
+ GtkWidget *widget,
+ int64_t (*getfunc)(UiInteger*),
+ void (*setfunc)(UiInteger*, int64_t),
+ const char *varname,
+ UiInteger *value,
+ void (*toggled_callback)(void*, void*),
+ ui_callback onchange,
+ void *onchangedata,
+ void (*enable_state_func)(void*, void*),
+ int enable_state);
+
+// event wrapper
+void ui_button_clicked(GtkWidget *widget, UiEventData *event);
+
+
+void ui_toggled_obs(void *widget, UiVarEventData *event);
+
+UIWIDGET ui_checkbox_var(UiObject *obj, char *label, UiVar *var);
+
+UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var);
+
+void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event);
+
+int64_t ui_switch_get(UiInteger *value);
+void ui_switch_set(UiInteger *value, int64_t i);
+
+int64_t ui_radiobutton_get(UiInteger *value);
+void ui_radiobutton_set(UiInteger *value, int64_t i);
+
+char* ui_linkbutton_get(UiString *s);
+void ui_linkbutton_set(UiString *s, const char *str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BUTTON_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "container.h"
+#include "toolkit.h"
+#include "headerbar.h"
+
+#include "../common/context.h"
+#include "../common/object.h"
+#include "../common/container.h"
+
+#include "../ui/properties.h"
+
+
+void ui_container_begin_close(UiObject *obj) {
+ UiContainerX *ct = obj->container_end;
+ ct->close = 1;
+}
+
+int ui_container_finish(UiObject *obj) {
+ UiContainerX *ct = obj->container_end;
+ if(ct->close) {
+ ui_end_new(obj);
+ return 0;
+ }
+ return 1;
+}
+
+GtkWidget* ui_gtk_vbox_new(int spacing) {
+#if GTK_MAJOR_VERSION >= 3
+ return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing);
+#else
+ return gtk_vbox_new(FALSE, spacing);
+#endif
+}
+
+GtkWidget* ui_gtk_hbox_new(int spacing) {
+#if GTK_MAJOR_VERSION >= 3
+ return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing);
+#else
+ return gtk_hbox_new(FALSE, spacing);
+#endif
+}
+
+GtkWidget* ui_subcontainer_create(
+ UiSubContainerType type,
+ UiObject *obj,
+ int spacing,
+ int columnspacing,
+ int rowspacing,
+ int margin)
+{
+ GtkWidget *sub = NULL;
+ GtkWidget *add = NULL;
+ UiContainerX *container = NULL;
+ switch(type) {
+ default: {
+ sub = ui_gtk_vbox_new(spacing);
+ add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+ container = ui_box_container(obj, sub, type);
+ break;
+ }
+ case UI_CONTAINER_HBOX: {
+ sub = ui_gtk_hbox_new(spacing);
+ add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+ container = ui_box_container(obj, sub, type);
+ break;
+ }
+ case UI_CONTAINER_GRID: {
+ sub = ui_create_grid_widget(columnspacing, rowspacing);
+ add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0);
+ container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE);
+ break;
+ }
+ case UI_CONTAINER_NO_SUB: {
+ break;
+ }
+ }
+ if(container) {
+ uic_object_push_container(obj, container);
+ }
+ return add;
+}
+
+
+/* -------------------- Box Container -------------------- */
+UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
+ UiBoxContainer *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiBoxContainer));
+ ct->container.widget = box;
+ ct->container.add = ui_box_container_add;
+ ct->type = type;
+ return (UiContainerX*)ct;
+}
+
+void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ UiBoxContainer *bc = (UiBoxContainer*)ct;
+ widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+
+ UiBool fill = layout->fill;
+ if(bc->has_fill && fill) {
+ fprintf(stderr, "UiError: container has 2 filled widgets");
+ fill = FALSE;
+ }
+ if(fill) {
+ bc->has_fill = TRUE;
+ }
+
+ UiBool expand = fill;
+#if GTK_MAJOR_VERSION >= 4
+ gtk_box_append(GTK_BOX(ct->widget), widget);
+ GtkAlign align = expand ? GTK_ALIGN_FILL : GTK_ALIGN_START;
+ if(bc->type == UI_CONTAINER_VBOX) {
+ gtk_widget_set_valign(widget, align);
+ gtk_widget_set_vexpand(widget, expand);
+ gtk_widget_set_hexpand(widget, TRUE);
+ } else if(bc->type == UI_CONTAINER_HBOX) {
+ gtk_widget_set_halign(widget, align);
+ gtk_widget_set_hexpand(widget, expand);
+ gtk_widget_set_vexpand(widget, TRUE);
+ }
+
+#else
+ gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0);
+#endif
+
+ ct->current = widget;
+}
+
+UiContainerX* ui_grid_container(
+ UiObject *obj,
+ GtkWidget *grid,
+ UiBool def_hexpand,
+ UiBool def_vexpand,
+ UiBool def_hfill,
+ UiBool def_vfill)
+{
+ UiGridContainer *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiGridContainer));
+ ct->def_hexpand = def_hexpand;
+ ct->def_vexpand = def_vexpand;
+ ct->def_hfill = def_hfill;
+ ct->def_vfill = def_vfill;
+ ct->container.widget = grid;
+ ct->container.add = ui_grid_container_add;
+ UI_GTK_V2(ct->width = 0);
+ UI_GTK_V2(ct->height = 1);
+ return (UiContainerX*)ct;
+}
+
+
+#if GTK_MAJOR_VERSION >= 3
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ UiGridContainer *grid = (UiGridContainer*)ct;
+ widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+
+ if(ct->container.newline) {
+ grid->x = 0;
+ grid->y++;
+ ct->container.newline = FALSE;
+ }
+
+ uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
+
+ if(!layout->hfill) {
+ gtk_widget_set_halign(widget, GTK_ALIGN_START);
+ }
+ if(!layout->vfill) {
+ gtk_widget_set_valign(widget, GTK_ALIGN_START);
+ }
+
+ gtk_widget_set_hexpand(widget, layout->hexpand);
+ gtk_widget_set_vexpand(widget, layout->vexpand);
+
+ int colspan = layout->colspan > 0 ? layout->colspan : 1;
+ int rowspan = layout->rowspan > 0 ? layout->rowspan : 1;
+
+ gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan);
+ grid->x += colspan;
+
+ grid->container.current = widget;
+}
+#endif
+#ifdef UI_GTK2
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget) {
+ UiGridContainer *grid = (UiGridContainer*)ct;
+ widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+
+ if(ct->container.newline) {
+ grid->x = 0;
+ grid->y++;
+ ct->container.newline = FALSE;
+ }
+
+ uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
+
+ GtkAttachOptions xoptions = 0;
+ GtkAttachOptions yoptions = 0;
+ if(layout->hexpand) {
+ xoptions = GTK_EXPAND;
+ }
+ if(layout->hfill) {
+ xoptions |= GTK_FILL;
+ }
+ if(layout->vexpand) {
+ yoptions = GTK_EXPAND;
+ }
+ if(layout->vfill) {
+ yoptions |= GTK_FILL;
+ }
+
+ int colspan = layout->colspan > 0 ? layout->colspan : 1;
+ int rowspan = layout->rowspan > 0 ? layout->rowspan : 1;
+ // TODO: use colspan/rowspan
+
+ gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0);
+ grid->x++;
+ int nw = grid->x > grid->width ? grid->x : grid->width;
+ if(grid->x > grid->width || grid->y > grid->height) {
+ grid->width = nw;
+ grid->height = grid->y + 1;
+ gtk_table_resize(GTK_TABLE(ct->widget), grid->width, grid->height);
+ }
+
+ ct->current = widget;
+}
+#endif
+
+UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame) {
+ UiContainerPrivate *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiContainerPrivate));
+ ct->widget = frame;
+ ct->add = ui_frame_container_add;
+ return (UiContainerX*)ct;
+}
+
+void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+ FRAME_SET_CHILD(ct->widget, widget);
+ ct->current = widget;
+}
+
+UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander) {
+ UiContainerPrivate *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiContainerPrivate));
+ ct->widget = expander;
+ ct->add = ui_expander_container_add;
+ return (UiContainerX*)ct;
+}
+
+void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+ EXPANDER_SET_CHILD(ct->widget, widget);
+ ct->current = widget;
+}
+
+void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+ // TODO: check if the widget implements GtkScrollable
+ SCROLLEDWINDOW_SET_CHILD(ct->widget, widget);
+ ct->current = widget;
+}
+
+UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
+ UiContainerPrivate *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiContainerPrivate));
+ ct->widget = scrolledwindow;
+ ct->add = ui_scrolledwindow_container_add;
+ return (UiContainerX*)ct;
+}
+
+UiContainerX* ui_tabview_container(UiObject *obj, GtkWidget *tabview) {
+ UiTabViewContainer *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiTabViewContainer));
+ ct->container.widget = tabview;
+ ct->container.add = ui_tabview_container_add;
+ return (UiContainerX*)ct;
+}
+
+void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ UiGtkTabView *data = ui_widget_get_tabview_data(ct->widget);
+ if(!data) {
+ fprintf(stderr, "UI Error: widget is not a tabview");
+ return;
+ }
+ widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
+ data->add_tab(ct->widget, -1, layout->label, widget);
+
+ ct->current = widget;
+}
+
+#ifdef UI_GTK2
+
+static void alignment_child_visibility_changed(GtkWidget *widget, gpointer user_data) {
+ gtk_widget_set_visible(gtk_widget_get_parent(widget), gtk_widget_get_visible(widget));
+}
+
+#endif
+
+GtkWidget* ui_gtk_set_margin(GtkWidget *widget, int margin, int margin_left, int margin_right, int margin_top, int margin_bottom) {
+ if(margin > 0) {
+ margin_left = margin;
+ margin_right = margin;
+ margin_top = margin;
+ margin_bottom = margin;
+ }
+ GtkWidget *ret = widget;
+#if GTK_MAJOR_VERSION >= 3
+#if GTK_CHECK_VERSION(3, 12, 0)
+ gtk_widget_set_margin_start(widget, margin_left);
+ gtk_widget_set_margin_end(widget, margin_right);
+#else
+ gtk_widget_set_margin_left(widget, margin_left);
+ gtk_widget_set_margin_right(widget, margin_right);
+#endif
+ gtk_widget_set_margin_top(widget, margin_top);
+ gtk_widget_set_margin_bottom(widget, margin_bottom);
+#elif defined(UI_GTK2)
+ GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin_top, margin_bottom, margin_left, margin_right);
+ gtk_container_add(GTK_CONTAINER(a), widget);
+ g_signal_connect(
+ widget,
+ "show",
+ G_CALLBACK(alignment_child_visibility_changed),
+ NULL);
+ g_signal_connect(
+ widget,
+ "hide",
+ G_CALLBACK(alignment_child_visibility_changed),
+ NULL);
+ ret = a;
+#endif
+ return ret;
+}
+
+UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType type) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args->spacing) : ui_gtk_hbox_new(args->spacing);
+ ui_set_name_and_style(box, args->name, args->style_class);
+ ct->add(ct, box, &layout);
+
+ UiContainerX *container = ui_box_container(obj, box, type);
+ uic_object_push_container(obj, container);
+
+ return box;
+}
+
+UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
+ return ui_box_create(obj, args, UI_CONTAINER_VBOX);
+}
+
+UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
+ return ui_box_create(obj, args, UI_CONTAINER_HBOX);
+}
+
+GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing) {
+#if GTK_MAJOR_VERSION >= 3
+ GtkWidget *grid = gtk_grid_new();
+ gtk_grid_set_column_spacing(GTK_GRID(grid), colspacing);
+ gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing);
+#else
+ GtkWidget *grid = gtk_table_new(1, 1, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(grid), colspacing);
+ gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing);
+#endif
+ return grid;
+}
+
+UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ GtkWidget *widget;
+
+ GtkWidget *grid = ui_create_grid_widget(args->columnspacing, args->rowspacing);
+ ui_set_name_and_style(grid, args->name, args->style_class);
+ ct->add(ct, grid, &layout);
+
+ UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill);
+ uic_object_push_container(obj, container);
+
+ return grid;
+}
+
+static void frame_create_subcontainer(UiObject *obj, UiFrameArgs *args) {
+ switch(args->subcontainer) {
+ default:
+ case UI_CONTAINER_VBOX: {
+ UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding };
+ ui_vbox_create(obj, &sub_args);
+ break;
+ }
+ case UI_CONTAINER_HBOX: {
+ UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding };
+ ui_hbox_create(obj, &sub_args);
+ break;
+ }
+ case UI_CONTAINER_GRID: {
+ UiContainerArgs sub_args = { .columnspacing = args->columnspacing, .rowspacing = args->rowspacing, .margin = args->padding };
+ ui_grid_create(obj, &sub_args);
+ break;
+ }
+ case UI_CONTAINER_NO_SUB: {
+ break; // NOOP
+ }
+ }
+}
+
+UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ GtkWidget *frame = gtk_frame_new(args->label);
+ ct->add(ct, frame, &layout);
+
+ GtkWidget *sub = ui_subcontainer_create(
+ args->subcontainer,
+ obj, args->spacing,
+ args->columnspacing,
+ args->rowspacing,
+ args->padding);
+ if(sub) {
+ FRAME_SET_CHILD(frame, sub);
+ } else {
+ UiContainerX *container = ui_frame_container(obj, frame);
+ uic_object_push_container(obj, container);
+ }
+
+ return frame;
+}
+
+UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ GtkWidget *expander = gtk_expander_new(args->label);
+ gtk_expander_set_expanded(GTK_EXPANDER(expander), args->isexpanded);
+ ct->add(ct, expander, &layout);
+
+ GtkWidget *sub = ui_subcontainer_create(
+ args->subcontainer,
+ obj, args->spacing,
+ args->columnspacing,
+ args->rowspacing,
+ args->padding);
+ if(sub) {
+ EXPANDER_SET_CHILD(expander, sub);
+ } else {
+ UiContainerX *container = ui_expander_container(obj, expander);
+ uic_object_push_container(obj, container);
+ }
+
+ return expander;
+}
+
+
+UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ GtkWidget *sw = SCROLLEDWINDOW_NEW();
+ ui_set_name_and_style(sw, args->name, args->style_class);
+ ct->add(ct, sw, &layout);
+
+ GtkWidget *sub = ui_subcontainer_create(
+ args->subcontainer,
+ obj, args->spacing,
+ args->columnspacing,
+ args->rowspacing,
+ args->padding);
+ if(sub) {
+ SCROLLEDWINDOW_SET_CHILD(sw, sub);
+ } else {
+ UiContainerX *container = ui_scrolledwindow_container(obj, sw);
+ uic_object_push_container(obj, container);
+ }
+
+ return sw;
+}
+
+
+void ui_notebook_tab_select(UIWIDGET tabview, int tab) {
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab);
+}
+
+void ui_notebook_tab_remove(UIWIDGET tabview, int tab) {
+ gtk_notebook_remove_page(GTK_NOTEBOOK(tabview), tab);
+}
+
+void ui_notebook_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) {
+ gtk_notebook_insert_page(
+ GTK_NOTEBOOK(widget),
+ child,
+ gtk_label_new(name),
+ index);
+}
+
+int64_t ui_notebook_get(UiInteger *i) {
+ GtkNotebook *nb = i->obj;
+ i->value = gtk_notebook_get_current_page(nb);
+ return i->value;
+}
+
+void ui_notebook_set(UiInteger *i, int64_t value) {
+ GtkNotebook *nb = i->obj;
+ gtk_notebook_set_current_page(nb, value);
+ i->value = gtk_notebook_get_current_page(nb);
+}
+
+
+#if GTK_MAJOR_VERSION >= 4
+static int stack_set_page(GtkWidget *stack, int index) {
+ GtkSelectionModel *pages = gtk_stack_get_pages(GTK_STACK(stack));
+ GListModel *list = G_LIST_MODEL(pages);
+ GtkStackPage *page = g_list_model_get_item(list, index);
+ if(page) {
+ gtk_stack_set_visible_child(GTK_STACK(stack), gtk_stack_page_get_child(page));
+ } else {
+ fprintf(stderr, "UI Error: ui_stack_set value out of bounds\n");
+ return -1;
+ }
+ return index;
+}
+
+void ui_stack_tab_select(UIWIDGET tabview, int tab) {
+ stack_set_page(tabview, tab);
+}
+
+void ui_stack_tab_remove(UIWIDGET tabview, int tab) {
+ GtkStack *stack = GTK_STACK(tabview);
+ GtkWidget *current = gtk_stack_get_visible_child(stack);
+ GtkSelectionModel *pages = gtk_stack_get_pages(stack);
+ GListModel *list = G_LIST_MODEL(pages);
+ GtkStackPage *page = g_list_model_get_item(list, tab);
+ if(page) {
+ gtk_stack_remove(stack, gtk_stack_page_get_child(page));
+ }
+}
+
+void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) {
+ (void)gtk_stack_add_titled(GTK_STACK(widget), child, name, name);
+}
+
+int64_t ui_stack_get(UiInteger *i) {
+ GtkStack *stack = GTK_STACK(i->obj);
+ GtkWidget *current = gtk_stack_get_visible_child(stack);
+ GtkSelectionModel *pages = gtk_stack_get_pages(stack);
+ GListModel *list = G_LIST_MODEL(pages);
+ int nitems = g_list_model_get_n_items(list);
+ for(int p=0;p<nitems;p++) {
+ GtkStackPage *page = g_list_model_get_item(list, p);
+ GtkWidget *child = gtk_stack_page_get_child(page);
+ if(child == current) {
+ i->value = p;
+ break;
+ }
+ }
+ return i->value;
+}
+
+void ui_stack_set(UiInteger *i, int64_t value) {
+ GtkWidget *widget = i->obj;
+ if(stack_set_page(widget, value) >= 0) {
+ i->value = value;
+ }
+}
+#elif GTK_MAJOR_VERSION >= 3
+static GtkWidget* stack_get_child(GtkWidget *stack, int index) {
+ GList *children = gtk_container_get_children(GTK_CONTAINER(stack));
+ if(children) {
+ return g_list_nth_data(children, index);
+ }
+ return NULL;
+}
+
+void ui_stack_tab_select(UIWIDGET tabview, int tab) {
+ GtkWidget *child = stack_get_child(tabview, tab);
+ if(child) {
+ gtk_stack_set_visible_child(GTK_STACK(tabview), child);
+ }
+}
+
+void ui_stack_tab_remove(UIWIDGET tabview, int tab) {
+ GtkWidget *child = stack_get_child(tabview, tab);
+ if(child) {
+ gtk_container_remove(GTK_CONTAINER(tabview), child);
+ }
+}
+
+void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) {
+ gtk_stack_add_titled(GTK_STACK(widget), child, name, name);
+}
+
+int64_t ui_stack_get(UiInteger *i) {
+ GtkWidget *visible = gtk_stack_get_visible_child(GTK_STACK(i->obj));
+ GList *children = gtk_container_get_children(GTK_CONTAINER(i->obj));
+ GList *elm = children;
+ int n = 0;
+ int64_t v = -1;
+ while(elm) {
+ GtkWidget *child = elm->data;
+ if(child == visible) {
+ v = n;
+ break;
+ }
+
+ elm = elm->next;
+ n++;
+ }
+ g_list_free(children);
+ i->value = v;
+ return v;
+}
+
+void ui_stack_set(UiInteger *i, int64_t value) {
+ GtkWidget *child = stack_get_child(i->obj, value);
+ if(child) {
+ gtk_stack_set_visible_child(GTK_STACK(i->obj), child);
+ i->value = value;
+ }
+}
+
+#endif
+
+
+
+
+UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview) {
+ return g_object_get_data(G_OBJECT(tabview), "ui_tabview");
+}
+
+static void tabview_switch_page(
+ GtkNotebook *self,
+ GtkWidget *page,
+ guint page_num,
+ gpointer userdata)
+{
+ UiGtkTabView *tabview = userdata;
+ if(!tabview->onchange) {
+ return;
+ }
+
+ UiEvent event;
+ event.obj = tabview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.set = ui_get_setop();
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = page_num;
+
+ tabview->onchange(&event, tabview->onchange);
+}
+
+#if GTK_CHECK_VERSION(3, 10, 0)
+
+static void tabview_stack_changed(
+ GObject *object,
+ GParamSpec *pspec,
+ UiGtkTabView *tabview)
+{
+ if(!tabview->onchange) {
+ return;
+ }
+
+ UiEvent event;
+ event.obj = tabview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.set = ui_get_setop();
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = 0;
+
+ tabview->onchange(&event, tabview->onchange);
+}
+
+#endif
+
+typedef int64_t(*ui_tabview_get_func)(UiInteger*);
+typedef void (*ui_tabview_set_func)(UiInteger*, int64_t);
+
+UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) {
+ UiGtkTabView *data = malloc(sizeof(UiGtkTabView));
+ memset(data, 0, sizeof(UiGtkTabView));
+ data->padding = args->padding;
+ data->spacing = args->spacing;
+ data->columnspacing = args->columnspacing;
+ data->rowspacing = args->rowspacing;
+
+ ui_tabview_get_func getfunc = NULL;
+ ui_tabview_set_func setfunc = NULL;
+
+ GtkWidget *widget = NULL;
+ GtkWidget *data_widget = NULL;
+ switch(args->tabview) {
+ case UI_TABVIEW_DOC: {
+ // TODO
+ break;
+ }
+ case UI_TABVIEW_NAVIGATION_SIDE: {
+#if GTK_CHECK_VERSION(3, 10, 0)
+ widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ GtkWidget *sidebar = gtk_stack_sidebar_new();
+ BOX_ADD(widget, sidebar);
+ GtkWidget *stack = gtk_stack_new();
+ g_signal_connect(stack, "notify::visible-child", G_CALLBACK(tabview_stack_changed), data);
+ gtk_stack_set_transition_type (GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+ gtk_stack_sidebar_set_stack(GTK_STACK_SIDEBAR(sidebar), GTK_STACK(stack));
+ BOX_ADD_EXPAND(widget, stack);
+ data->select_tab = ui_stack_tab_select;
+ data->remove_tab = ui_stack_tab_remove;
+ data->add_tab = ui_stack_tab_add;
+ getfunc = ui_stack_get;
+ setfunc = ui_stack_set;
+ data_widget = stack;
+#else
+ // TODO
+#endif
+ break;
+ }
+ case UI_TABVIEW_DEFAULT: /* fall through */
+ case UI_TABVIEW_NAVIGATION_TOP: /* fall through */
+ case UI_TABVIEW_INVISIBLE: /* fall through */
+ case UI_TABVIEW_NAVIGATION_TOP2: {
+ widget = gtk_notebook_new();
+ g_signal_connect(
+ widget,
+ "switch-page",
+ G_CALLBACK(tabview_switch_page),
+ data);
+ data_widget = widget;
+ data->select_tab = ui_notebook_tab_select;
+ data->remove_tab = ui_notebook_tab_remove;
+ data->add_tab = ui_notebook_tab_add;
+ getfunc = ui_notebook_get;
+ setfunc = ui_notebook_set;
+ if(args->tabview == UI_TABVIEW_INVISIBLE) {
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(widget), FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(widget), FALSE);
+ }
+ break;
+ }
+ }
+
+ if(args->value || args->varname) {
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ UiInteger *i = var->value;
+ i->get = getfunc;
+ i->set = setfunc;
+ i->obj = data_widget;
+ }
+
+ g_object_set_data(G_OBJECT(widget), "ui_tabview", data);
+ data->widget = data_widget;
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ UiContainerX *container = ui_tabview_container(obj, widget);
+ uic_object_push_container(obj, container);
+
+ return widget;
+}
+
+static GtkWidget* create_tab(UiObject *obj, UiGtkTabView *tabview, const char *title, int tab) {
+ UiContainerX *container;
+ GtkWidget *sub;
+ switch(tabview->subcontainer) {
+ default: {
+ sub = ui_gtk_vbox_new(tabview->spacing);
+ container = ui_box_container(obj, sub, tabview->subcontainer);
+ break;
+ }
+ case UI_CONTAINER_HBOX: {
+ sub = ui_gtk_hbox_new(tabview->spacing);
+ container = ui_box_container(obj, sub, tabview->subcontainer);
+ break;
+ }
+ case UI_CONTAINER_GRID: {
+ sub = ui_create_grid_widget(tabview->columnspacing, tabview->rowspacing);
+ container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE);
+ break;
+ }
+ }
+
+ uic_object_push_container(obj, container);
+
+ GtkWidget *widget = ui_gtk_set_margin(sub, tabview->padding, 0, 0, 0, 0);
+ tabview->add_tab(tabview->widget, tab, title, widget);
+
+ return sub;
+}
+
+void ui_tab_create(UiObject* obj, const char* title) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ GtkWidget *tabview = ct->widget;
+ UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
+ if(!data) {
+ fprintf(stderr, "UI Error: widget is not a tabview\n");
+ return;
+ }
+
+ create_tab(obj, data, title, -1);
+}
+
+
+
+void ui_tabview_select(UIWIDGET tabview, int tab) {
+ UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
+ if(!data) {
+ fprintf(stderr, "UI Error: widget is not a tabview\n");
+ return;
+ }
+ data->select_tab(tabview, tab);
+}
+
+void ui_tabview_remove(UIWIDGET tabview, int tab) {
+ UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
+ if(!data) {
+ fprintf(stderr, "UI Error: widget is not a tabview\n");
+ return;
+ }
+ data->remove_tab(tabview, tab);
+}
+
+UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
+ UiGtkTabView *data = ui_widget_get_tabview_data(tabview);
+ if(!data) {
+ fprintf(stderr, "UI Error: widget is not a tabview\n");
+ return NULL;
+ }
+
+ UiObject *newobj = uic_object_new_toplevel();
+ newobj->widget = create_tab(newobj, data, name, tab_index);
+
+ return newobj;
+}
+
+
+/* -------------------- Headerbar -------------------- */
+
+static void hb_set_part(UiObject *obj, int part) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ GtkWidget *headerbar = ct->widget;
+
+ UiHeaderbarContainer *hb = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiHeaderbarContainer));
+ memcpy(hb, ct, sizeof(UiHeaderbarContainer));
+ hb->part = part;
+ uic_object_push_container(obj, (UiContainerX*)hb);
+}
+
+void ui_headerbar_start_create(UiObject *obj) {
+ hb_set_part(obj, 0);
+}
+
+void ui_headerbar_center_create(UiObject *obj) {
+ hb_set_part(obj, 2);
+}
+
+void ui_headerbar_end_create(UiObject *obj) {
+ hb_set_part(obj, 1);
+}
+
+UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs *args) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ GtkWidget *box = ui_gtk_hbox_new(args->alt_spacing);
+ ui_set_name_and_style(box, args->name, args->style_class);
+ ct->add(ct, box, &layout);
+
+ UiContainerX *container = ui_headerbar_fallback_container(obj, box);
+ uic_object_push_container(obj, container);
+
+ return box;
+}
+
+static void hb_fallback_set_part(UiObject *obj, int part) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ GtkWidget *headerbar = ct->widget;
+
+ UiContainerX *container = ui_headerbar_container(obj, headerbar);
+ uic_object_push_container(obj, container);
+
+ UiHeaderbarContainer *hb = (UiHeaderbarContainer*)container;
+ hb->part = part;
+}
+
+UiContainerX* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) {
+ UiHeaderbarContainer *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiHeaderbarContainer));
+ ct->container.widget = headerbar;
+ ct->container.add = ui_headerbar_fallback_container_add;
+ return (UiContainerX*)ct;
+}
+
+void ui_headerbar_fallback_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
+ BOX_ADD(ct->widget, widget);
+}
+
+#if GTK_CHECK_VERSION(3, 10, 0)
+
+UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) {
+ GtkWidget *headerbar = g_object_get_data(G_OBJECT(obj->widget), "ui_headerbar");
+ if(!headerbar) {
+ return ui_headerbar_fallback_create(obj, args);
+ }
+
+ UiContainerX *container = ui_headerbar_container(obj, headerbar);
+ uic_object_push_container(obj, container);
+
+ return headerbar;
+}
+
+UiContainerX* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) {
+ UiHeaderbarContainer *ct = cxCalloc(
+ obj->ctx->allocator,
+ 1,
+ sizeof(UiHeaderbarContainer));
+ ct->container.widget = headerbar;
+ ct->container.add = ui_headerbar_container_add;
+ return (UiContainerX*)ct;
+}
+
+void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct;
+ if(hb->part == 0) {
+ UI_HEADERBAR_PACK_START(ct->widget, widget);
+ } else if(hb->part == 1) {
+ UI_HEADERBAR_PACK_END(ct->widget, widget);
+ } else if(hb->part == 2) {
+ if(!hb->centerbox) {
+ GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ hb->centerbox = box;
+ UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box);
+ }
+ BOX_ADD(hb->centerbox, widget);
+ }
+}
+
+#else
+
+UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) {
+ return ui_headerbar_fallback_create(obj, args);
+}
+
+#endif
+
+/* -------------------- Sidebar -------------------- */
+
+#ifdef UI_LIBADWAITA
+UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
+ GtkWidget *sidebar_toolbar_view = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar");
+ if(!sidebar_toolbar_view) {
+ fprintf(stderr, "Error: window is not configured for sidebar\n");
+ return NULL;
+ }
+
+ GtkWidget *box = ui_gtk_vbox_new(args->spacing);
+ ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
+ adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), box);
+
+ UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+ uic_object_push_container(obj, container);
+
+ return box;
+}
+#else
+UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
+ GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar");
+
+ GtkWidget *box = ui_gtk_vbox_new(args->spacing);
+ ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
+ BOX_ADD_EXPAND(sidebar_vbox, box);
+
+ UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+ uic_object_push_container(obj, container);
+
+ return box;
+}
+#endif
+
+/* ------------------------ Split Window Panels ------------------------ */
+
+static UIWIDGET splitwindow_panel(UiObject *obj, GtkWidget *pbox, UiSidebarArgs *args) {
+ if(!pbox) {
+ fprintf(stderr, "Error: window is not a splitview window\n");
+ return NULL;
+ }
+
+ GtkWidget *box = ui_gtk_vbox_new(args->spacing);
+ ui_set_name_and_style(box, args->name, args->style_class);
+ ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom);
+ BOX_ADD_EXPAND(pbox, box);
+
+ UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+ uic_object_push_container(obj, container);
+
+ return box;
+}
+
+UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args) {
+ return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_left_panel"), args);
+}
+
+UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args) {
+ return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_right_panel"), args);
+}
+
+
+/* -------------------- Splitpane -------------------- */
+
+static GtkWidget* create_paned(UiOrientation orientation) {
+#if GTK_MAJOR_VERSION >= 3
+ switch(orientation) {
+ case UI_HORIZONTAL: return gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ case UI_VERTICAL: return gtk_paned_new(GTK_ORIENTATION_VERTICAL);
+ }
+#else
+ switch(orientation) {
+ case UI_HORIZONTAL: return gtk_hpaned_new();
+ case UI_VERTICAL: return gtk_vpaned_new();
+ }
+#endif
+ return NULL;
+}
+
+static void save_pane_pos(GtkWidget *widget, char *property_name) {
+ int pos = gtk_paned_get_position(GTK_PANED(widget));
+ char buf[32];
+ snprintf(buf, 32, "%d", pos);
+ ui_set_property(property_name, buf);
+ free(property_name);
+}
+
+static UIWIDGET splitpane_create(UiObject *obj, UiOrientation orientation, UiSplitPaneArgs *args) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ GtkWidget *pane0 = create_paned(orientation);
+ ct->add(ct, pane0, &layout);
+
+ int max = args->max_panes == 0 ? 2 : args->max_panes;
+
+ if(args->position_property) {
+ const char *pos_str = ui_get_property(args->position_property);
+ if(pos_str) {
+ char *end;
+ long pos = strtol(pos_str, &end, 10);
+ if(*end == '\0') {
+ args->initial_position = (int)pos;
+ }
+ }
+
+ g_signal_connect(
+ pane0,
+ "destroy",
+ G_CALLBACK(save_pane_pos),
+ strdup(args->position_property));
+ }
+
+ UiSplitPane *splitpane = ui_create_splitpane_data(pane0, orientation, max, args->initial_position);
+ UiContainerX *container = ui_splitpane_container(obj, pane0, splitpane);
+ uic_object_push_container(obj, container);
+
+ g_object_set_data(G_OBJECT(pane0), "ui_splitpane", splitpane);
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *i = var->value;
+ splitpane->initial_position = i->value;
+
+ i->obj = splitpane;
+ i->get = ui_splitpane_get;
+ i->set = ui_splitpane_set;
+ }
+
+ return pane0;
+}
+
+UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) {
+ return splitpane_create(obj, UI_HORIZONTAL, args);
+}
+
+UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) {
+ return splitpane_create(obj, UI_VERTICAL, args);
+}
+
+UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init) {
+ UiSplitPane *ct = malloc(sizeof(UiSplitPane));
+ ct->current_pane = pane;
+ ct->orientation = orientation;
+ ct->max = max;
+ ct->initial_position = init;
+ ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+ return ct;
+}
+
+UiContainerX* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiSplitPane *data) {
+ UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer));
+ ct->container.widget = pane;
+ ct->container.add = ui_splitpane_container_add;
+ ct->splitpane = data;
+ return (UiContainerX*)ct;
+}
+
+void ui_splitpane_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ UiSplitPaneContainer *sct = (UiSplitPaneContainer*)ct;
+ UiSplitPane *s = sct->splitpane;
+
+ if(s->nchildren >= s->max) {
+ fprintf(stderr, "splitpane: maximum number of children reached\n");
+ return;
+ }
+
+ cxListAdd(s->children, widget);
+
+ if(s->pos == 0) {
+ PANED_SET_CHILD1(s->current_pane, widget);
+ if(s->initial_position > 0) {
+ gtk_paned_set_position(GTK_PANED(s->current_pane), s->initial_position);
+ }
+ s->pos++;
+ s->nchildren++;
+ } else {
+ if(s->nchildren+1 == s->max) {
+ PANED_SET_CHILD2(s->current_pane, widget);
+ } else {
+ GtkWidget *pane = create_paned(s->orientation);
+ PANED_SET_CHILD1(pane, widget);
+ PANED_SET_CHILD2(s->current_pane, pane);
+ s->current_pane = pane;
+ }
+
+ s->pos = 0;
+ s->nchildren++;
+ }
+}
+
+int64_t ui_splitpane_get(UiInteger *i) {
+ UiSplitPane *s = i->obj;
+ i->value = gtk_paned_get_position(GTK_PANED(s->current_pane));
+ return i->value;
+}
+
+void ui_splitpane_set(UiInteger *i, int64_t value) {
+ UiSplitPane *s = i->obj;
+ i->value = value;
+ gtk_paned_set_position(GTK_PANED(s->current_pane), (int)value);
+}
+
+UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible) {
+ UiSplitPane *s = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane");
+ if(!s) {
+ fprintf(stderr, "UI Error: not a splitpane\n");
+ return;
+ }
+
+ GtkWidget *w = cxListAt(s->children, child_index);
+ if(w) {
+ gtk_widget_set_visible(w, visible);
+ }
+}
+
+/* -------------------- ItemList Container -------------------- */
+
+static void remove_item(void *data, void *item) {
+ UiGtkItemListContainer *ct = data;
+ UiObject *obj = item;
+ if(ct->remove_items) {
+ BOX_REMOVE(ct->widget, obj->widget);
+ }
+ uic_object_destroy(obj);
+}
+
+static void update_itemlist(UiList *list, int c) {
+ UiGtkItemListContainer *ct = list->obj;
+
+ CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ new_items->collection.advanced_destructor = remove_item;
+ new_items->collection.destructor_data = ct;
+
+ // only create new widgets for new elements, so at first we have
+ // to find which elements are new
+ // check which elements in the list are already in the container
+ void *elm = list->first(list);
+ int j = 0;
+ while(elm) {
+ CxHashKey key = cx_hash_key(&elm, sizeof(void*));
+ UiObject *item_obj = NULL;
+ cxMapRemoveAndGet(ct->current_items, key, &item_obj);
+ if(item_obj) {
+ g_object_ref(G_OBJECT(item_obj->widget));
+ BOX_REMOVE(ct->widget, item_obj->widget);
+ cxMapPut(new_items, key, item_obj);
+ }
+ elm = list->next(list);
+ j++;
+ }
+
+ // ct->current_items only contains elements, that are not in the list
+ cxMapFree(ct->current_items); // calls destructor remove_item
+ ct->current_items = new_items;
+
+ // add all items
+ int index = 0;
+ elm = list->first(list);
+ while(elm) {
+ CxHashKey key = cx_hash_key(&elm, sizeof(void*));
+ UiObject *item_obj = cxMapGet(ct->current_items, key);
+ if(item_obj) {
+ // re-add previously created widget
+ UiLayout layout = {0};
+ ui_box_container_add(ct->container, item_obj->widget, &layout);
+ } else {
+ // create new widget and object for this list element
+ UiObject *obj = uic_object_new_toplevel();
+ obj->ctx->parent = ct->parent->ctx;
+ obj->window = NULL;
+ obj->widget = ui_subcontainer_create(
+ ct->subcontainer,
+ obj,
+ ct->spacing,
+ ct->columnspacing,
+ ct->rowspacing,
+ ct->margin);
+ UiLayout layout = {0};
+ ui_box_container_add(ct->container, obj->widget, &layout);
+ if(ct->create_ui) {
+ ct->create_ui(obj, index, elm, ct->userdata);
+ }
+ cxMapPut(new_items, key, obj);
+ }
+ elm = list->next(list);
+ index++;
+ }
+
+#if GTK_MAJOR_VERSION < 4
+ gtk_widget_show_all(ct->widget);
+#endif
+}
+
+static void destroy_itemlist_container(GtkWidget *w, UiGtkItemListContainer *container) {
+ container->remove_items = FALSE;
+ cxMapFree(container->current_items);
+ free(container);
+}
+
+UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) {
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ GtkWidget *box = args->container == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args->spacing) : ui_gtk_hbox_new(args->spacing);
+ ui_set_name_and_style(box, args->name, args->style_class);
+ ct->add(ct, box, &layout);
+
+ UiGtkItemListContainer *container = malloc(sizeof(UiGtkItemListContainer));
+ container->parent = obj;
+ container->widget = box;
+ container->container = (UiContainerPrivate*)ui_box_container(obj, box, args->container);
+ container->create_ui = args->create_ui;
+ container->userdata = args->userdata;
+ container->subcontainer = args->subcontainer;
+ container->current_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ container->current_items->collection.advanced_destructor = remove_item;
+ container->current_items->collection.destructor_data = container;
+ container->margin = args->sub_margin;
+ container->spacing = args->sub_spacing;
+ container->columnspacing = args->sub_columnspacing;
+ container->rowspacing = args->sub_rowspacing;
+ container->remove_items = TRUE;
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_LIST);
+ if(var) {
+ UiList *list = var->value;
+ list->obj = container;
+ list->update = update_itemlist;
+ update_itemlist(list, 0);
+ }
+ g_signal_connect(
+ box,
+ "destroy",
+ G_CALLBACK(destroy_itemlist_container),
+ container);
+
+ return box;
+}
+
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CONTAINER_H
+#define CONTAINER_H
+
+#include "../ui/toolkit.h"
+#include "../ui/container.h"
+#include "toolkit.h"
+#include <string.h>
+
+#include <cx/allocator.h>
+#include <cx/hash_map.h>
+#include <cx/list.h>
+#include <cx/array_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout))
+
+
+typedef struct UiDocumentView UiDocumentView;
+
+typedef struct UiContainerPrivate UiContainerPrivate;
+struct UiContainerPrivate {
+ UiContainerX container;
+ GtkWidget *widget;
+ UIMENU menu;
+ GtkWidget *current; // TODO: remove
+
+ void (*add)(UiContainerPrivate*, GtkWidget*, UiLayout *layout);
+ UiLayout layout;
+
+ int close;
+};
+
+typedef struct UiBoxContainer {
+ UiContainerPrivate container;
+ UiSubContainerType type;
+ UiBool has_fill;
+} UiBoxContainer;
+
+typedef struct UiGridContainer {
+ UiContainerPrivate container;
+ UiBool def_hexpand;
+ UiBool def_vexpand;
+ UiBool def_hfill;
+ UiBool def_vfill;
+ int x;
+ int y;
+#ifdef UI_GTK2
+ int width;
+ int height;
+#endif
+} UiGridContainer;
+
+typedef struct UiTabViewContainer {
+ UiContainerPrivate container;
+} UiTabViewContainer;
+
+typedef void (*ui_select_tab_func)(UIWIDGET widget, int tab);
+typedef void (*ui_add_tab_func)(UIWIDGET widget, int index, const char *name, UIWIDGET child);
+
+typedef struct UiGtkTabView {
+ UiObject *obj;
+ GtkWidget *widget;
+ ui_select_tab_func select_tab;
+ ui_select_tab_func remove_tab;
+ ui_add_tab_func add_tab;
+ UiSubContainerType subcontainer;
+ int padding;
+ int spacing;
+ int columnspacing;
+ int rowspacing;
+ ui_callback onchange;
+ void *onchangedata;
+} UiGtkTabView;
+
+typedef struct UiSplitPane {
+ GtkWidget *current_pane;
+ CxList *children;
+ UiOrientation orientation;
+ int pos;
+ int max;
+ int nchildren;
+ int initial_position;
+} UiSplitPane;
+
+typedef struct UiSplitPaneContainer {
+ UiContainerPrivate container;
+ UiSplitPane *splitpane;
+} UiSplitPaneContainer;
+
+typedef struct UiHeaderbarContainer {
+ UiContainerPrivate container;
+ GtkWidget *centerbox;
+ int part;
+ UiHeaderbarAlternative alternative; /* only used by fallback headerbar */
+} UiHeaderbarContainer;
+
+typedef struct UiGtkItemListContainer {
+ UiObject *parent;
+ GtkWidget *widget;
+ UiContainerPrivate *container;
+ void (*create_ui)(UiObject *, int, void *, void *);
+ void *userdata;
+ UiSubContainerType subcontainer;
+ CxMap *current_items;
+ int margin;
+ int spacing;
+ int columnspacing;
+ int rowspacing;
+ bool remove_items;
+} UiGtkItemListContainer;
+
+GtkWidget* ui_gtk_vbox_new(int spacing);
+GtkWidget* ui_gtk_hbox_new(int spacing);
+
+GtkWidget* ui_subcontainer_create(
+ UiSubContainerType type,
+ UiObject *newobj,
+ int spacing,
+ int columnspacing,
+ int rowspacing,
+ int margin);
+
+GtkWidget* ui_gtk_set_margin(GtkWidget *widget, int margin, int margin_left, int margin_right, int margin_top, int margin_bottom);
+UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType type);
+
+UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type);
+void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing);
+UiContainerX* ui_grid_container(
+ UiObject *obj,
+ GtkWidget *grid,
+ UiBool def_hexpand,
+ UiBool def_vexpand,
+ UiBool def_hfill,
+ UiBool def_vfill);
+void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame);
+void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander);
+void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow);
+void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+UiContainerX* ui_tabview_container(UiObject *obj, GtkWidget *tabview);
+void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init);
+UiContainerX* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiSplitPane *data);
+void ui_splitpane_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+int64_t ui_splitpane_get(UiInteger *i);
+void ui_splitpane_set(UiInteger *i, int64_t value);
+
+UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview);
+
+void ui_gtk_notebook_select_tab(GtkWidget *widget, int tab);
+
+#if GTK_CHECK_VERSION(3, 10, 0)
+UiContainerX* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar);
+void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+#endif
+
+UiContainerX* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar);
+void ui_headerbar_fallback_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONTAINER_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "display.h"
+#include "container.h"
+#include "../common/context.h"
+#include "../common/object.h"
+#include "../ui/display.h"
+
+#include <cx/printf.h>
+
+static void set_alignment(GtkWidget *widget, float xalign, float yalign) {
+#if GTK_MAJOR_VERSION >= 4 || (GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16)
+ gtk_label_set_xalign(GTK_LABEL(widget), xalign);
+ gtk_label_set_yalign(GTK_LABEL(widget), yalign);
+#else
+ gtk_misc_set_alignment(GTK_MISC(widget), xalign, yalign);
+#endif
+}
+
+UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) {
+ const char *css_class = NULL;
+ char *markup = NULL;
+ if(args->label) {
+ #if GTK_MAJOR_VERSION < 3
+ switch(args->style) {
+ case UI_LABEL_STYLE_DEFAULT: break;
+ case UI_LABEL_STYLE_TITLE: {
+ cxmutstr m = cx_asprintf("<b>%s</b>", args->label);
+ markup = m.ptr;
+ args->label = NULL;
+ }
+ case UI_LABEL_STYLE_SUBTITLE: {
+ break;
+ }
+ case UI_LABEL_STYLE_DIM: {
+ break;
+ }
+ }
+# else
+ switch(args->style) {
+ case UI_LABEL_STYLE_DEFAULT: break;
+ case UI_LABEL_STYLE_TITLE: {
+ css_class = "ui_label_title";
+ break;
+ }
+ case UI_LABEL_STYLE_SUBTITLE: {
+ css_class = "subtitle";
+ break;
+ }
+ case UI_LABEL_STYLE_DIM: {
+ css_class = "dim-label";
+ break;
+ }
+ }
+# endif
+ }
+
+
+ GtkWidget *widget = gtk_label_new(args->label);
+ if(markup) {
+ gtk_label_set_markup(GTK_LABEL(widget), markup);
+ free(markup);
+ }
+
+ if(css_class) {
+ WIDGET_ADD_CSS_CLASS(widget, css_class);
+ }
+
+ 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: set_alignment(widget, .5, .5); break;
+ }
+
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiString* value = (UiString*)var->value;
+ value->obj = widget;
+ value->get = ui_label_get;
+ value->set = ui_label_set;
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ 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);
+}
+
+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;
+}
+
+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("");
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ return widget;
+}
+
+UIWIDGET ui_separator_deprecated(UiObject *obj) {
+#if GTK_MAJOR_VERSION >= 3
+ GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
+#else
+ GtkWidget *widget = gtk_hseparator_new();
+#endif
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ return widget;
+}
+*/
+
+/* ------------------------- progress bar ------------------------- */
+
+typedef struct UiProgressBarRange {
+ double min;
+ double max;
+} UiProgressBarRange;
+
+UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) {
+ GtkWidget *progressbar = gtk_progress_bar_new();
+ if(args->max > args->min) {
+ UiProgressBarRange *range = malloc(sizeof(UiProgressBarRange));
+ range->min = args->min;
+ range->max = args->max;
+ g_signal_connect(
+ progressbar,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ range);
+ g_object_set_data(G_OBJECT(progressbar), "ui_range", range);
+ }
+
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_DOUBLE);
+ if(var && var->value) {
+ UiDouble *value = var->value;
+ value->get = ui_progressbar_get;
+ value->set = ui_progressbar_set;
+ value->obj = progressbar;
+ ui_progressbar_set(value, value->value);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, progressbar, &layout);
+
+ return progressbar;
+}
+
+double ui_progressbar_get(UiDouble *d) {
+ UiProgressBarRange *range = g_object_get_data(d->obj, "ui_range");
+ double fraction = gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(d->obj));
+ if(range) {
+ fraction = range->min + (range->max - range->min) * fraction;
+ }
+ d->value = fraction;
+ return d->value;
+}
+
+void ui_progressbar_set(UiDouble *d, double value) {
+ d->value = value;
+ UiProgressBarRange *range = g_object_get_data(d->obj, "ui_range");
+ if(range) {
+ value = (value - range->min) / (range->max - range->min);
+ }
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(d->obj), value);
+}
+
+
+/* ------------------------- progress spinner ------------------------- */
+
+UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args) {
+ GtkWidget *spinner = gtk_spinner_new();
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var && var->value) {
+ UiInteger *value = var->value;
+ value->get = ui_spinner_get;
+ value->set = ui_spinner_set;
+ value->obj = spinner;
+ ui_spinner_set(value, value->value);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, spinner, &layout);
+
+ 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LABEL_H
+#define LABEL_H
+
+#include "../ui/toolkit.h"
+#include "toolkit.h"
+
+#ifdef __cplusplus
+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
+}
+#endif
+
+#endif /* LABEL_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dnd.h"
+#include <cx/buffer.h>
+#include <cx/array_list.h>
+
+#ifdef UI_GTK2LEGACY
+static gboolean selection_data_set_uris(GtkSelectionData *selection_data, char **uris) {
+ CxBuffer *buf = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ char *uri;
+ int i = 0;
+ while((uri = uris[i]) != NULL) {
+ cxBufferPutString(buf, uri);
+ cxBufferPutString(buf, "\r\n");
+ }
+ GdkAtom type = gdk_atom_intern("text/uri-list", FALSE);
+ gtk_selection_data_set(selection_data, type, 8, (guchar*)buf->space, buf->pos);
+ cxBufferFree(buf);
+ return TRUE;
+}
+static char** selection_data_get_uris(GtkSelectionData *selection_data) {
+ // TODO: implement
+ return NULL;
+}
+#define gtk_selection_data_set_uris selection_data_set_uris
+#define gtk_selection_data_get_uris selection_data_get_uris
+#endif
+
+/*
+void ui_selection_settext(UiSelection *sel, char *str, int len) {
+ // TODO: handle error?
+ gtk_selection_data_set_text(sel->data, str, len);
+}
+
+void ui_selection_seturis(UiSelection *sel, char **uris, int nelm) {
+ char **uriarray = calloc(nelm+1, sizeof(char*));
+ for(int i=0;i<nelm;i++) {
+ uriarray[i] = uris[i];
+ }
+ uriarray[nelm] = NULL;
+ gtk_selection_data_set_uris(sel->data, uriarray);
+}
+
+char* ui_selection_gettext(UiSelection *sel) {
+ guchar *text = gtk_selection_data_get_text(sel->data);
+ if(text) {
+ char *textcp = strdup((char*)text);
+ g_free(text);
+ return textcp;
+ }
+ return NULL;
+}
+
+char** ui_selection_geturis(UiSelection *sel, size_t *nelm) {
+ gchar **uris = gtk_selection_data_get_uris(sel->data);
+ if(uris) {
+ size_t al = 32;
+ char **array = malloc(al * sizeof(char*));
+ size_t i = 0;
+ while(uris[i] != NULL) {
+ if(i >= al) {
+ al *= 2;
+ array = realloc(array, al * sizeof(char*));
+ }
+ array[i] = strdup((char*)uris[i]);
+ i++;
+ }
+ *nelm = i;
+ g_strfreev(uris);
+ return array;
+ }
+ return NULL;
+}
+*/
+
+#if GTK_MAJOR_VERSION >= 4
+
+void ui_selection_settext(UiDnD *sel, char *str, int len) {
+ if(!sel->providers) {
+ return;
+ }
+
+ if(len == -1) {
+ len = strlen(str);
+ }
+ GBytes *bytes = g_bytes_new(str, len);
+ GdkContentProvider *provider = gdk_content_provider_new_for_bytes("text/plain;charset=utf-8", bytes);
+ g_bytes_unref(bytes);
+
+ cxListAdd(sel->providers, &provider);
+}
+
+void ui_selection_seturis(UiDnD *sel, char **uris, int nelm) {
+ if(!sel->providers) {
+ return;
+ }
+
+ GFile **files = calloc(nelm, sizeof(GFile*));
+ for(int i=0;i<nelm;i++) {
+ GFile *file = uris[i][0] == '/' ? g_file_new_for_path(uris[i]) : g_file_new_for_uri(uris[i]);
+ files[i] = file;
+ }
+ GdkFileList *list = gdk_file_list_new_from_array(files, nelm);
+
+ GdkContentProvider *provider = gdk_content_provider_new_typed(GDK_TYPE_FILE_LIST, list);
+ cxListAdd(sel->providers, &provider);
+
+ g_slist_free_full ((GSList*)list, g_object_unref);
+ free(files);
+}
+
+char* ui_selection_gettext(UiDnD *sel) {
+ if(!sel->value) {
+ return NULL;
+ }
+
+ if(G_VALUE_HOLDS(sel->value, G_TYPE_STRING)) {
+ const char *str = g_value_get_string(sel->value);
+ return str ? strdup(str) : NULL;
+ }
+
+ return NULL;
+}
+
+UiFileList ui_selection_geturis(UiDnD *sel) {
+ if(!sel->value) {
+ return (UiFileList){NULL,0};
+ }
+
+ if(G_VALUE_HOLDS(sel->value, GDK_TYPE_FILE_LIST)) {
+ GSList *list = g_value_get_boxed(sel->value);
+ if(!list) {
+ return (UiFileList){NULL,0};
+ }
+ guint size = g_slist_length(list);
+
+ UiFileList flist;
+ flist.nfiles = size;
+ flist.files = calloc(size, sizeof(char*));
+ int i=0;
+ while(list) {
+ GFile *file = list->data;
+ char *uri = g_file_get_uri(file);
+ flist.files[i++] = strdup(uri);
+ g_free(uri);
+ list = list->next;
+ }
+ return flist;
+ }
+ return (UiFileList){NULL,0};
+}
+
+
+UiDnD* ui_create_dnd(void) {
+ UiDnD *dnd = malloc(sizeof(UiDnD));
+ memset(dnd, 0, sizeof(UiDnD));
+ dnd->providers = cxArrayListCreateSimple(sizeof(void*), 16);
+ dnd->selected_action = 0;
+ dnd->delete = FALSE;
+ return dnd;
+}
+
+void ui_dnd_free(UiDnD *dnd) {
+ cxListFree(dnd->providers);
+ free(dnd);
+}
+
+UiDnDAction ui_dnd_result(UiDnD *dnd) {
+ switch(dnd->selected_action) {
+ case 0: return UI_DND_ACTION_NONE;
+ case GDK_ACTION_COPY: return UI_DND_ACTION_COPY;
+ case GDK_ACTION_MOVE: return UI_DND_ACTION_MOVE;
+ case GDK_ACTION_LINK: return UI_DND_ACTION_LINK;
+ default: break;
+ }
+ return UI_DND_ACTION_CUSTOM;
+}
+
+#else
+
+void ui_selection_settext(UiDnD *sel, char *str, int len) {
+ gtk_selection_data_set_text(sel->data, str, len);
+}
+
+void ui_selection_seturis(UiDnD *sel, char **uris, int nelm) {
+ char **uriarray = calloc(nelm+1, sizeof(char*));
+ for(int i=0;i<nelm;i++) {
+ uriarray[i] = uris[i];
+ }
+ uriarray[nelm] = NULL;
+ gtk_selection_data_set_uris(sel->data, uriarray);
+ free(uriarray);
+}
+
+char* ui_selection_gettext(UiDnD *sel) {
+ if(!sel->data) {
+ return NULL;
+ }
+
+ guchar *text = gtk_selection_data_get_text(sel->data);
+ if(text) {
+ char *textcp = strdup((char*)text);
+ g_free(text);
+ return textcp;
+ }
+ return NULL;
+}
+
+UiFileList ui_selection_geturis(UiDnD *sel) {
+ if(!sel->data) {
+ return (UiFileList){NULL,0};
+ }
+
+ gchar **uris = gtk_selection_data_get_uris(sel->data);
+ if(uris) {
+ size_t al = 32;
+ char **array = malloc(al * sizeof(char*));
+ size_t i = 0;
+ while(uris[i] != NULL) {
+ if(i >= al) {
+ al *= 2;
+ array = realloc(array, al * sizeof(char*));
+ }
+ array[i] = strdup((char*)uris[i]);
+ i++;
+ }
+ g_strfreev(uris);
+ return (UiFileList){array,i};
+ }
+
+ return (UiFileList){NULL,0};
+}
+
+UiDnDAction ui_dnd_result(UiDnD *dnd) {
+ switch(dnd->selected_action) {
+ case 0: return UI_DND_ACTION_NONE;
+ case GDK_ACTION_COPY: return UI_DND_ACTION_COPY;
+ case GDK_ACTION_MOVE: return UI_DND_ACTION_MOVE;
+ case GDK_ACTION_LINK: return UI_DND_ACTION_LINK;
+ default: break;
+ }
+ return UI_DND_ACTION_CUSTOM;
+}
+
+
+UiDnD* ui_create_dnd(void) {
+ UiDnD *dnd = malloc(sizeof(UiDnD));
+ memset(dnd, 0, sizeof(UiDnD));
+ return dnd;
+}
+
+void ui_dnd_free(UiDnD *dnd) {
+ free(dnd);
+}
+
+#endif
+
+UiBool ui_dnd_need_delete(UiDnD *dnd) {
+ return dnd->delete;
+}
+
+void ui_dnd_accept(UiDnD *dnd, UiBool accept) {
+ dnd->accept = accept;
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DND_H
+#define DND_H
+
+#include "../ui/dnd.h"
+#include "toolkit.h"
+
+#include <cx/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if GTK_MAJOR_VERSION >= 4
+
+struct UiDnD {
+ GtkDropTarget *target;
+ const GValue *value;
+ CxList *providers;
+ GdkDragAction selected_action;
+ gboolean delete;
+ gboolean accept;
+};
+
+#else
+
+struct UiDnD {
+ GdkDragContext *context;
+ GtkSelectionData *data;
+ GdkDragAction selected_action;
+ gboolean delete;
+ gboolean accept;
+};
+
+#endif
+
+UiDnD* ui_create_dnd(void);
+void ui_dnd_free(UiDnD *dnd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DND_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "container.h"
+
+#include "draw_cairo.h"
+
+
+#if GTK_MAJOR_VERSION >= 3
+void ui_drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height) {
+ UiCairoGraphics g;
+ g.g.width = width;
+ g.g.height = height;
+ g.widget = drawingarea->widget;;
+ g.cr = cr;
+
+ UiEvent event;
+ event.obj = drawingarea->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = 0;
+ event.set = 0;
+
+ if(drawingarea->draw) {
+ drawingarea->draw(&event, &g.g, drawingarea->drawdata);
+ }
+}
+#endif
+
+/*
+#if UI_GTK3
+gboolean ui_drawingarea_expose(GtkWidget *w, cairo_t *cr, void *data) {
+ int width = gtk_widget_get_allocated_width(w);
+ int height = gtk_widget_get_allocated_height(w);
+ ui_drawingarea_draw(GTK_DRAWING_AREA(w), cr, width, height, data);
+ return FALSE;
+}
+#endif
+#ifdef UI_GTK2
+gboolean ui_canvas_expose(GtkWidget *w, GdkEventExpose *e, void *data) {
+ UiCairoGraphics g;
+ g.g.width = w->allocation.width;
+ g.g.height = w->allocation.height;
+ g.widget = w;
+ g.cr = gdk_cairo_create(w->window);
+
+ UiDrawEvent *event = data;
+ UiEvent ev;
+ ev.obj = event->obj;
+ ev.window = event->obj->window;
+ ev.document = event->obj->ctx->document;
+
+ event->callback(&ev, &g.g, event->userdata);
+
+ return FALSE;
+}
+#endif
+*/
+
+// function from graphics.h
+
+/*
+void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) {
+#if GTK_MAJOR_VERSION >= 4
+ gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), ui_drawingarea_draw, event, NULL);
+#elif GTK_MAJOR_VERSION == 3
+ g_signal_connect(G_OBJECT(widget),
+ "draw",
+ G_CALLBACK(ui_drawingarea_expose),
+ event);
+#else
+ g_signal_connect(G_OBJECT(widget),
+ "expose_event",
+ G_CALLBACK(ui_canvas_expose),
+ event);
+#endif
+}
+*/
+
+PangoContext *ui_get_pango_context(UiGraphics *g) {
+ UiCairoGraphics *gr = (UiCairoGraphics*)g;
+ //return gtk_widget_get_pango_context(gr->widget);
+ return pango_cairo_create_context(gr->cr);
+}
+
+
+// drawing functions
+void ui_graphics_color(UiGraphics *g, int red, int green, int blue) {
+ UiCairoGraphics *gr = (UiCairoGraphics*)g;
+ double dred = (double)red / (double)255;
+ double dgreen = (double)green / (double)255;
+ double dblue = (double)blue / (double)255;
+ cairo_set_source_rgb(gr->cr, dred, dgreen, dblue);
+}
+
+
+void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2) {
+ UiCairoGraphics *gr = (UiCairoGraphics*)g;
+ cairo_set_line_width(gr->cr, 1);
+ cairo_move_to(gr->cr, (double)x1 + 0.5, (double)y1 + 0.5);
+ cairo_line_to(gr->cr, (double)x2 + 0.5, (double)y2 + 0.5);
+ cairo_stroke(gr->cr);
+}
+
+void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, UiBool fill) {
+ UiCairoGraphics *gr = (UiCairoGraphics*)g;
+ cairo_set_line_width(gr->cr, 1);
+ cairo_rectangle(gr->cr, x + 0.5, y + 0.5 , w, h);
+ if(fill) {
+ cairo_fill(gr->cr);
+ } else {
+ cairo_stroke(gr->cr);
+ }
+}
+
+void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text) {
+ UiCairoGraphics *gr = (UiCairoGraphics*)g;
+ cairo_move_to(gr->cr, x, y);
+ pango_cairo_show_layout(gr->cr, text->layout);
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DRAW_CAIRO_H
+#define DRAW_CAIRO_H
+
+#include "graphics.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiCairoGraphics {
+ UiGraphics g;
+ GtkWidget *widget;
+ cairo_t *cr;
+} UiCairoGraphics;
+
+void ui_drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DRAW_CAIRO_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "container.h"
+
+#include "draw_gdk.h"
+
+
+gboolean ui_drawingarea_expose(GtkWidget *w, GdkEventExpose *e, void *data) {
+ UiGdkGraphics g;
+ g.g.width = w->allocation.width;
+ g.g.height = w->allocation.height;
+ g.widget = w;
+ g.gc = gdk_gc_new(w->window);
+
+ UiDrawEvent *event = data;
+ UiEvent ev;
+ ev.obj = event->obj;
+ ev.window = event->obj->window;
+ ev.document = event->obj->ctx->document;
+
+ event->callback(&ev, &g.g, event->userdata);
+
+ return FALSE;
+}
+
+// function from graphics.h
+
+void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) {
+ g_signal_connect(G_OBJECT(widget),
+ "expose_event",
+ G_CALLBACK(ui_drawingarea_expose),
+ event);
+}
+
+PangoContext *ui_get_pango_context(UiGraphics *g) {
+ UiGdkGraphics *gr = (UiGdkGraphics*)g;
+ return gtk_widget_get_pango_context(gr->widget);
+}
+
+// drawing functions
+void ui_graphics_color(UiGraphics *g, int red, int green, int blue) {
+ UiGdkGraphics *gr = (UiGdkGraphics*)g;
+ GdkColor color;
+ color.red = red * 257;
+ color.green = green * 257;
+ color.blue = blue * 257;
+ gdk_gc_set_rgb_fg_color(gr->gc, &color);
+ //gdk_gc_set_rgb_bg_color(g->gc, &color);
+}
+
+void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2) {
+ UiGdkGraphics *gr = (UiGdkGraphics*)g;
+ gdk_draw_line(gr->widget->window, gr->gc, x1, y1, x2, y2);
+}
+
+void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill) {
+ UiGdkGraphics *gr = (UiGdkGraphics*)g;
+ gdk_draw_rectangle(gr->widget->window, gr->gc, fill, x, y, w, h);
+}
+
+void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text) {
+ UiGdkGraphics *gr = (UiGdkGraphics*)g;
+ gdk_draw_layout(gr->widget->window, gr->gc, x, y, text->layout);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DRAW_GDK_H
+#define DRAW_GDK_H
+
+#include "graphics.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiGdkGraphics {
+ UiGraphics g;
+ GtkWidget *widget;
+ GdkGC *gc;
+} UiGdkGraphics;
+
+gboolean ui_canvas_expose(GtkWidget *w, GdkEventExpose *e, void *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DRAW_GDK_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../common/context.h"
+#include "../common/object.h"
+#include "container.h"
+#include "entry.h"
+
+
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
+ double min = args->min;
+ double max = args->max != 0 ? args->max : 1000;
+
+ UiVar *var = NULL;
+ UiVarType vartype = 0;
+ if(args->varname) {
+ var = uic_get_var(obj->ctx, args->varname);
+ if(var) {
+ vartype = var->type;
+ } else {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, args->varname, UI_VAR_RANGE);
+ vartype = UI_VAR_RANGE;
+ }
+ }
+
+ if(!var) {
+ if(args->intvalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+ vartype = UI_VAR_INTEGER;
+ } else if(args->doublevalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+ vartype = UI_VAR_DOUBLE;
+ } else if(args->rangevalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+ vartype = UI_VAR_RANGE;
+ }
+ }
+
+ if(vartype == UI_VAR_RANGE) {
+ UiRange *r = var->value;
+ min = r->min;
+ max = r->max;
+ }
+ 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, args->step);
+ ui_set_name_and_style(spin, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, spin, args->groups);
+
+ if(args->width > 0) {
+ gtk_widget_set_size_request(spin, args->width, -1);
+ }
+
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), args->digits);
+ UiObserver **obs = NULL;
+ if(var) {
+ double value = 0;
+ switch(vartype) {
+ default: break;
+ case UI_VAR_INTEGER: {
+ UiInteger *i = var->value;
+ i->get = ui_spinbutton_getint;
+ i->set = ui_spinbutton_setint;
+ i->obj = spin;
+ value = (double)i->value;
+ obs = &i->observers;
+ break;
+ }
+ case UI_VAR_DOUBLE: {
+ UiDouble *d = var->value;
+ d->get = ui_spinbutton_getdouble;
+ d->set = ui_spinbutton_setdouble;
+ d->obj = spin;
+ value = d->value;
+ obs = &d->observers;
+ break;
+ }
+ case UI_VAR_RANGE: {
+ UiRange *r = var->value;
+ r->get = ui_spinbutton_getrangeval;
+ r->set = ui_spinbutton_setrangeval;
+ r->setrange = ui_spinbutton_setrange;
+ r->setextent = ui_spinbutton_setextent;
+ r->obj = spin;
+ value = r->value;
+ obs = &r->observers;
+ break;
+ }
+ }
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value);
+ }
+
+ 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);
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, spin, &layout);
+
+ return spin;
+}
+
+void ui_spinner_setrange(UIWIDGET spinner, double min, double max) {
+ gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinner), min, max);
+}
+
+void ui_spinner_setdigits(UIWIDGET spinner, int digits) {
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner), digits);
+}
+
+
+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 = NULL;
+ e.eventdatatype = 0;
+ e.intval = (int64_t)value;
+
+ if(event->callback) {
+ event->callback(&e, event->userdata);
+ }
+
+ if(event->observers) {
+ UiObserver *observer = *event->observers;
+ ui_notify_evt(observer, &e);
+ }
+}
+
+
+int64_t ui_spinbutton_getint(UiInteger *i) {
+ i->value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(i->obj));
+ return i->value;
+}
+
+void ui_spinbutton_setint(UiInteger *i, int64_t val) {
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->obj), (double)val);
+ i->value = val;
+}
+
+double ui_spinbutton_getdouble(UiDouble *d) {
+ d->value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(d->obj));
+ return d->value;
+}
+
+void ui_spinbutton_setdouble(UiDouble *d, double val) {
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->obj), val);
+ d->value = val;
+}
+
+double ui_spinbutton_getrangeval(UiRange *r) {
+ r->value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(r->obj));
+ return r->value;
+}
+
+void ui_spinbutton_setrangeval(UiRange *r, double val) {
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->obj), val);
+ r->value = val;
+}
+void ui_spinbutton_setrange(UiRange *r, double min, double max) {
+ gtk_spin_button_set_range(GTK_SPIN_BUTTON(r->obj), min, max);
+ r->min = min;
+ r->max = max;
+}
+
+void ui_spinbutton_setextent(UiRange *r, double extent) {
+ gtk_spin_button_set_increments(GTK_SPIN_BUTTON(r->obj), extent, extent*10);
+ r->extent = extent;
+}
--- /dev/null
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/*
+ * File: entry.h
+ * Author: olaf
+ *
+ * Created on 11. November 2017, 13:38
+ */
+
+#ifndef ENTRY_H
+#define ENTRY_H
+
+#include "toolkit.h"
+#include "../ui/entry.h"
+#include "../common/context.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ui_spinner_changed(GtkSpinButton *spinner, UiVarEventData *event);
+
+int64_t ui_spinbutton_getint(UiInteger *i);
+void ui_spinbutton_setint(UiInteger *i, int64_t val);
+
+double ui_spinbutton_getdouble(UiDouble *d);
+void ui_spinbutton_setdouble(UiDouble *d, double val);
+
+double ui_spinbutton_getrangeval(UiRange *r);
+void ui_spinbutton_setrangeval(UiRange *r, double val);
+void ui_spinbutton_setrange(UiRange *r, double min, double max);
+void ui_spinbutton_setextent(UiRange *r, double extent);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ENTRY_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "graphics.h"
+#include "container.h"
+#include "../common/object.h"
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+#include "draw_cairo.h"
+#endif
+
+static void destroy_drawingarea(GtkWidget *widget, UiDrawingArea *drawingarea) {
+ free(drawingarea);
+}
+
+static void drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height) {
+ UiEvent event;
+ event.obj = drawingarea->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = 0;
+ event.set = 0;
+
+
+}
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void drawfunc(
+ GtkDrawingArea *area,
+ cairo_t *cr,
+ int width,
+ int height,
+ gpointer userdata)
+{
+ ui_drawingarea_draw(userdata, cr, width, height);
+}
+#elif GTK_CHECK_VERSION(3, 0, 0)
+gboolean draw_callback(GtkWidget *widget, cairo_t *cr, UiDrawingArea *drawingarea) {
+ int width = gtk_widget_get_allocated_width(widget);
+ int height = gtk_widget_get_allocated_height(widget);
+ ui_drawingarea_draw(drawingarea, cr, width, height);
+ return FALSE;
+}
+#endif
+
+UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args) {
+ GtkWidget *widget = gtk_drawing_area_new();
+ ui_set_name_and_style(widget, args->name, args->style_class);
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+ gtk_drawing_area_set_content_width(GTK_DRAWING_AREA(widget), args->width > 0 ? args->width : 100);
+ gtk_drawing_area_set_content_height(GTK_DRAWING_AREA(widget), args->height > 0 ? args->height : 100);
+#else
+ int w = args->width > 0 ? args->width : 100;
+ int h = args->height > 0 ? args->height : 100;
+ gtk_widget_set_size_request(widget, w, h);
+#endif
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ UiDrawingArea *drawingarea = malloc(sizeof(UiDrawingArea));
+ drawingarea->obj = obj;
+ drawingarea->widget = widget;
+ drawingarea->draw = args->draw;
+ drawingarea->drawdata = args->drawdata;
+ drawingarea->onclick = args->onclick;
+ drawingarea->onclickdata = args->onclickdata;
+ drawingarea->onmotion = args->onmotion;
+ drawingarea->onmotiondata = args->onmotiondata;
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+ gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), drawfunc, drawingarea, NULL);
+#elif GTK_CHECK_VERSION(3, 0, 0)
+ g_signal_connect(
+ widget,
+ "draw",
+ G_CALLBACK(draw_callback),
+ NULL);
+#endif
+
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(destroy_drawingarea),
+ drawingarea);
+
+ return widget;
+}
+
+
+#if GTK_MAJOR_VERSION <= 3
+static gboolean widget_button_pressed(
+ GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata)
+{
+ UiEventData *eventdata = userdata;
+
+ UiMouseEvent me;
+ me.x = (int)event->button.x;
+ me.y = (int)event->button.y;
+
+ int exec = 0;
+ if(event->button.type == GDK_BUTTON_PRESS) {
+ exec = 1;
+ me.type = UI_PRESS;
+ } else if(event->button.type == GDK_2BUTTON_PRESS) {
+ exec = 1;
+ me.type = UI_PRESS2;
+ }
+
+ if(exec) {
+ UiEvent e;
+ e.obj = eventdata->obj;
+ e.window = eventdata->obj->window;
+ e.document = eventdata->obj->ctx->document;
+ e.eventdata = &me;
+ e.intval = 0;
+ eventdata->callback(&e, eventdata->userdata);
+ }
+ return TRUE;
+}
+#endif
+
+void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) {
+#if GTK_MAJOR_VERSION >= 4
+ *width = gtk_widget_get_width(drawingarea);
+ *height = gtk_widget_get_height(drawingarea);
+#elif GTK_MAJOR_VERSION == 3
+ *width = gtk_widget_get_allocated_width(drawingarea);
+ *height = gtk_widget_get_allocated_height(drawingarea);
+#else
+ *width = drawingarea->allocation.width;
+ *height = drawingarea->allocation.height;
+#endif
+}
+
+void ui_drawingarea_redraw(UIWIDGET drawingarea) {
+ gtk_widget_queue_draw(drawingarea);
+}
+
+void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u) {
+#if GTK_MAJOR_VERSION >= 4
+ // TODO
+#else
+ gtk_widget_set_events(widget, GDK_BUTTON_PRESS_MASK);
+ if(f) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->callback = f;
+ event->userdata = u;
+ event->customdata = NULL;
+ event->value = 0;
+
+ g_signal_connect(G_OBJECT(widget),
+ "button-press-event",
+ G_CALLBACK(widget_button_pressed),
+ event);
+ } else {
+ // TODO: warning
+ }
+#endif
+}
+
+
+// text layout
+UiTextLayout* ui_text(UiGraphics *g) {
+ UiTextLayout *layout = malloc(sizeof(UiTextLayout));
+ PangoContext *pc = ui_get_pango_context(g);
+ layout->layout = pango_layout_new(pc);
+ return layout;
+}
+
+void ui_text_setstring(UiTextLayout *layout, char *str) {
+ pango_layout_set_text(layout->layout, str, -1);
+}
+
+void ui_text_setstringl(UiTextLayout *layout, char *str, int len) {
+ pango_layout_set_text(layout->layout, str, len);
+}
+
+void ui_text_setfont(UiTextLayout *layout, const char *font, int size) {
+ PangoFontDescription *fontDesc;
+ fontDesc = pango_font_description_from_string(font);
+ pango_font_description_set_size(fontDesc, size * PANGO_SCALE);
+ pango_layout_set_font_description(layout->layout, fontDesc);
+ pango_font_description_free(fontDesc);
+}
+
+void ui_text_getsize(UiTextLayout *layout, int *width, int *height) {
+ pango_layout_get_size(layout->layout, width, height);
+ *width = *width / PANGO_SCALE;
+ *height = *height / PANGO_SCALE;
+}
+
+void ui_text_setwidth(UiTextLayout *layout, int width) {
+ pango_layout_set_width(layout->layout, width * PANGO_SCALE);
+ pango_layout_set_ellipsize(layout->layout, PANGO_ELLIPSIZE_END);
+ //pango_layout_set_wrap(layout->layout, PANGO_WRAP_WORD_CHAR);
+}
+
+void ui_text_free(UiTextLayout *text) {
+ g_object_unref(text->layout);
+ free(text);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DRAWINGAREA_H
+#define DRAWINGAREA_H
+
+#include "../ui/graphics.h"
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct UiDrawingArea {
+ UiObject *obj;
+ GtkWidget *widget;
+ ui_drawfunc draw;
+ void *drawdata;
+ ui_callback onclick;
+ void *onclickdata;
+ ui_callback onmotion;
+ void *onmotiondata;
+} UiDrawingArea;
+
+struct UiTextLayout {
+ PangoLayout *layout;
+};
+
+// implemented in draw_*.h
+//void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event);
+PangoContext *ui_get_pango_context(UiGraphics *g);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DRAWINGAREA_H */
+
--- /dev/null
+/*
+ * 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 "headerbar.h"
+
+#include "button.h"
+#include "menu.h"
+
+#include "../ui/properties.h"
+
+#if GTK_CHECK_VERSION(3, 10, 0)
+
+void ui_fill_headerbar(UiObject *obj, GtkWidget *sidebar_headerbar, GtkWidget *main_headerbar, GtkWidget *right_headerbar) {
+ CxList *sidebar_left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_LEFT);
+ CxList *sidebar_right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_RIGHT);
+
+ CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
+ CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
+ CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
+
+ CxList *rightpanel_left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_LEFT);
+ CxList *rightpanel_center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_CENTER);
+ CxList *rightpanel_right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_RIGHT);
+
+ UiToolbarMenuItem *appmenu = uic_get_appmenu();
+ const char *appmenu_pos_str = ui_get_property("ui.gtk.window.appmenu.position");
+ int appmenu_pos = UI_TOOLBAR_RIGHT;
+ if(sidebar_headerbar) {
+ appmenu_pos = UI_TOOLBAR_SIDEBAR_RIGHT;
+ } else if(right_headerbar) {
+ appmenu_pos = UI_TOOLBAR_RIGHTPANEL_RIGHT;
+ }
+ if(appmenu_pos_str) {
+ if(!strcmp(appmenu_pos_str, "sidebar") && sidebar_headerbar) {
+ appmenu_pos = UI_TOOLBAR_SIDEBAR_RIGHT;
+ } else if(!strcmp(appmenu_pos_str, "main")) {
+ appmenu_pos = UI_TOOLBAR_RIGHT;
+ } else if(!strcmp(appmenu_pos_str, "rightpanel") && right_headerbar) {
+ appmenu_pos = UI_TOOLBAR_RIGHTPANEL_RIGHT;
+ }
+ }
+
+ // main toolbar
+ ui_headerbar_add_items(obj, main_headerbar, left_defaults, UI_TOOLBAR_LEFT);
+ ui_headerbar_add_items(obj, main_headerbar, center_defaults, UI_TOOLBAR_CENTER);
+
+ if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHT) {
+ ui_add_headerbar_menu(main_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
+ }
+ ui_headerbar_add_items(obj, main_headerbar, right_defaults, UI_TOOLBAR_RIGHT);
+
+ // sidebar
+ if(sidebar_headerbar) {
+ // ui_headerbar_add_items pos parameter uses only UI_TOOLBAR_LEFT, UI_TOOLBAR_CENTER, UI_TOOLBAR_RIGHT
+ ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_left_defaults, UI_TOOLBAR_LEFT);
+
+ if(appmenu && appmenu_pos == UI_TOOLBAR_SIDEBAR_RIGHT) {
+ ui_add_headerbar_menu(sidebar_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
+ }
+ ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_right_defaults, UI_TOOLBAR_RIGHT);
+ }
+
+ // right panel
+ if(right_headerbar) {
+ ui_headerbar_add_items(obj, right_headerbar, rightpanel_left_defaults, UI_TOOLBAR_LEFT);
+ ui_headerbar_add_items(obj, right_headerbar, rightpanel_center_defaults, UI_TOOLBAR_CENTER);
+
+ if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHTPANEL_RIGHT) {
+ ui_add_headerbar_menu(right_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
+ }
+ ui_headerbar_add_items(obj, right_headerbar, rightpanel_right_defaults, UI_TOOLBAR_RIGHT);
+ }
+}
+
+static void create_item(UiObject *obj, GtkWidget *headerbar, GtkWidget *box, UiToolbarItemI *i, enum UiToolbarPos pos) {
+ switch(i->type) {
+ case UI_TOOLBAR_ITEM: {
+ ui_add_headerbar_item(headerbar, box, (UiToolbarItem*)i, obj, pos);
+ break;
+ }
+ case UI_TOOLBAR_TOGGLEITEM: {
+ ui_add_headerbar_toggleitem(headerbar, box, (UiToolbarToggleItem*)i, obj, pos);
+ break;
+ }
+ case UI_TOOLBAR_MENU: {
+ ui_add_headerbar_menu(headerbar, box, (UiToolbarMenuItem*)i, obj, pos);
+ break;
+ }
+ default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type);
+ }
+}
+
+static void headerbar_add(GtkWidget *headerbar, GtkWidget *box, GtkWidget *item, enum UiToolbarPos pos) {
+ switch(pos) {
+ case UI_TOOLBAR_LEFT: {
+ UI_HEADERBAR_PACK_START(headerbar, item);
+ break;
+ }
+ case UI_TOOLBAR_CENTER: {
+
+#if GTK_MAJOR_VERSION >= 4
+ gtk_box_append(GTK_BOX(box), item);
+#else
+ gtk_box_pack_start(GTK_BOX(box), item, 0, 0, 0);
+#endif
+ break;
+ }
+ case UI_TOOLBAR_RIGHT: {
+ UI_HEADERBAR_PACK_END(headerbar, item);
+ break;
+ }
+ }
+}
+
+void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos) {
+ GtkWidget *box = NULL;
+
+ if(pos == UI_TOOLBAR_CENTER && cxListSize(items) > 0) {
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ UI_HEADERBAR_SET_TITLE_WIDGET(headerbar, box);
+ }
+
+ CxIterator i = pos == UI_TOOLBAR_RIGHT ? cxListBackwardsIterator(items) : cxListIterator(items);
+ 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, headerbar, box, item, pos);
+ }
+}
+
+void ui_add_headerbar_item(
+ GtkWidget *headerbar,
+ GtkWidget *box,
+ UiToolbarItem *item,
+ UiObject *obj,
+ enum UiToolbarPos pos)
+{
+ GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE);
+ ui_set_widget_groups(obj->ctx, button, item->args.groups);
+ ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
+ WIDGET_ADD_CSS_CLASS(button, "flat");
+ headerbar_add(headerbar, box, button, pos);
+}
+
+void ui_add_headerbar_toggleitem(
+ GtkWidget *headerbar,
+ GtkWidget *box,
+ UiToolbarToggleItem *item,
+ UiObject *obj,
+ enum UiToolbarPos pos)
+{
+ GtkWidget *button = gtk_toggle_button_new();
+ ui_set_widget_groups(obj->ctx, button, item->args.groups);
+ ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
+ WIDGET_ADD_CSS_CLASS(button, "flat");
+ ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.tooltip, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0);
+ headerbar_add(headerbar, box, button, pos);
+}
+
+void ui_add_headerbar_menu(
+ GtkWidget *headerbar,
+ GtkWidget *box,
+ UiToolbarMenuItem *item,
+ UiObject *obj,
+ enum UiToolbarPos pos)
+{
+
+
+#if GTK_MAJOR_VERSION >= 4
+ GtkWidget *menubutton = gtk_menu_button_new();
+ ui_set_widget_visibility_states(obj->ctx, menubutton, item->args.visibility_states);
+ if(item->args.label) {
+ gtk_menu_button_set_label(GTK_MENU_BUTTON(menubutton), item->args.label);
+ }
+ if(item->args.icon) {
+ gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(menubutton), item->args.icon);
+ }
+
+ if(!item->args.label && !item->args.icon) {
+ gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(menubutton), "open-menu-symbolic");
+ }
+
+ GMenu *menu = g_menu_new();
+ ui_gmenu_add_menu_items(menu, 0, &item->menu, obj);
+
+ gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton), G_MENU_MODEL(menu));
+#else
+ GtkWidget *menubutton = gtk_menu_button_new();
+ GtkWidget *menu = gtk_menu_new();
+ ui_add_menu_items(menu, 0, &item->menu, obj);
+ gtk_widget_show_all(menu);
+ gtk_menu_button_set_popup(GTK_MENU_BUTTON(menubutton), menu);
+
+#endif
+
+ headerbar_add(headerbar, box, menubutton, pos);
+}
+
+#endif // GTK_CHECK_VERSION(3, 10, 0)
--- /dev/null
+/*
+ * 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 HEADERBAR_H
+#define HEADERBAR_H
+
+#include "toolkit.h"
+#include "../ui/toolbar.h"
+#include "../common/toolbar.h"
+#include <cx/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if GTK_CHECK_VERSION(3, 10, 0)
+
+#ifdef UI_LIBADWAITA
+#define UI_HEADERBAR AdwHeaderBar*
+#define UI_HEADERBAR_CAST(h) ADW_HEADER_BAR(h)
+#define UI_HEADERBAR_PACK_START(h, w) adw_header_bar_pack_start(ADW_HEADER_BAR(h), w)
+#define UI_HEADERBAR_PACK_END(h, w) adw_header_bar_pack_end(ADW_HEADER_BAR(h), w)
+#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) adw_header_bar_set_title_widget(ADW_HEADER_BAR(h), w)
+#else
+#define UI_HEADERBAR GtkHeaderBar*
+#define UI_HEADERBAR_CAST(h) GTK_HEADER_BAR(h)
+#define UI_HEADERBAR_PACK_START(h, w) gtk_header_bar_pack_start(GTK_HEADER_BAR(h), w)
+#define UI_HEADERBAR_PACK_END(h, w) gtk_header_bar_pack_end(GTK_HEADER_BAR(h), w)
+#if GTK_MAJOR_VERSION >= 4
+#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_title_widget(GTK_HEADER_BAR(h), w)
+#else
+#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_custom_title(GTK_HEADER_BAR(h), w)
+#endif
+#endif
+
+void ui_fill_headerbar(UiObject *obj, GtkWidget *sidebar_headerbar, GtkWidget *main_headerbar, GtkWidget *right_headerbar);
+
+void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos);
+
+void ui_add_headerbar_item(
+ GtkWidget *headerbar,
+ GtkWidget *box,
+ UiToolbarItem *item,
+ UiObject *obj,
+ enum UiToolbarPos pos);
+
+void ui_add_headerbar_toggleitem(
+ GtkWidget *headerbar,
+ GtkWidget *box,
+ UiToolbarToggleItem *item,
+ UiObject *obj,
+ enum UiToolbarPos pos);
+
+void ui_add_headerbar_menu(
+ GtkWidget *headerbar,
+ GtkWidget *box,
+ UiToolbarMenuItem *item,
+ UiObject *obj,
+ enum UiToolbarPos pos);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADERBAR_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cx/map.h>
+
+#include "toolkit.h"
+#include "icon.h"
+#include "../common/properties.h"
+
+static CxMap *image_map;
+
+static GtkIconTheme *icon_theme;
+
+#if GTK_MAJOR_VERSION >= 4
+#define ICONTHEME_GET_DEFAULT() gtk_icon_theme_get_for_display(gdk_display_get_default())
+#else
+#define ICONTHEME_GET_DEFAULT() gtk_icon_theme_get_default()
+#endif
+
+void ui_image_init(void) {
+ image_map = cxHashMapCreateSimple(CX_STORE_POINTERS);
+
+ icon_theme = ICONTHEME_GET_DEFAULT();
+}
+
+// **** deprecated functions ****
+
+GdkPixbuf* ui_get_image(const char *name) {
+ UiImage *img = cxMapGet(image_map, name);
+ if(img) {
+ return img->pixbuf;
+ } else {
+ //ui_add_image(name, name);
+ //return ucx_map_cstr_get(image_map, name);
+ // TODO
+ return NULL;
+ }
+}
+
+// **** deprecated2****
+
+static UiIcon* get_icon(const char *name, int size, int scale) {
+#if GTK_MAJOR_VERSION >= 4
+ GtkIconPaintable *info = gtk_icon_theme_lookup_icon(icon_theme, name, NULL, size, scale, GTK_TEXT_DIR_LTR, GTK_ICON_LOOKUP_FORCE_REGULAR);
+#elif defined(UI_SUPPORTS_SCALE)
+ GtkIconInfo *info = gtk_icon_theme_lookup_icon_for_scale(icon_theme, name, size, scale, 0);
+#else
+ GtkIconInfo *info = gtk_icon_theme_lookup_icon(icon_theme, name, size, 0);
+#endif
+ if(info) {
+ UiIcon *icon = malloc(sizeof(UiIcon));
+ icon->info = info;
+ icon->pixbuf = NULL;
+ return icon;
+ }
+ return NULL;
+}
+
+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) {
+ UiIcon *icon = ui_icon("file", size);
+#if GTK_MAJOR_VERSION >= 4
+ GFile *file = gtk_icon_paintable_get_file(icon->info);
+ char *path = g_file_get_path(file);
+ if(!path) {
+ icon = ui_icon("application-x-generic", size);
+ }
+#else
+ if(!icon) {
+ icon = ui_icon("application-x-generic", size);
+ }
+#endif
+ return icon;
+}
+
+UiIcon* ui_icon_unscaled(const char *name, int size) {
+ return get_icon(name, size, 1);
+}
+
+#if GTK_MAJOR_VERSION >= 4
+GdkPixbuf* ui_icon_pixbuf(UiIcon *icon) {
+ if(!icon->pixbuf) {
+ GFile *file = gtk_icon_paintable_get_file(icon->info);
+ GError *error = NULL;
+ char *path = g_file_get_path(file);
+ icon->pixbuf = gdk_pixbuf_new_from_file(path, &error);
+ }
+ return icon->pixbuf;
+}
+#else
+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;
+}
+#endif
+
+/*
+UiImage* ui_icon_image(UiIcon *icon) {
+ GError *error = NULL;
+ GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
+ if(pixbuf) {
+ UiImage *img = malloc(sizeof(UiImage));
+ img->pixbuf = pixbuf;
+ return img;
+ }
+ return NULL;
+}
+*/
+
+/*
+UiImage* ui_image(const char *filename) {
+ return ui_named_image(filename, NULL);
+}
+
+UiImage* ui_named_image(const char *filename, const char *name) {
+ char *path = uic_get_image_path(filename);
+ if(!path) {
+ fprintf(stderr, "UiError: pixmaps directory not set\n");
+ return NULL;
+ }
+ UiImage *img = ui_load_image_from_path(path, name);
+ free(path);
+ return img;
+}
+
+UiImage* ui_load_image_from_path(const char *path, const char *name) {
+ GError *error = NULL;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error);
+ if(!pixbuf) {
+ fprintf(stderr, "UiError: Cannot load image: %s\n", path);
+ return NULL;
+ }
+
+ UiImage *img = malloc(sizeof(UiImage));
+ img->pixbuf = pixbuf;
+ if(name) {
+ cxMapPut(image_map, name, img);
+ }
+ return img;
+}
+*/
+
+void ui_free_image(UiImage *img) {
+ g_object_unref(img->pixbuf);
+ free(img);
+}
--- /dev/null
+/*
+ * 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 ICON_H
+#define ICON_H
+
+#include "../ui/icons.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 10
+#define UI_SUPPORTS_SCALE
+#endif
+
+
+struct UiIcon {
+#if GTK_MAJOR_VERSION >= 4
+ GtkIconPaintable *info;
+#else
+ GtkIconInfo *info;
+#endif
+ GdkPixbuf *pixbuf;
+};
+
+struct UiImage {
+ GdkPixbuf *pixbuf;
+};
+
+void ui_image_init(void);
+
+GdkPixbuf* ui_get_image(const char *name);
+
+GdkPixbuf* ui_icon_pixbuf(UiIcon *icon);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ICON_H */
+
--- /dev/null
+/*
+ * 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 "image.h"
+
+#include "container.h"
+#include "menu.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+static void imageviewer_destroy(UiImageViewer *iv) {
+ if(iv->pixbuf) {
+ g_object_unref(iv->pixbuf);
+ }
+ free(iv);
+}
+
+#if GTK_MAJOR_VERSION >= 4
+
+static void imageviewer_draw(
+ GtkDrawingArea *drawingarea,
+ cairo_t *cr,
+ int width,
+ int height,
+ gpointer userdata)
+{
+ ui_cairo_draw_image(userdata, cr, width, height);
+}
+
+#else
+
+static gboolean imageviewer_draw(GtkWidget *widget, cairo_t *cr, gpointer userdata) {
+ int width = gtk_widget_get_allocated_width(widget);
+ int height = gtk_widget_get_allocated_height(widget);
+ ui_cairo_draw_image(userdata, cr, width, height);
+ return FALSE;
+}
+
+#endif
+
+UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) {
+ GtkWidget *drawingarea = gtk_drawing_area_new();
+ GtkWidget *toplevel;
+ GtkWidget *widget = drawingarea;
+
+ gtk_widget_set_size_request(drawingarea, 100, 100);
+
+#if GTK_MAJOR_VERSION < 4
+ GtkWidget *eventbox = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(eventbox), drawingarea);
+ widget = eventbox;
+#endif
+
+ if(args->scrollarea) {
+ toplevel = SCROLLEDWINDOW_NEW();
+ SCROLLEDWINDOW_SET_CHILD(toplevel, widget);
+ args->adjustwidgetsize = TRUE;
+ } else {
+ toplevel = widget;
+ }
+
+ UiImageViewer *imgviewer = malloc(sizeof(UiImageViewer));
+ memset(imgviewer, 0, sizeof(UiImageViewer));
+ imgviewer->obj = obj;
+ imgviewer->onbuttonpress = args->onbuttonpress;
+ imgviewer->onbuttonpressdata = args->onbuttonpressdata;
+ imgviewer->onbuttonrelease = args->onbuttonrelease;
+ imgviewer->onbuttonreleasedata = args->onbuttonreleasedata;
+ if(args->image_padding > 0) {
+ imgviewer->padding_left = args->image_padding;
+ imgviewer->padding_right = args->image_padding;
+ imgviewer->padding_top = args->image_padding;
+ imgviewer->padding_bottom = args->image_padding;
+ } else {
+ imgviewer->padding_left = args->image_padding_left;
+ imgviewer->padding_right = args->image_padding_right;
+ imgviewer->padding_top = args->image_padding_top;
+ imgviewer->padding_bottom = args->image_padding_bottom;
+ }
+ imgviewer->adjustwidgetsize = args->adjustwidgetsize;
+ imgviewer->autoscale = args->autoscale;
+ imgviewer->useradjustable = args->useradjustable;
+ imgviewer->zoom_scale = 20;
+
+ g_object_set_data_full(G_OBJECT(drawingarea), "uiimageviewer", imgviewer, (GDestroyNotify)imageviewer_destroy);
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC);
+ imgviewer->var = var;
+ imgviewer->widget = drawingarea;
+
+ if(var) {
+ UiGeneric *value = var->value;
+ value->get = ui_imageviewer_get;
+ value->get_type = ui_imageviewer_get_type;
+ value->set = ui_imageviewer_set;
+ value->obj = imgviewer;
+ if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) {
+ GdkPixbuf *pixbuf = value->value;
+ value->value = NULL;
+ ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE);
+ g_object_unref(pixbuf);
+ }
+ }
+
+#if GTK_MAJOR_VERSION >= 4
+ gtk_drawing_area_set_draw_func(
+ GTK_DRAWING_AREA(drawingarea),
+ imageviewer_draw,
+ imgviewer,
+ NULL);
+
+ if(args->useradjustable) {
+ gtk_widget_set_focusable(drawingarea, TRUE);
+ }
+
+ GtkEventController *scrollcontroller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
+ g_signal_connect(scrollcontroller, "scroll", G_CALLBACK(ui_imageviewer_scroll), imgviewer);
+ gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(scrollcontroller));
+
+ GtkGesture *drag = gtk_gesture_drag_new();
+ g_signal_connect(drag, "drag-begin", G_CALLBACK(ui_imageviewer_drag_begin_cb), imgviewer);
+ g_signal_connect(drag, "drag-end", G_CALLBACK(ui_imageviewer_drag_end_cb), imgviewer);
+ g_signal_connect(drag, "drag-update", G_CALLBACK(ui_imageviewer_drag_update_cb), imgviewer);
+ gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(drag));
+
+ GtkGesture *click = gtk_gesture_click_new();
+ g_signal_connect(click, "pressed", G_CALLBACK(ui_imageviewer_pressed_cb), imgviewer);
+ g_signal_connect(click, "released", G_CALLBACK(ui_imageviewer_released_cb), imgviewer);
+ gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(click));
+
+#elif GTK_MAJOR_VERSION == 3
+ g_signal_connect(
+ drawingarea,
+ "draw",
+ G_CALLBACK(imageviewer_draw),
+ imgviewer);
+
+ gtk_widget_add_events(eventbox, GDK_SCROLL_MASK);
+
+ g_signal_connect(
+ eventbox,
+ "scroll-event",
+ G_CALLBACK(ui_imageviewer_scroll_event),
+ imgviewer);
+ g_signal_connect(
+ eventbox,
+ "button-press-event",
+ G_CALLBACK(ui_imageviewer_button_press_event),
+ imgviewer);
+ g_signal_connect(
+ eventbox,
+ "button-release-event",
+ G_CALLBACK(ui_imageviewer_button_release_event),
+ imgviewer);
+
+#endif
+
+ if(args->contextmenu) {
+ UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, widget);
+ ui_widget_set_contextmenu(widget, menu);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, toplevel, &layout);
+
+ return toplevel;
+}
+
+static void imageviewer_reset(UiImageViewer *imgviewer) {
+ imgviewer->isautoscaled = FALSE;
+ imgviewer->transx = 0;
+ imgviewer->transy;
+ imgviewer->begin_transx = 0;
+ imgviewer->begin_transy = 0;
+ imgviewer->scale = 1;
+ imgviewer->user_scale = 1;
+}
+
+UIWIDGET ui_imageviewer_reset(UIWIDGET w) {
+ UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
+ if(imgviewer) {
+ imageviewer_reset(imgviewer);
+ gtk_widget_queue_draw(w);
+ }
+}
+
+UIWIDGET ui_imageviewer_set_autoscale(UIWIDGET w, UiBool set) {
+ UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
+ if(imgviewer) {
+ imgviewer->autoscale = set;
+ }
+}
+
+UIWIDGET ui_imageviewer_set_adjustwidgetsize(UIWIDGET w, UiBool set) {
+ UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
+ if(imgviewer) {
+ imgviewer->adjustwidgetsize = set;
+ }
+}
+
+UIWIDGET ui_imageviewer_set_useradjustable(UIWIDGET w, UiBool set) {
+ UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer");
+ if(imgviewer) {
+ imgviewer->useradjustable = set;
+ }
+}
+
+void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height) {
+ if(!imgviewer->pixbuf) {
+ return;
+ }
+
+ GdkPixbuf *pixbuf = imgviewer->pixbuf;
+ double dpixwidth = (double)gdk_pixbuf_get_width(pixbuf);
+ double dpixheight = (double)gdk_pixbuf_get_height(pixbuf);
+
+ double dwidth = width;
+ double dheight = height;
+ double scale = 1;
+ // if autoscale is enabled, scale the image to fill available space
+ // if useradjustable is also enabled, the autoscaling is only done once
+ if(imgviewer->autoscale && imgviewer->scale != 0) {
+ if(!imgviewer->isautoscaled) {
+ scale = dwidth / dpixwidth;
+ if(dpixheight * scale > dheight) {
+ scale = dheight / dpixheight;
+ }
+
+ if(imgviewer->useradjustable) {
+ imgviewer->isautoscaled = TRUE;
+ }
+
+ imgviewer->scale = scale;
+ } else {
+ scale = imgviewer->scale;
+ }
+
+ imgviewer->user_scale = scale;
+ } else {
+ // user-adjusted scaling
+ //scale = 1 + ((double)imgviewer->zoom / (double)imgviewer->zoom_scale);
+ scale = imgviewer->user_scale;
+ }
+
+ dpixwidth *= scale;
+ dpixheight *= scale;
+ double x = (dwidth - dpixwidth) / 2;
+ double y = (dheight - dpixheight) / 2;
+
+ x += imgviewer->transx;
+ y += imgviewer->transy;
+
+ cairo_translate(cr, x, y);
+ cairo_scale(cr, scale, scale);
+
+ gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
+ cairo_paint(cr);
+}
+
+void* ui_imageviewer_get(UiGeneric *g) {
+ UiImageViewer *imgviewer = g->obj;
+ g->value = imgviewer->pixbuf;
+ return g->value;
+}
+
+const char* ui_imageviewer_get_type(UiGeneric *g) {
+ return UI_IMAGE_OBJECT_TYPE;
+}
+
+int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) {
+ if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) {
+ return 1;
+ }
+
+ GdkPixbuf *pixbuf = value;
+ g_object_ref(pixbuf);
+
+ UiImageViewer *imgviewer = g->obj;
+ g->value = pixbuf;
+
+ imageviewer_reset(imgviewer);
+
+ if(imgviewer->pixbuf) {
+ g_object_unref(imgviewer->pixbuf);
+ }
+ imgviewer->pixbuf = pixbuf;
+
+ if(imgviewer->adjustwidgetsize && !imgviewer->autoscale) {
+ int width = gdk_pixbuf_get_width(pixbuf);
+ int height = gdk_pixbuf_get_height(pixbuf);
+ gtk_widget_set_size_request(imgviewer->widget, width, height);
+ }
+ gtk_widget_queue_draw(imgviewer->widget);
+
+ return 0;
+}
+
+
+
+int ui_image_load_file(UiGeneric *obj, const char *path) {
+ GError *error = NULL;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error);
+ if(!pixbuf) {
+ return 1;
+ }
+
+ if(obj->set) {
+ obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE);
+ g_object_unref(pixbuf);
+ } else {
+ obj->value = pixbuf;
+ obj->type = UI_IMAGE_OBJECT_TYPE;
+ }
+
+ return 0;
+}
+
+UIEXPORT int ui_image_load_data(UiGeneric *obj, const void *imgdata, size_t size) {
+ GBytes *bytes = g_bytes_new_static(imgdata, size);
+ GInputStream *in = g_memory_input_stream_new_from_bytes(bytes);
+ GError *error = NULL;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream(in, NULL, &error);
+ g_object_unref(in);
+ if(!pixbuf) {
+ return 1;
+ }
+
+ if(obj->set) {
+ obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE);
+ g_object_unref(pixbuf);
+ } else {
+ obj->value = pixbuf;
+ obj->type = UI_IMAGE_OBJECT_TYPE;
+ }
+
+ return 0;
+}
+
+void ui_image_ref(UIIMAGE img) {
+ g_object_ref(img);
+}
+
+void ui_image_unref(UIIMAGE img) {
+ g_object_unref(img);
+}
+
+#if GTK_MAJOR_VERSION >= 4
+
+gboolean ui_imageviewer_scroll(
+ GtkEventControllerScroll *widget,
+ gdouble dx,
+ gdouble dy,
+ gpointer userdata)
+{
+ UiImageViewer *imgviewer = userdata;
+ if(imgviewer->useradjustable) {
+ double step = dy / imgviewer->zoom_scale;
+ if(imgviewer->user_scale - step > 0) {
+ imgviewer->user_scale -= step;
+ }
+
+ imgviewer->scale = 0; // disable autoscale
+ gtk_widget_queue_draw(imgviewer->widget);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void ui_imageviewer_drag_begin_cb(
+ GtkGestureDrag *self,
+ gdouble start_x,
+ gdouble start_y,
+ gpointer userdata)
+{
+ UiImageViewer *imgviewer = userdata;
+ imgviewer->begin_transx = imgviewer->transx;
+ imgviewer->begin_transy = imgviewer->transy;
+}
+
+void ui_imageviewer_drag_end_cb(
+ GtkGestureDrag* self,
+ gdouble x,
+ gdouble y,
+ gpointer userdata)
+{
+
+}
+
+void ui_imageviewer_drag_update_cb(
+ GtkGestureDrag *self,
+ gdouble x,
+ gdouble y,
+ gpointer userdata)
+{
+ UiImageViewer *imgviewer = userdata;
+ if(imgviewer->useradjustable) {
+ imgviewer->transx = imgviewer->begin_transx + x;
+ imgviewer->transy = imgviewer->begin_transy + y;
+ gtk_widget_queue_draw(imgviewer->widget);
+ }
+}
+
+static void imgviewer_button_event(
+ GtkGestureClick *gesture,
+ UiImageViewer *imgviewer,
+ ui_callback callback,
+ void *userdata)
+{
+ UiEvent event;
+ event.obj = imgviewer->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(gesture));
+ event.set = 0;
+ callback(&event, userdata);
+}
+
+void ui_imageviewer_pressed_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata)
+{
+ UiImageViewer *imgviewer = userdata;
+ if(imgviewer->onbuttonpress) {
+ imgviewer_button_event(self, imgviewer, imgviewer->onbuttonpress, imgviewer->onbuttonpressdata);
+ }
+}
+
+void ui_imageviewer_released_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata)
+{
+ UiImageViewer *imgviewer = userdata;
+ if(imgviewer->onbuttonrelease) {
+ imgviewer_button_event(self, imgviewer, imgviewer->onbuttonrelease, imgviewer->onbuttonreleasedata);
+ }
+}
+
+#else
+
+gboolean ui_imageviewer_scroll_event(
+ GtkWidget *widget,
+ GdkEventScroll event,
+ gpointer userdata)
+{
+ // TODO
+ return FALSE;
+}
+
+gboolean ui_imageviewer_button_press_event(
+ GtkWidget *widget,
+ GdkEventButton event,
+ gpointer userdata)
+{
+ // TODO
+ return FALSE;
+}
+
+gboolean ui_imageviewer_button_release_event(
+ GtkWidget *widget,
+ GdkEventButton event,
+ gpointer userdata)
+{
+ // TODO
+ return FALSE;
+}
+
+#endif
--- /dev/null
+/*
+ * 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 IMAGE_H
+#define IMAGE_H
+
+#include "../ui/image.h"
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiImageViewer {
+ UiObject *obj;
+ GtkWidget *widget;
+ UiVar *var;
+ int padding_left;
+ int padding_right;
+ int padding_top;
+ int padding_bottom;
+ UiBool autoscale;
+ UiBool adjustwidgetsize;
+ UiBool useradjustable;
+ GdkPixbuf *pixbuf;
+
+ double zoom_scale;
+ int transx;
+ int transy;
+ int begin_transx;
+ int begin_transy;
+ UiBool isautoscaled;
+ double user_scale;
+ double scale;
+
+ ui_callback onbuttonpress;
+ void *onbuttonpressdata;
+ ui_callback onbuttonrelease;
+ void *onbuttonreleasedata;
+} UiImageViewer;
+
+void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height);
+
+void* ui_imageviewer_get(UiGeneric *g);
+const char* ui_imageviewer_get_type(UiGeneric *g);
+int ui_imageviewer_set(UiGeneric *g, void *value, const char *type);
+
+#if GTK_MAJOR_VERSION >= 4
+
+gboolean ui_imageviewer_scroll(
+ GtkEventControllerScroll *widget,
+ gdouble dx,
+ gdouble dy,
+ gpointer userdata);
+
+void ui_imageviewer_drag_begin_cb(
+ GtkGestureDrag* self,
+ gdouble start_x,
+ gdouble start_y,
+ gpointer userdata);
+
+void ui_imageviewer_drag_end_cb(
+ GtkGestureDrag* self,
+ gdouble x,
+ gdouble y,
+ gpointer userdata);
+
+void ui_imageviewer_drag_update_cb(
+ GtkGestureDrag* self,
+ gdouble x,
+ gdouble y,
+ gpointer userdata);
+
+void ui_imageviewer_pressed_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata);
+
+void ui_imageviewer_released_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata);
+
+#else
+
+gboolean ui_imageviewer_scroll_event(
+ GtkWidget *widget,
+ GdkEventScroll event,
+ gpointer userdata);
+
+gboolean ui_imageviewer_button_press_event(
+ GtkWidget *widget,
+ GdkEventButton event,
+ gpointer userdata);
+
+gboolean ui_imageviewer_button_release_event(
+ GtkWidget *widget,
+ GdkEventButton event,
+ gpointer userdata);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IMAGE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "../common/context.h"
+#include "../common/object.h"
+#include "container.h"
+
+#include <cx/array_list.h>
+#include <cx/linked_list.h>
+
+#include "list.h"
+#include "button.h"
+#include "icon.h"
+#include "menu.h"
+#include "dnd.h"
+
+
+static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
+ return getvalue(elm, col);
+}
+
+static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ return elm;
+}
+
+static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ return NULL;
+}
+
+/*
+static GtkTargetEntry targetentries[] =
+ {
+ { "STRING", 0, 0 },
+ { "text/plain", 0, 1 },
+ { "text/uri-list", 0, 2 },
+ };
+*/
+
+static void listview_copy_static_elements(UiListView *listview, char **elm, size_t nelm) {
+ listview->elements = calloc(nelm, sizeof(char*));
+ listview->nelm = nelm;
+ for(int i=0;i<nelm;i++) {
+ listview->elements[i] = strdup(elm[i]);
+ }
+}
+
+static UiListView* create_listview(UiObject *obj, UiListArgs *args) {
+ UiListView *tableview = malloc(sizeof(UiListView));
+ memset(tableview, 0, sizeof(UiListView));
+ tableview->obj = obj;
+ tableview->model = args->model;
+ tableview->onactivate = args->onactivate;
+ tableview->onactivatedata = args->onactivatedata;
+ tableview->onselection = args->onselection;
+ tableview->onselectiondata = args->onselectiondata;
+ tableview->ondragstart = args->ondragstart;
+ tableview->ondragstartdata = args->ondragstartdata;
+ tableview->ondragcomplete = args->ondragcomplete;
+ tableview->ondragcompletedata = args->ondragcompletedata;
+ tableview->ondrop = args->ondrop;
+ tableview->ondropdata = args->ondropdata;
+ tableview->selection.count = 0;
+ tableview->selection.rows = NULL;
+ tableview->current_row = -1;
+ tableview->getstyle = args->getstyle;
+ tableview->getstyledata = args->getstyledata;
+ tableview->onsave = args->onsave;
+ tableview->onsavedata = args->onsavedata;
+
+ if(args->getvalue2) {
+ tableview->getvalue = args->getvalue2;
+ tableview->getvaluedata = args->getvalue2data;
+ } else if(args->getvalue) {
+ tableview->getvalue = getvalue_wrapper;
+ tableview->getvaluedata = (void*)args->getvalue;
+ } else {
+ tableview->getvalue = null_getvalue;
+ }
+
+ return tableview;
+}
+
+#if GTK_CHECK_VERSION(4, 10, 0)
+
+
+/* BEGIN GObject wrapper for generic pointers */
+
+typedef struct _ObjWrapper {
+ GObject parent_instance;
+ void *data;
+ int i;
+} ObjWrapper;
+
+typedef struct _ObjWrapperClass {
+ GObjectClass parent_class;
+} ObjWrapperClass;
+
+G_DEFINE_TYPE(ObjWrapper, obj_wrapper, G_TYPE_OBJECT)
+
+static void obj_wrapper_class_init(ObjWrapperClass *klass) {
+
+}
+
+static void obj_wrapper_init(ObjWrapper *self) {
+ self->data = NULL;
+}
+
+ObjWrapper* obj_wrapper_new(void* data, int i) {
+ ObjWrapper *obj = g_object_new(obj_wrapper_get_type(), NULL);
+ obj->data = data;
+ obj->i = i;
+ return obj;
+}
+
+/* END GObject wrapper for generic pointers */
+
+typedef struct UiCellEntry {
+ GtkEntry *entry;
+ UiListView *listview;
+ char *previous_value;
+ int row;
+ int col;
+} UiCellEntry;
+
+static void cell_save_value(UiCellEntry *data, int restore) {
+ if(data->listview && data->listview->onsave) {
+ UiVar *var = data->listview->var;
+ UiList *list = var ? var->value : NULL;
+ const char *str = ENTRY_GET_TEXT(data->entry);
+ UiCellValue value;
+ value.string = str;
+ value.type = UI_STRING_EDITABLE;
+ if(data->listview->onsave(list, data->row, data->col, &value, data->listview->onsavedata)) {
+ free(data->previous_value);
+ data->previous_value = strdup(str);
+ } else if(restore) {
+ ENTRY_SET_TEXT(data->entry, data->previous_value);
+ }
+ }
+}
+
+static void cell_entry_leave_focus(
+ GtkEventControllerFocus *self,
+ UiCellEntry *data)
+{
+ // TODO: use a different singal to track focus
+ // we only want to call cell_save_value, when another entry is selected,
+ // not when the window loses focus or something like that
+ cell_save_value(data, TRUE);
+}
+
+static void cell_entry_destroy(GtkWidget *object, UiCellEntry *data) {
+ free(data->previous_value);
+ free(data);
+}
+
+static void cell_entry_unmap(GtkWidget *w, UiCellEntry *data) {
+ const char *text = ENTRY_GET_TEXT(w);
+ cell_save_value(data, FALSE);
+}
+
+static void cell_entry_activate(
+ GtkEntry *self,
+ UiCellEntry *data)
+{
+ cell_save_value(data, TRUE);
+}
+
+static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
+ UiColData *col = userdata;
+ UiModel *model = col->listview->model;
+ UiModelType type = model->types[col->model_column];
+ if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+ GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ GtkWidget *image = gtk_image_new();
+ GtkWidget *label = gtk_label_new(NULL);
+ BOX_ADD(hbox, image);
+ BOX_ADD(hbox, label);
+ gtk_list_item_set_child(item, hbox);
+ g_object_set_data(G_OBJECT(hbox), "image", image);
+ g_object_set_data(G_OBJECT(hbox), "label", label);
+ } else if(type == UI_ICON) {
+ GtkWidget *image = gtk_image_new();
+ gtk_list_item_set_child(item, image);
+ } else if(type == UI_STRING_EDITABLE) {
+ GtkWidget *textfield = gtk_entry_new();
+ gtk_widget_add_css_class(textfield, "ui-table-entry");
+ gtk_list_item_set_child(item, textfield);
+
+ UiCellEntry *entry_data = malloc(sizeof(UiCellEntry));
+ entry_data->entry = GTK_ENTRY(textfield);
+ entry_data->listview = NULL;
+ entry_data->previous_value = NULL;
+ entry_data->col = 0;
+ entry_data->row = 0;
+ g_object_set_data(G_OBJECT(textfield), "ui_entry_data", entry_data);
+
+ g_signal_connect(
+ textfield,
+ "destroy",
+ G_CALLBACK(cell_entry_destroy),
+ entry_data);
+ g_signal_connect(
+ textfield,
+ "activate",
+ G_CALLBACK(cell_entry_activate),
+ entry_data);
+ g_signal_connect(
+ textfield,
+ "unmap",
+ G_CALLBACK(cell_entry_unmap),
+ entry_data);
+
+ GtkEventController *focus_controller = gtk_event_controller_focus_new();
+ g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data);
+ gtk_widget_add_controller(textfield, focus_controller);
+ } else if(type == UI_BOOL_EDITABLE) {
+ GtkWidget *checkbox = gtk_check_button_new();
+ gtk_list_item_set_child(item, checkbox);
+ }else {
+ GtkWidget *label = gtk_label_new(NULL);
+ gtk_label_set_xalign(GTK_LABEL(label), 0);
+ gtk_list_item_set_child(item, label);
+ }
+}
+
+PangoAttrList* textstyle2pangoattributes(UiTextStyle style) {
+ PangoAttrList *attr = pango_attr_list_new();
+
+ if(style.text_style & UI_TEXT_STYLE_BOLD) {
+ pango_attr_list_insert(attr, pango_attr_weight_new(PANGO_WEIGHT_BOLD));
+ }
+ if(style.text_style & UI_TEXT_STYLE_ITALIC) {
+ pango_attr_list_insert(attr, pango_attr_style_new(PANGO_STYLE_ITALIC));
+ }
+ if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
+ pango_attr_list_insert(attr, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE));
+ }
+
+ // foreground color, convert from 8bit to 16bit
+ guint16 r = (guint16)style.fg.red * 257;
+ guint16 g = (guint16)style.fg.green * 257;
+ guint16 b = (guint16)style.fg.blue * 257;
+ pango_attr_list_insert(attr, pango_attr_foreground_new(r, g, b));
+
+ return attr;
+}
+
+static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, gpointer userdata) {
+ UiColData *col = userdata;
+ UiList *list = col->listview->var ? col->listview->var->value : NULL;
+ UiListView *listview = col->listview;
+
+ ObjWrapper *obj = gtk_list_item_get_item(item);
+ UiModel *model = col->listview->model;
+ UiModelType type = model->types[col->model_column];
+
+ // cache the GtkListItem
+ CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
+ UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
+ if(row) {
+ if(row->items[col->model_column] == NULL) {
+ row->bound++;
+ }
+ } else {
+ row = calloc(1, sizeof(UiRowItems) + listview->numcolumns * sizeof(GtkListItem*));
+ cxMapPut(listview->bound_rows, row_key, row);
+ row->bound = 1;
+ }
+ row->items[col->model_column] = item;
+
+ UiBool freevalue = FALSE;
+ void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
+ GtkWidget *child = gtk_list_item_get_child(item);
+
+ PangoAttrList *attributes = NULL;
+ UiTextStyle style = { 0, 0 };
+ if(listview->getstyle) {
+ // query current row style, if it wasn't already queried
+ if(obj->i != listview->current_row) {
+ listview->current_row = obj->i;
+ listview->row_style = (UiTextStyle){ 0, 0 };
+ listview->apply_row_style = listview->getstyle(list, obj->data, obj->i, -1, listview->getstyledata, &listview->row_style);
+ style = listview->row_style;
+ if(listview->apply_row_style) {
+ pango_attr_list_unref(listview->current_row_attributes);
+ listview->current_row_attributes = textstyle2pangoattributes(style);
+ }
+ }
+
+ int style_col = col->data_column;
+ if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+ style_col++; // col->data_column is the icon, we need the next col for the label
+ }
+
+ // get the column style
+ if(listview->getstyle(list, obj->data, obj->i, style_col, listview->getstyledata, &style)) {
+ attributes = textstyle2pangoattributes(style);
+ } else if(listview->apply_row_style) {
+ attributes = listview->current_row_attributes;
+ }
+ }
+
+ switch(type) {
+ case UI_STRING_FREE: {
+ freevalue = TRUE;
+ }
+ case UI_STRING: {
+ gtk_label_set_label(GTK_LABEL(child), data);
+ if(freevalue) {
+ free(data);
+ }
+ gtk_label_set_attributes(GTK_LABEL(child), attributes);
+ break;
+ }
+ case UI_INTEGER: {
+ intptr_t intvalue = (intptr_t)data;
+ char buf[32];
+ snprintf(buf, 32, "%d", (int)intvalue);
+ gtk_label_set_label(GTK_LABEL(child), buf);
+ gtk_label_set_attributes(GTK_LABEL(child), attributes);
+ break;
+ }
+ case UI_ICON: {
+ UiIcon *icon = data;
+ if(icon) {
+ gtk_image_set_from_paintable(GTK_IMAGE(child), GDK_PAINTABLE(icon->info));
+ }
+ break;
+ }
+ case UI_ICON_TEXT: {
+
+ }
+ case UI_ICON_TEXT_FREE: {
+ void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue);
+ if(type == UI_ICON_TEXT_FREE) {
+ freevalue = TRUE;
+ }
+ GtkWidget *image = g_object_get_data(G_OBJECT(child), "image");
+ GtkWidget *label = g_object_get_data(G_OBJECT(child), "label");
+ if(data && image) {
+ UiIcon *icon = data;
+ gtk_image_set_from_paintable(GTK_IMAGE(image), GDK_PAINTABLE(icon->info));
+ }
+ if(data2 && label) {
+ gtk_label_set_label(GTK_LABEL(label), data2);
+ gtk_label_set_attributes(GTK_LABEL(label), attributes);
+ }
+ if(freevalue) {
+ free(data2);
+ }
+ break;
+ }
+ case UI_STRING_EDITABLE: {
+ UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
+ if(entry) {
+ entry->listview = col->listview;
+ entry->row = obj->i;
+ entry->col = col->data_column;
+ entry->previous_value = strdup(data);
+ }
+ ENTRY_SET_TEXT(child, data);
+ break;
+ }
+ case UI_BOOL_EDITABLE: {
+ intptr_t i = (intptr_t)data;
+ gtk_check_button_set_active(GTK_CHECK_BUTTON(child), (gboolean)i);
+ break;
+ }
+ }
+
+ if(attributes != listview->current_row_attributes) {
+ pango_attr_list_unref(attributes);
+ }
+}
+
+static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
+ ObjWrapper *obj = gtk_list_item_get_item(item);
+ UiListView *listview = col->listview;
+ CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
+ UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
+ if(row) {
+ row->items[col->model_column] = NULL;
+ row->bound--;
+ if(row->bound == 0) {
+ cxMapRemove(listview->bound_rows, row_key);
+ }
+ } // else: should not happen
+
+ GtkWidget *child = gtk_list_item_get_child(item);
+ UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
+ if(entry) {
+ cell_save_value(entry, FALSE);
+ entry->listview = NULL;
+ free(entry->previous_value);
+ entry->previous_value = NULL;
+ } else if(GTK_IS_CHECK_BUTTON(child)) {
+
+ }
+}
+
+
+static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
+ GtkSelectionModel *selection_model;
+ if(multiselection) {
+ selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore)));
+ } else {
+ selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(liststore)));
+ gtk_single_selection_set_can_unselect(GTK_SINGLE_SELECTION(selection_model), TRUE);
+ gtk_single_selection_set_autoselect(GTK_SINGLE_SELECTION(selection_model), FALSE);
+ }
+ g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview);
+ return selection_model;
+}
+
+UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
+ // to simplify things and share code with ui_table_create, we also
+ // use a UiModel for the listview
+ UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
+ args->model = model;
+
+ GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
+ UiListView *listview = create_listview(obj, args);
+ if(!args->getvalue && !args->getvalue2) {
+ listview->getvalue = str_getvalue;
+ }
+
+ listview->numcolumns = 1;
+ listview->columns = malloc(sizeof(UiColData));
+ listview->columns->listview = listview;
+ listview->columns->data_column = 0;
+ listview->columns->model_column = 0;
+
+ listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+ listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
+
+ GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
+ g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
+ g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
+
+ GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection);
+ GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ // init listview
+ listview->widget = view;
+ listview->var = var;
+ listview->liststore = ls;
+ listview->selectionmodel = selection_model;
+ g_signal_connect(
+ view,
+ "destroy",
+ G_CALLBACK(ui_listview_destroy),
+ listview);
+
+ // bind listview to list
+ if(var && var->value) {
+ UiList *list = var->value;
+
+ list->obj = listview;
+ list->update = ui_listview_update2;
+ list->getselection = ui_listview_getselection2;
+ list->setselection = ui_listview_setselection2;
+
+ ui_update_liststore(ls, list);
+ } else if (args->static_elements && args->static_nelm > 0) {
+ listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
+ listview->getvalue = str_getvalue; // force string values
+ ui_update_liststore_static(ls, listview->elements, listview->nelm);
+ }
+
+ // event handling
+ if(args->onactivate) {
+ // columnview and listview can use the same callback function, because
+ // the first parameter (which is technically a different pointer type)
+ // is ignored
+ g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview);
+ }
+ if(args->contextmenu) {
+ UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view);
+ ui_widget_set_contextmenu(view, menu);
+ }
+
+ // add widget to parent
+ GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(scroll_area),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
+ SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
+
+ if(args->width > 0 || args->height > 0) {
+ int width = args->width;
+ int height = args->height;
+ if(width == 0) {
+ width = -1;
+ }
+ if(height == 0) {
+ height = -1;
+ }
+ gtk_widget_set_size_request(scroll_area, width, height);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, scroll_area, &layout);
+
+ return scroll_area;
+}
+
+UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
+ // to simplify things and share code with ui_tableview_create, we also
+ // use a UiModel for the listview
+ UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
+ args->model = model;
+
+ GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
+ UiListView *listview = create_listview(obj, args);
+
+ if(!args->getvalue && !args->getvalue2) {
+ listview->getvalue = str_getvalue;
+ }
+
+ listview->numcolumns = 1;
+ listview->columns = malloc(sizeof(UiColData));
+ listview->columns->listview = listview;
+ listview->columns->data_column = 0;
+ listview->columns->model_column = 0;
+
+ listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+ listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
+
+ GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
+ g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
+ g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
+
+ GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL);
+ gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory);
+ if(args->width > 0) {
+ gtk_widget_set_size_request(view, args->width, -1);
+ }
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ // init listview
+ listview->widget = view;
+ listview->var = var;
+ listview->liststore = ls;
+ listview->selectionmodel = NULL;
+ g_signal_connect(
+ view,
+ "destroy",
+ G_CALLBACK(ui_listview_destroy),
+ listview);
+
+ // bind listview to list
+ if(var && var->value) {
+ UiList *list = var->value;
+
+ list->obj = listview;
+ list->update = ui_listview_update2;
+ list->getselection = ui_combobox_getselection;
+ list->setselection = ui_combobox_setselection;
+
+ ui_update_liststore(ls, list);
+ } else if (args->static_elements && args->static_nelm > 0) {
+ listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
+ listview->getvalue = str_getvalue; // force string values
+ ui_update_liststore_static(ls, listview->elements, listview->nelm);
+ }
+
+ // event handling
+ if(args->onactivate) {
+ g_signal_connect(view, "notify::selected", G_CALLBACK(ui_dropdown_notify), listview);
+ }
+
+ // add widget to parent
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, view, &layout);
+
+ return view;
+}
+
+void ui_listview_select(UIWIDGET listview, int index) {
+ GtkSelectionModel *model = gtk_list_view_get_model(GTK_LIST_VIEW(listview));
+ gtk_selection_model_select_item(model, index, TRUE);
+}
+
+void ui_combobox_select(UIWIDGET dropdown, int index) {
+ gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index);
+}
+
+UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
+ GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
+ //g_list_store_append(ls, v1);
+
+ // create obj to store all relevant data we need for handling events
+ // and list updates
+ UiListView *tableview = create_listview(obj, args);
+
+ GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection);
+ GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model));
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ // init tableview
+ tableview->widget = view;
+ tableview->var = var;
+ tableview->liststore = ls;
+ tableview->selectionmodel = selection_model;
+ g_signal_connect(
+ view,
+ "destroy",
+ G_CALLBACK(ui_listview_destroy),
+ tableview);
+
+
+ // create columns from UiModel
+ UiModel *model = args->model;
+ int columns = model ? model->columns : 0;
+
+ tableview->columns = calloc(columns, sizeof(UiColData));
+ tableview->numcolumns = columns;
+
+ tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+ tableview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
+
+ int addi = 0;
+ for(int i=0;i<columns;i++) {
+ tableview->columns[i].listview = tableview;
+ tableview->columns[i].model_column = i;
+ tableview->columns[i].data_column = i+addi;
+
+ if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
+ // icon+text has 2 data columns
+ addi++;
+ }
+
+ GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
+ UiColData *col = &tableview->columns[i];
+ g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col);
+ g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col);
+
+ GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[i], factory);
+ gtk_column_view_column_set_resizable(column, true);
+ gtk_column_view_append_column(GTK_COLUMN_VIEW(view), column);
+
+ int size = model->columnsize[i];
+ if(size > 0) {
+ gtk_column_view_column_set_fixed_width(column, size);
+ } else if(size < 0) {
+ gtk_column_view_column_set_expand(column, TRUE);
+ }
+ }
+
+ // bind listview to list
+ if(var && var->value) {
+ UiList *list = var->value;
+
+ list->obj = tableview;
+ list->update = ui_listview_update2;
+ list->getselection = ui_listview_getselection2;
+ list->setselection = ui_listview_setselection2;
+
+ ui_update_liststore(ls, list);
+ }
+
+ // event handling
+ if(args->onactivate) {
+ g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), tableview);
+ }
+ if(args->contextmenu) {
+ UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view);
+ ui_widget_set_contextmenu(view, menu);
+ }
+
+ // add widget to parent
+ GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(scroll_area),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
+ SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
+
+ if(args->width > 0 || args->height > 0) {
+ int width = args->width;
+ int height = args->height;
+ if(width == 0) {
+ width = -1;
+ }
+ if(height == 0) {
+ height = -1;
+ }
+ gtk_widget_set_size_request(scroll_area, width, height);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, scroll_area, &layout);
+
+ return scroll_area;
+}
+
+static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
+ UiListSelection sel = { 0, NULL };
+ GtkBitset *bitset = gtk_selection_model_get_selection(model);
+ int n = gtk_bitset_get_size(bitset);
+ printf("bitset %d\n", n);
+
+ gtk_bitset_unref(bitset);
+ return sel;
+}
+
+static void listview_event(ui_callback cb, void *cbdata, UiListView *view) {
+ UiEvent event;
+ event.obj = view->obj;
+ event.document = event.obj->ctx->document;
+ event.window = event.obj->window;
+ event.intval = view->selection.count;
+ event.eventdata = &view->selection;
+ event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+ event.set = ui_get_setop();
+ if(cb) {
+ cb(&event, cbdata);
+ }
+}
+
+static void listview_update_selection(UiListView *view) {
+ free(view->selection.rows);
+ view->selection.count = 0;
+ view->selection.rows = NULL;
+
+ CX_ARRAY_DECLARE(int, newselection);
+ cx_array_initialize(newselection, 8);
+
+ size_t nitems = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
+
+ for(size_t i=0;i<nitems;i++) {
+ if(gtk_selection_model_is_selected(view->selectionmodel, i)) {
+ int s = (int)i;
+ cx_array_simple_add(newselection, s);
+ }
+ }
+
+ if(newselection_size > 0) {
+ view->selection.count = newselection_size;
+ view->selection.rows = newselection;
+ } else {
+ free(newselection);
+ }
+}
+
+void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata) {
+ UiListView *view = userdata;
+ guint index = gtk_drop_down_get_selected(GTK_DROP_DOWN(dropdown));
+ GObject *item = gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown));
+ if(item && view->onactivate) {
+ ObjWrapper *eventdata = (ObjWrapper*)item;
+ UiEvent event;
+ event.obj = view->obj;
+ event.document = event.obj->ctx->document;
+ event.window = event.obj->window;
+ event.intval = index;
+ event.eventdata = eventdata->data;
+ event.eventdatatype = UI_EVENT_DATA_LIST_ELM;
+ event.set = ui_get_setop();
+ view->onactivate(&event, view->onactivatedata);
+ }
+}
+
+
+void ui_columnview_activate(void *ignore, guint position, gpointer userdata) {
+ UiListView *view = userdata;
+ if(view->selection.count == 0) {
+ listview_update_selection(view);
+ }
+ listview_event(view->onactivate, view->onactivatedata, view);
+}
+
+void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer userdata) {
+ UiListView *view = userdata;
+ listview_update_selection(view);
+ listview_event(view->onselection, view->onselectiondata, view);
+}
+
+void ui_dropdown_activate(GtkDropDown* self, gpointer userdata) {
+ UiListView *view = userdata;
+ guint selection = gtk_drop_down_get_selected(GTK_DROP_DOWN(view->widget));
+ UiListSelection sel = { 0, NULL };
+ int sel2 = (int)selection;
+ if(selection != GTK_INVALID_LIST_POSITION) {
+ sel.count = 1;
+ sel.rows = &sel2;
+ }
+
+ if(view->onactivate) {
+ UiEvent event;
+ event.obj = view->obj;
+ event.document = event.obj->ctx->document;
+ event.window = event.obj->window;
+ event.intval = view->selection.count;
+ event.eventdata = &view->selection;
+ event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+ event.set = ui_get_setop();
+ view->onactivate(&event, view->onactivatedata);
+ }
+}
+
+void ui_update_liststore(GListStore *liststore, UiList *list) {
+ g_list_store_remove_all(liststore);
+ int i = 0;
+ void *elm = list->first(list);
+ while(elm) {
+ ObjWrapper *obj = obj_wrapper_new(elm, i++);
+ g_list_store_append(liststore, obj);
+ elm = list->next(list);
+ }
+}
+
+void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm) {
+ g_list_store_remove_all(liststore);
+ for(int i=0;i<nelm;i++) {
+ ObjWrapper *obj = obj_wrapper_new(elm[i], i);
+ g_list_store_append(liststore, obj);
+ }
+}
+
+void ui_listview_update2(UiList *list, int i) {
+ UiListView *view = list->obj;
+ if(i < 0) {
+ ui_update_liststore(view->liststore, list);
+ } else {
+ void *value = list->get(list, i);
+ if(value) {
+ ObjWrapper *obj = g_list_model_get_item(G_LIST_MODEL(view->liststore), i);
+ if(obj) {
+ obj->data = value;
+ }
+
+ CxHashKey row_key = cx_hash_key(&i, sizeof(int));
+ UiRowItems *row = cxMapGet(view->bound_rows, row_key);
+ if(row) {
+ for(int c=0;c<view->numcolumns;c++) {
+ if(row->items[c] != NULL) {
+ column_factory_bind(NULL, row->items[c], &view->columns[c]);
+ }
+ }
+ }
+ }
+ }
+}
+
+UiListSelection ui_listview_getselection2(UiList *list) {
+ UiListView *view = list->obj;
+ UiListSelection selection;
+ selection.count = view->selection.count;
+ selection.rows = calloc(selection.count, sizeof(int));
+ memcpy(selection.rows, view->selection.rows, selection.count*sizeof(int));
+ return selection;
+}
+
+void ui_listview_setselection2(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
+ UiListView *view = list->obj;
+ UiListSelection newselection;
+ newselection.count = view->selection.count;
+ if(selection.count > 0) {
+ newselection.rows = calloc(newselection.count, sizeof(int));
+ memcpy(newselection.rows, selection.rows, selection.count*sizeof(int));
+ } else {
+ newselection.rows = NULL;
+ }
+ free(view->selection.rows);
+ view->selection = newselection;
+
+ gtk_selection_model_unselect_all(view->selectionmodel);
+ if(selection.count > 0) {
+ for(int i=0;i<selection.count;i++) {
+ gtk_selection_model_select_item(view->selectionmodel, selection.rows[i], FALSE);
+ }
+ }
+ ui_setop_enable(FALSE);
+}
+
+UiListSelection ui_combobox_getselection(UiList *list) {
+ UiListView *view = list->obj;
+ guint selection = gtk_drop_down_get_selected(GTK_DROP_DOWN(view->widget));
+ UiListSelection sel = { 0, NULL };
+ if(selection != GTK_INVALID_LIST_POSITION) {
+ sel.count = 1;
+ sel.rows = malloc(sizeof(int));
+ sel.rows[0] = (int)selection;
+ }
+ return sel;
+}
+
+void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
+ UiListView *view = list->obj;
+ if(selection.count > 0) {
+ gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), selection.rows[0]);
+ } else {
+ gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), GTK_INVALID_LIST_POSITION);
+ }
+ ui_setop_enable(FALSE);
+}
+
+#else
+
+static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) {
+ UiModel *model = listview->model;
+ ui_getstylefunc getstyle = listview->getstyle;
+
+ // get the row style
+ UiBool style_set = FALSE;
+ UiTextStyle style = { 0, 0 };
+ if(getstyle) {
+ style_set = getstyle(list, elm, row, -1, listview->getstyledata, &style);
+ }
+
+ // set column values
+ int c = 0;
+ for(int i=0;i<model->columns;i++,c++) {
+ UiBool freevalue = FALSE;
+ void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
+
+ UiModelType type = model->types[i];
+
+ if(getstyle) {
+ // in case the column is icon+text, only get a style for the text column
+ int style_col = c;
+ if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+ style_col++;
+ }
+
+ // Get the individual column style
+ // The column style overrides the row style, however if no column style
+ // is provided, we stick with the row style
+ if(getstyle(list, elm, row, style_col, listview->getstyledata, &style)) {
+ style_set = TRUE;
+ }
+ }
+
+ GValue value = G_VALUE_INIT;
+ switch(type) {
+ case UI_STRING_FREE: {
+ freevalue = TRUE;
+ }
+ case UI_STRING: {
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, data);
+ if(freevalue) {
+ free(data);
+ }
+ break;
+ }
+ case UI_INTEGER: {
+ g_value_init(&value, G_TYPE_INT);
+ intptr_t intptr = (intptr_t)data;
+ g_value_set_int(&value, (int)intptr);
+ break;
+ }
+ case UI_ICON: {
+ g_value_init(&value, G_TYPE_OBJECT);
+ UiIcon *icon = data;
+#if GTK_MAJOR_VERSION >= 4
+ g_value_set_object(&value, icon->info); // TODO: does this work?
+#else
+ if(!icon->pixbuf && icon->info) {
+ GError *error = NULL;
+ GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
+ icon->pixbuf = pixbuf;
+ }
+
+ if(icon->pixbuf) {
+ g_value_set_object(&value, icon->pixbuf);
+ }
+#endif
+ break;
+ }
+ case UI_ICON_TEXT:
+ case UI_ICON_TEXT_FREE: {
+ UiIcon *icon = data;
+#if GTK_MAJOR_VERSION >= 4
+ if(icon) {
+ GValue iconvalue = G_VALUE_INIT;
+ g_value_init(&iconvalue, G_TYPE_OBJECT);
+ g_value_set_object(&iconvalue, ui_icon_pixbuf(icon));
+ gtk_list_store_set_value(store, &iter, c, &iconvalue);
+ }
+#else
+ GValue pixbufvalue = G_VALUE_INIT;
+ if(icon) {
+ if(!icon->pixbuf && icon->info) {
+ GError *error = NULL;
+ GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
+ icon->pixbuf = pixbuf;
+ }
+ g_value_init(&pixbufvalue, G_TYPE_OBJECT);
+ g_value_set_object(&pixbufvalue, icon->pixbuf);
+ gtk_list_store_set_value(store, iter, c, &pixbufvalue);
+ }
+#endif
+ c++;
+
+ freevalue = FALSE;
+ char *str = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, str);
+ if(model->types[i] == UI_ICON_TEXT_FREE || freevalue) {
+ free(str);
+ }
+ break;
+ }
+ }
+
+ gtk_list_store_set_value(store, iter, c, &value);
+
+ if(style_set) {
+ int soff = listview->style_offset + i*6;
+
+ GValue style_set_value = G_VALUE_INIT;
+ g_value_init(&style_set_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&style_set_value, TRUE);
+ gtk_list_store_set_value(store, iter, soff, &style_set_value);
+
+ GValue style_weight_value = G_VALUE_INIT;
+ g_value_init(&style_weight_value, G_TYPE_INT);
+ if(style.text_style & UI_TEXT_STYLE_BOLD) {
+ g_value_set_int(&style_weight_value, 600);
+ } else {
+ g_value_set_int(&style_weight_value, 400);
+ }
+ gtk_list_store_set_value(store, iter, soff + 1, &style_weight_value);
+
+ GValue style_underline_value = G_VALUE_INIT;
+ g_value_init(&style_underline_value, G_TYPE_INT);
+ if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
+ g_value_set_int(&style_underline_value, PANGO_UNDERLINE_SINGLE);
+ } else {
+ g_value_set_int(&style_underline_value, PANGO_UNDERLINE_NONE);
+ }
+ gtk_list_store_set_value(store, iter, soff + 2, &style_underline_value);
+
+ GValue style_italic_value = G_VALUE_INIT;
+ g_value_init(&style_italic_value, G_TYPE_INT);
+ if(style.text_style & UI_TEXT_STYLE_ITALIC) {
+ g_value_set_int(&style_italic_value, PANGO_STYLE_ITALIC);
+ } else {
+ g_value_set_int(&style_italic_value, PANGO_STYLE_NORMAL);
+ }
+ gtk_list_store_set_value(store, iter, soff + 3, &style_italic_value);
+
+ GValue style_fgset_value = G_VALUE_INIT;
+ g_value_init(&style_fgset_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&style_fgset_value, style.fg_set);
+ gtk_list_store_set_value(store, iter, soff + 4, &style_fgset_value);
+
+ if(style.fg_set) {
+ char buf[8];
+ snprintf(buf, 8, "#%02X%02X%02X", (int)style.fg.red, (int)style.fg.green, (int)style.fg.blue);
+
+ GValue style_fg_value = G_VALUE_INIT;
+ g_value_init(&style_fg_value, G_TYPE_STRING);
+ g_value_set_string(&style_fg_value, buf);
+ gtk_list_store_set_value(store, iter, soff + 5, &style_fg_value);
+ }
+ }
+ }
+}
+
+static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
+ UiModel *model = listview->model;
+ int columns = model->columns;
+ GType *types = calloc(columns*8, sizeof(GType));
+ 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;
+ }
+ }
+ }
+ int s = 0;
+ for(int i=0;i<columns;i++) {
+ types[listview->style_offset+s] = G_TYPE_BOOLEAN; s++; // *-set
+ types[listview->style_offset+s] = G_TYPE_INT; s++; // weight
+ types[listview->style_offset+s] = G_TYPE_INT; s++; // underline
+ types[listview->style_offset+s] = G_TYPE_INT; s++; // style
+ types[listview->style_offset+s] = G_TYPE_BOOLEAN; s++; // foreground-set
+ types[listview->style_offset+s] = G_TYPE_STRING; s++; // foreground
+ }
+
+ GtkListStore *store = gtk_list_store_newv(c+s, types);
+ free(types);
+
+ if(list) {
+ void *elm = list->first(list);
+ int i = 0;
+ while(elm) {
+ // insert new row
+ GtkTreeIter iter;
+ gtk_list_store_insert (store, &iter, -1);
+
+ update_list_row(listview, store, &iter, list, elm, i++);
+
+ // next row
+ elm = list->next(list);
+ }
+ }
+
+ return store;
+}
+
+
+UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
+ // create treeview
+ GtkWidget *view = gtk_tree_view_new();
+ ui_set_name_and_style(view, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, view, args->groups);
+ GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+ GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
+#ifdef UI_GTK3
+#if GTK_MINOR_VERSION >= 8
+ //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE);
+#else
+ // TODO: implement for older gtk3
+#endif
+#else
+ // TODO: implement for gtk2
+#endif
+
+ UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
+
+ UiListView *listview = create_listview(obj, args);
+ listview->style_offset = 1;
+ if(!args->getvalue && !args->getvalue2) {
+ listview->getvalue = str_getvalue;
+ }
+ listview->model = model;
+ g_signal_connect(
+ view,
+ "destroy",
+ G_CALLBACK(ui_listview_destroy),
+ listview);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ // init listview
+ listview->widget = view;
+ listview->var = var;
+
+ UiList *list = var ? var->value : NULL;
+ GtkListStore *listmodel = create_list_store(listview, list);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
+ g_object_unref(listmodel);
+
+ // bind var
+ list->update = ui_listview_update;
+ list->getselection = ui_listview_getselection;
+ list->setselection = ui_listview_setselection;
+ list->obj = listview;
+
+ // add callback
+ UiTreeEventData *event = malloc(sizeof(UiTreeEventData));
+ event->obj = obj;
+ event->activate = args->onactivate;
+ event->activatedata = args->onactivatedata;
+ event->selection = args->onselection;
+ event->selectiondata = args->onselectiondata;
+ g_signal_connect(
+ view,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+
+ if(args->onactivate) {
+ g_signal_connect(
+ view,
+ "row-activated",
+ G_CALLBACK(ui_listview_activate_event),
+ event);
+ }
+ if(args->onselection) {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(
+ GTK_TREE_VIEW(view));
+ g_signal_connect(
+ selection,
+ "changed",
+ G_CALLBACK(ui_listview_selection_event),
+ event);
+ }
+ if(args->contextmenu) {
+ UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view);
+ ui_widget_set_contextmenu(view, menu);
+ }
+
+
+ // add widget to the current container
+ GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(scroll_area),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
+ SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
+
+ if(args->width > 0 || args->height > 0) {
+ int width = args->width;
+ int height = args->height;
+ if(width == 0) {
+ width = -1;
+ }
+ if(height == 0) {
+ height = -1;
+ }
+ gtk_widget_set_size_request(scroll_area, width, height);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, scroll_area, &layout);
+
+ return scroll_area;
+}
+
+void ui_listview_select(UIWIDGET listview, int index) {
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview));
+ GtkTreePath *path = gtk_tree_path_new_from_indicesv(&index, 1);
+ gtk_tree_selection_select_path(sel, path);
+ //g_object_unref(path);
+}
+
+void ui_combobox_select(UIWIDGET dropdown, int index) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index);
+}
+
+UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
+ // create treeview
+ GtkWidget *view = gtk_tree_view_new();
+
+ UiModel *model = args->model;
+ int columns = model ? model->columns : 0;
+
+ // find the last data column index
+ int addi = 0;
+ int style_offset = 0;
+ int i = 0;
+ for(;i<columns;i++) {
+ if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
+ addi++;
+ }
+ }
+ style_offset = i+addi;
+
+ // create columns and init cell renderers
+ addi = 0;
+ for(i=0;i<columns;i++) {
+ GtkTreeViewColumn *column = NULL;
+ if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, model->titles[i]);
+
+ GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
+ GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
+
+ gtk_tree_view_column_pack_end(column, textrenderer, TRUE);
+ gtk_tree_view_column_pack_start(column, iconrenderer, FALSE);
+
+
+ gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", addi + i);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "text", addi + i+1);
+
+ if(args->getstyle) {
+ int soff = style_offset + i*6;
+ gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff);
+
+ gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5);
+ }
+
+ addi++;
+ } else if (model->types[i] == UI_ICON) {
+ 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 *textrenderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(
+ model->titles[i],
+ textrenderer,
+ "text",
+ i + addi,
+ NULL);
+
+ if(args->getstyle) {
+ int soff = style_offset + i*6;
+ gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff);
+
+ gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4);
+ gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5);
+ }
+ }
+
+ int colsz = model->columnsize[i];
+ if(colsz > 0) {
+ gtk_tree_view_column_set_fixed_width(column, colsz);
+ } else if(colsz < 0) {
+ gtk_tree_view_column_set_expand(column, TRUE);
+ }
+
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ }
+
+ //gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
+#ifdef UI_GTK3
+ //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE);
+#else
+
+#endif
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL);
+ //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL);
+
+ // add TreeView as observer to the UiList to update the TreeView if the
+ // data changes
+ UiListView *tableview = create_listview(obj, args);
+ tableview->widget = view;
+ tableview->style_offset = style_offset;
+ g_signal_connect(
+ view,
+ "destroy",
+ G_CALLBACK(ui_listview_destroy),
+ tableview);
+
+ UiList *list = var ? var->value : NULL;
+ GtkListStore *listmodel = create_list_store(tableview, list);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
+ g_object_unref(listmodel);
+
+ // bind var
+ list->update = ui_listview_update;
+ list->getselection = ui_listview_getselection;
+ list->setselection = ui_listview_setselection;
+ list->obj = tableview;
+
+ // add callback
+ UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData));
+ event->obj = obj;
+ 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(args->onselection) {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(
+ GTK_TREE_VIEW(view));
+ g_signal_connect(
+ selection,
+ "changed",
+ G_CALLBACK(ui_listview_selection_event),
+ event);
+ }
+ // TODO: destroy callback
+
+
+ if(args->ondragstart) {
+ ui_listview_add_dnd(tableview, args);
+ }
+ if(args->ondrop) {
+ ui_listview_enable_drop(tableview, args);
+ }
+
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
+ if(args->multiselection) {
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+ }
+
+ // add widget to the current container
+ GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(scroll_area),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
+ SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
+
+ if(args->width > 0 || args->height > 0) {
+ int width = args->width;
+ int height = args->height;
+ if(width == 0) {
+ width = -1;
+ }
+ if(height == 0) {
+ height = -1;
+ }
+ gtk_widget_set_size_request(scroll_area, width, height);
+ }
+
+ if(args->contextmenu) {
+ UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, scroll_area);
+#if GTK_MAJOR_VERSION >= 4
+ ui_widget_set_contextmenu(scroll_area, menu);
+#else
+ ui_widget_set_contextmenu(view, menu);
+#endif
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, scroll_area, &layout);
+
+ return scroll_area;
+}
+
+
+
+void ui_listview_update(UiList *list, int i) {
+ UiListView *view = list->obj;
+ if(i < 0) {
+ GtkListStore *store = create_list_store(view, list);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store));
+ g_object_unref(G_OBJECT(store));
+ } else {
+ void *elm = list->get(list, i);
+ GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(view->widget));
+ GtkTreeIter iter;
+ if(gtk_tree_model_iter_nth_child(store, &iter, NULL, i)) {
+ update_list_row(view, GTK_LIST_STORE(store), &iter, list, elm, i);
+ }
+ }
+}
+
+UiListSelection ui_listview_getselection(UiList *list) {
+ UiListView *view = list->obj;
+ UiListSelection selection = ui_listview_selection(
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget)),
+ NULL);
+ return selection;
+}
+
+void ui_listview_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
+ UiListView *view = list->obj;
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget));
+ GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count);
+ gtk_tree_selection_select_path(sel, path);
+ //g_object_unref(path);
+ ui_setop_enable(FALSE);
+}
+
+
+
+/* --------------------------- ComboBox --------------------------- */
+
+UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
+ GtkWidget *combobox = gtk_combo_box_new();
+ if(args->width > 0) {
+ gtk_widget_set_size_request(combobox, args->width, -1);
+ }
+
+ ui_set_name_and_style(combobox, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, combobox, args->groups);
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, combobox, &layout);
+
+ UiListView *listview = create_listview(obj, args);
+ listview->widget = combobox;
+ listview->style_offset = 1;
+ listview->model = ui_model(obj->ctx, UI_STRING, "", -1);
+ g_signal_connect(
+ combobox,
+ "destroy",
+ G_CALLBACK(ui_listview_destroy),
+ listview);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+ UiList *list = var ? var->value : NULL;
+ GtkListStore *listmodel = create_list_store(listview, list);
+ if(var) {
+ listview->var = var;
+ list->update = ui_combobox_modelupdate;
+ list->getselection = ui_combobox_getselection;
+ list->setselection = ui_combobox_setselection;
+ list->obj = listview;
+ list->update(list, -1);
+ } else if(args->static_nelm > 0) {
+ listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
+ for(int i=0;i<args->static_nelm;i++) {
+ GtkTreeIter iter;
+ GValue value = G_VALUE_INIT;
+ gtk_list_store_insert(listmodel, &iter, -1);
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, listview->elements[i]);
+ gtk_list_store_set_value(listmodel, &iter, 0, &value);
+ }
+ }
+
+ if(listmodel) {
+ gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel));
+ g_object_unref(listmodel);
+ }
+
+ GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE);
+ gtk_cell_layout_set_attributes(
+ GTK_CELL_LAYOUT(combobox),
+ renderer,
+ "text",
+ 0,
+ NULL);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
+
+ // add callback
+ if(args->onactivate) {
+ UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = args->onactivatedata;
+ event->callback = args->onactivate;
+ event->value = 0;
+ event->customdata = listview;
+
+ g_signal_connect(
+ combobox,
+ "changed",
+ G_CALLBACK(ui_combobox_change_event),
+ event);
+ }
+
+ return combobox;
+}
+
+void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) {
+ int index = gtk_combo_box_get_active(widget);
+ UiListView *listview = e->customdata;
+ void *eventdata = NULL;
+ if(listview->var && listview->var->value) {
+ UiList *list = listview->var->value;
+ eventdata = ui_list_get(list, index);
+ } else if(listview->elements && listview->nelm > index) {
+ eventdata = listview->elements[index];
+ }
+
+ UiEvent event;
+ event.obj = e->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = eventdata;
+ event.intval = index;
+ event.set = ui_get_setop();
+ e->callback(&event, e->userdata);
+}
+
+void ui_combobox_modelupdate(UiList *list, int i) {
+ UiListView *view = list->obj;
+ GtkListStore *store = create_list_store(view, list);
+ gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store));
+ g_object_unref(store);
+}
+
+UiListSelection ui_combobox_getselection(UiList *list) {
+ UiListView *combobox = list->obj;
+ UiListSelection ret;
+ ret.rows = malloc(sizeof(int*));
+ ret.count = 1;
+ ret.rows[0] = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox->widget));
+ return ret;
+}
+
+void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
+ UiListView *combobox = list->obj;
+ if(selection.count > 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]);
+ }
+ ui_setop_enable(FALSE);
+}
+
+
+
+
+void ui_listview_activate_event(
+ GtkTreeView *treeview,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ UiTreeEventData *event)
+{
+ UiListSelection selection = ui_listview_selection(
+ gtk_tree_view_get_selection(treeview),
+ event);
+
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = &selection;
+ e.intval = selection.count > 0 ? selection.rows[0] : -1;
+ e.set = ui_get_setop();
+ event->activate(&e, event->activatedata);
+
+ if(selection.count > 0) {
+ free(selection.rows);
+ }
+}
+
+void ui_listview_selection_event(
+ GtkTreeSelection *treeselection,
+ UiTreeEventData *event)
+{
+ UiListSelection selection = ui_listview_selection(treeselection, event);
+
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = &selection;
+ e.intval = selection.count > 0 ? selection.rows[0] : -1;
+ e.set = ui_get_setop();
+ event->selection(&e, event->selectiondata);
+
+ if(selection.count > 0) {
+ free(selection.rows);
+ }
+}
+
+UiListSelection ui_listview_selection(
+ GtkTreeSelection *selection,
+ UiTreeEventData *event)
+{
+ GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ UiListSelection ls;
+ ls.count = g_list_length(rows);
+ ls.rows = calloc(ls.count, sizeof(int));
+ GList *r = rows;
+ int i = 0;
+ while(r) {
+ GtkTreePath *path = r->data;
+ ls.rows[i] = ui_tree_path_list_index(path);
+ r = r->next;
+ i++;
+ }
+ return ls;
+}
+
+int ui_tree_path_list_index(GtkTreePath *path) {
+ int depth = gtk_tree_path_get_depth(path);
+ if(depth == 0) {
+ fprintf(stderr, "UiError: treeview selection: depth == 0\n");
+ return -1;
+ }
+ int *indices = gtk_tree_path_get_indices(path);
+ return indices[depth - 1];
+}
+
+
+#endif
+
+
+#if GTK_MAJOR_VERSION >= 4
+
+static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double x, double y, void *data) {
+ //printf("drag prepare\n");
+ UiListView *listview = data;
+
+ UiDnD *dnd = ui_create_dnd();
+ GdkContentProvider *provider = NULL;
+
+
+ if(listview->ondragstart) {
+ UiEvent event;
+ event.obj = listview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = dnd;
+ event.eventdatatype = UI_EVENT_DATA_DND;
+ event.intval = 0;
+ event.set = ui_get_setop();
+ listview->ondragstart(&event, listview->ondragstartdata);
+ }
+
+ size_t numproviders = cxListSize(dnd->providers);
+ if(numproviders > 0) {
+ GdkContentProvider **providers = (GdkContentProvider**)cxListAt(dnd->providers, 0);
+ provider = gdk_content_provider_new_union(providers, numproviders);
+ }
+ ui_dnd_free(dnd);
+
+ return provider;
+}
+
+static void ui_listview_drag_begin(GtkDragSource *self, GdkDrag *drag, gpointer userdata) {
+ //printf("drag begin\n");
+}
+
+static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean delete_data, gpointer user_data) {
+ //printf("drag end\n");
+ UiListView *listview = user_data;
+ if(listview->ondragcomplete) {
+ UiDnD dnd;
+ dnd.target = NULL;
+ dnd.value = NULL;
+ dnd.providers = NULL;
+ dnd.selected_action = gdk_drag_get_selected_action(drag);
+ dnd.delete = delete_data;
+ dnd.accept = FALSE;
+
+ UiEvent event;
+ event.obj = listview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &dnd;
+ event.eventdatatype = UI_EVENT_DATA_DND;
+ event.intval = 0;
+ event.set = ui_get_setop();
+ listview->ondragcomplete(&event, listview->ondragcompletedata);
+ }
+}
+
+static gboolean ui_listview_drop(
+ GtkDropTarget *target,
+ const GValue* value,
+ gdouble x,
+ gdouble y,
+ gpointer user_data)
+{
+ UiListView *listview = user_data;
+ UiDnD dnd;
+ dnd.providers = NULL;
+ dnd.target = target;
+ dnd.value = value;
+ dnd.selected_action = 0;
+ dnd.delete = FALSE;
+ dnd.accept = FALSE;
+
+ if(listview->ondrop) {
+ dnd.accept = TRUE;
+ UiEvent event;
+ event.obj = listview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &dnd;
+ event.eventdatatype = UI_EVENT_DATA_DND;
+ event.intval = 0;
+ event.set = ui_get_setop();
+ listview->ondrop(&event, listview->ondropdata);
+ }
+
+ return dnd.accept;
+}
+
+void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
+ GtkDragSource *dragsource = gtk_drag_source_new();
+ gtk_widget_add_controller(listview->widget, GTK_EVENT_CONTROLLER(dragsource));
+ g_signal_connect (dragsource, "prepare", G_CALLBACK (ui_listview_dnd_prepare), listview);
+ g_signal_connect(
+ dragsource,
+ "drag-begin",
+ G_CALLBACK(ui_listview_drag_begin),
+ listview);
+ g_signal_connect(
+ dragsource,
+ "drag-end",
+ G_CALLBACK(ui_listview_drag_end),
+ listview);
+}
+
+void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
+ GtkDropTarget *target = gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY);
+ gtk_widget_add_controller(listview->widget, GTK_EVENT_CONTROLLER(target));
+ GType default_types[2] = { GDK_TYPE_FILE_LIST, G_TYPE_STRING };
+ gtk_drop_target_set_gtypes(target, default_types, 2);
+ g_signal_connect(target, "drop", G_CALLBACK(ui_listview_drop), listview);
+}
+
+#else
+
+static GtkTargetEntry targetentries[] =
+{
+ { "STRING", 0, 0 },
+ { "text/plain", 0, 1 },
+ { "text/uri-list", 0, 2 },
+};
+
+static void ui_listview_drag_getdata(
+ GtkWidget* self,
+ GdkDragContext* context,
+ GtkSelectionData* data,
+ guint info,
+ guint time,
+ gpointer user_data)
+{
+ UiListView *listview = user_data;
+ UiDnD dnd;
+ dnd.context = context;
+ dnd.data = data;
+ dnd.selected_action = 0;
+ dnd.delete = FALSE;
+ dnd.accept = FALSE;
+
+ if(listview->ondragstart) {
+ UiEvent event;
+ event.obj = listview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &dnd;
+ event.intval = 0;
+ event.set = ui_get_setop();
+ listview->ondragstart(&event, listview->ondragstartdata);
+ }
+}
+
+static void ui_listview_drag_end(
+ GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ gpointer user_data)
+{
+ UiListView *listview = user_data;
+ UiDnD dnd;
+ dnd.context = context;
+ dnd.data = NULL;
+ dnd.selected_action = gdk_drag_context_get_selected_action(context);
+ dnd.delete = dnd.selected_action == UI_DND_ACTION_MOVE ? TRUE : FALSE;
+ dnd.accept = FALSE;
+ if(listview->ondragcomplete) {
+ UiEvent event;
+ event.obj = listview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &dnd;
+ event.intval = 0;
+ event.set = ui_get_setop();
+ listview->ondragcomplete(&event, listview->ondragcompletedata);
+ }
+}
+
+void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
+ gtk_tree_view_enable_model_drag_source(
+ GTK_TREE_VIEW(listview->widget),
+ GDK_BUTTON1_MASK,
+ targetentries,
+ 2,
+ GDK_ACTION_COPY);
+
+ g_signal_connect(listview->widget, "drag-data-get", G_CALLBACK(ui_listview_drag_getdata), listview);
+ g_signal_connect(listview->widget, "drag-end", G_CALLBACK(ui_listview_drag_end), listview);
+}
+
+
+
+
+static void ui_listview_drag_data_received(
+ GtkWidget *self,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ gpointer user_data)
+{
+ UiListView *listview = user_data;
+ UiDnD dnd;
+ dnd.context = context;
+ dnd.data = data;
+ dnd.selected_action = 0;
+ dnd.delete = FALSE;
+ dnd.accept = FALSE;
+
+ if(listview->ondrop) {
+ dnd.accept = TRUE;
+ UiEvent event;
+ event.obj = listview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &dnd;
+ event.intval = 0;
+ event.set = ui_get_setop();
+ listview->ondrop(&event, listview->ondropdata);
+ }
+}
+
+void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
+ gtk_tree_view_enable_model_drag_dest(
+ GTK_TREE_VIEW(listview->widget),
+ targetentries,
+ 3,
+ GDK_ACTION_COPY);
+ if(listview->ondrop) {
+ g_signal_connect(listview->widget, "drag_data_received", G_CALLBACK(ui_listview_drag_data_received), listview);
+ }
+}
+
+#endif
+
+
+GtkWidget* ui_get_tree_widget(UIWIDGET widget) {
+ return SCROLLEDWINDOW_GET_CHILD(widget);
+}
+
+static char** targets2array(char *target0, va_list ap, int *nelm) {
+ int al = 16;
+ char **targets = calloc(16, sizeof(char*));
+ targets[0] = target0;
+
+ int i = 1;
+ char *target;
+ while((target = va_arg(ap, char*)) != NULL) {
+ if(i >= al) {
+ al *= 2;
+ targets = realloc(targets, al*sizeof(char*));
+ }
+ targets[i] = target;
+ i++;
+ }
+
+ *nelm = i;
+ return targets;
+}
+
+/*
+static GtkTargetEntry* targetstr2gtktargets(char **str, int nelm) {
+ GtkTargetEntry *targets = calloc(nelm, sizeof(GtkTargetEntry));
+ for(int i=0;i<nelm;i++) {
+ targets[i].target = str[i];
+ }
+ return targets;
+}
+*/
+
+void ui_table_dragsource(UIWIDGET tablewidget, int actions, char *target0, ...) {
+ va_list ap;
+ va_start(ap, target0);
+ int nelm;
+ char **targets = targets2array(target0, ap, &nelm);
+ va_end(ap);
+
+ // disabled
+ //ui_table_dragsource_a(tablewidget, actions, targets, nelm);
+
+ free(targets);
+}
+
+/*
+void ui_table_dragsource_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) {
+ GtkTargetEntry* t = targetstr2gtktargets(targets, nelm);
+ gtk_tree_view_enable_model_drag_source(
+ GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)),
+ GDK_BUTTON1_MASK,
+ t,
+ nelm,
+ GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
+ free(t);
+}
+
+
+void ui_table_dragdest(UIWIDGET tablewidget, int actions, char *target0, ...) {
+ va_list ap;
+ va_start(ap, target0);
+ int nelm;
+ char **targets = targets2array(target0, ap, &nelm);
+ va_end(ap);
+ ui_table_dragdest_a(tablewidget, actions, targets, nelm);
+ free(targets);
+}
+
+void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) {
+ GtkTargetEntry* t = targetstr2gtktargets(targets, nelm);
+ gtk_tree_view_enable_model_drag_dest(
+ GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)),
+ t,
+ nelm,
+ GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
+ free(t);
+}
+*/
+
+void ui_listview_destroy(GtkWidget *w, UiListView *v) {
+ //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL);
+ if(v->var) {
+ ui_destroy_boundvar(v->obj->ctx, v->var);
+ }
+ if(v->elements) {
+ for(int i=0;i<v->nelm;i++) {
+ free(v->elements[i]);
+ }
+ free(v->elements);
+ }
+#if GTK_CHECK_VERSION(4, 10, 0)
+ free(v->columns);
+ pango_attr_list_unref(v->current_row_attributes);
+ cxMapFree(v->bound_rows);
+#endif
+ free(v->selection.rows);
+ free(v);
+}
+
+
+/* ------------------------------ Source List ------------------------------ */
+
+static ui_sourcelist_update_func sourcelist_update_finished_callback;
+
+void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb) {
+ sourcelist_update_finished_callback = cb;
+}
+
+static void ui_sourcelist_update_finished(void) {
+ if(sourcelist_update_finished_callback) {
+ sourcelist_update_finished_callback();
+ }
+}
+
+static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
+ cxListFree(v->sublists);
+ free(v);
+}
+
+static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) {
+ free(sublist->header);
+ ui_destroy_boundvar(obj->ctx, sublist->var);
+ cxListFree(sublist->widgets);
+}
+
+static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) {
+ // first rows in sublists have the ui_listbox property
+ UiListBox *listbox = g_object_get_data(G_OBJECT(row), "ui_listbox");
+ if(!listbox) {
+ return;
+ }
+
+ UiListBoxSubList *sublist = g_object_get_data(G_OBJECT(row), "ui_listbox_sublist");
+ if(!sublist) {
+ return;
+ }
+
+ if(sublist->separator) {
+ GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
+ gtk_list_box_row_set_header(row, separator);
+ } else if(sublist->header && !listbox->header_is_item) {
+ GtkWidget *header = gtk_label_new(sublist->header);
+ gtk_widget_set_halign(header, GTK_ALIGN_START);
+ if(row == listbox->first_row) {
+ WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header-first");
+ } else {
+ WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header");
+ }
+ gtk_list_box_row_set_header(row, header);
+ }
+}
+
+#ifdef UI_GTK3
+typedef struct _UiSidebarListBoxClass {
+ GtkListBoxClass parent_class;
+} UiSidebarListBoxClass;
+
+typedef struct _UiSidebarListBox {
+ GtkListBox parent_instance;
+} UiSidebarListBox;
+
+G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box, GTK_TYPE_LIST_BOX)
+
+/* Initialize the instance */
+static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) {
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+ gtk_widget_class_set_css_name (widget_class, "placessidebar");
+}
+
+static void ui_sidebar_list_box_init(UiSidebarListBox *self) {
+
+}
+#endif
+
+
+static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *sublist) {
+ UiListBoxSubList uisublist;
+ uisublist.var = uic_widget_var(
+ uilistbox->obj->ctx,
+ uilistbox->obj->ctx,
+ sublist->value,
+ sublist->varname,
+ UI_VAR_LIST);
+ uisublist.numitems = 0;
+ uisublist.header = sublist->header ? strdup(sublist->header) : NULL;
+ uisublist.separator = sublist->separator;
+ uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ uisublist.listbox = uilistbox;
+ uisublist.userdata = sublist->userdata;
+ uisublist.index = cxListSize(sublists);
+ uisublist.startpos = 0;
+ cxListAdd(sublists, &uisublist);
+
+ // bind UiList
+ UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)-1);
+ if(uisublist.var && uisublist.var->value) {
+ UiList *list = uisublist.var->value;
+ list->obj = sublist_ptr;
+ list->update = ui_listbox_list_update;
+ }
+}
+
+UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
+#ifdef UI_GTK3
+ GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL);
+#else
+ GtkWidget *listbox = gtk_list_box_new();
+#endif
+ if(!args->style_class) {
+#if GTK_MAJOR_VERSION >= 4
+ WIDGET_ADD_CSS_CLASS(listbox, "navigation-sidebar");
+#else
+ WIDGET_ADD_CSS_CLASS(listbox, "sidebar");
+#endif
+ }
+ gtk_list_box_set_header_func(GTK_LIST_BOX(listbox), listbox_create_header, NULL, NULL);
+ GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
+ SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
+
+ ui_set_name_and_style(listbox, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, listbox, args->groups);
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, scroll_area, &layout);
+
+ UiListBox *uilistbox = malloc(sizeof(UiListBox));
+ uilistbox->obj = obj;
+ uilistbox->listbox = GTK_LIST_BOX(listbox);
+ uilistbox->header_is_item = args->header_is_item;
+ uilistbox->getvalue = args->getvalue;
+ uilistbox->getvaluedata = args->getvaluedata;
+ uilistbox->onactivate = args->onactivate;
+ uilistbox->onactivatedata = args->onactivatedata;
+ uilistbox->onbuttonclick = args->onbuttonclick;
+ uilistbox->onbuttonclickdata = args->onbuttonclickdata;
+ uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4);
+ uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
+ uilistbox->sublists->collection.destructor_data = obj;
+ uilistbox->first_row = NULL;
+
+ if(args->sublists) {
+ // static sublist initalization
+ if(args->numsublists == 0 && args->sublists) {
+ args->numsublists = INT_MAX;
+ }
+ for(int i=0;i<args->numsublists;i++) {
+ UiSubList sublist = args->sublists[i];
+ if(!sublist.varname && !sublist.value) {
+ break;
+ }
+
+ add_sublist(uilistbox, uilistbox->sublists, &sublist);
+ }
+
+ // fill items
+ ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
+ } else {
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST);
+ if(var) {
+ UiList *list = var->value;
+ list->obj = uilistbox;
+ list->update = ui_listbox_dynamic_update;
+
+ ui_listbox_dynamic_update(list, -1);
+ }
+ }
+
+ // register uilistbox for both widgets, so it doesn't matter which
+ // widget is used later
+ g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox);
+ g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox);
+
+ if(args->contextmenu) {
+ UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, listbox);
+ ui_widget_set_contextmenu(listbox, menu);
+ }
+
+ // signals
+ g_signal_connect(
+ listbox,
+ "destroy",
+ G_CALLBACK(ui_destroy_sourcelist),
+ uilistbox);
+
+ if(args->onactivate) {
+ g_signal_connect(
+ listbox,
+ "row-activated",
+ G_CALLBACK(ui_listbox_row_activate),
+ NULL);
+ }
+
+ return scroll_area;
+}
+
+void ui_listbox_dynamic_update(UiList *list, int x) {
+ UiListBox *uilistbox = list->obj;
+
+ // unbind/free previous list vars
+ CxIterator i = cxListIterator(uilistbox->sublists);
+ cx_foreach(UiListBoxSubList *, s, i) {
+ // TODO: "unbind/free previous list vars" will also remove
+ // the widget list. This makes the widget optimization
+ // in ui_listbox_update_sublist pointless
+ // Is it actually possible to not recreate the whole list?
+ CxIterator r = cxListIterator(s->widgets);
+ cx_foreach(GtkWidget*, widget, r) {
+ LISTBOX_REMOVE(uilistbox->listbox, widget);
+ }
+
+ if(s->var) {
+ UiList *sl = s->var->value;
+ sl->obj = NULL;
+ sl->update = NULL;
+ if(s->var->type == UI_VAR_SPECIAL) {
+ ui_free(s->var->from_ctx, s->var);
+ }
+ }
+ }
+
+ cxListFree(uilistbox->sublists);
+ CxList *new_sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), list->count(list));
+ uilistbox->sublists = new_sublists;
+
+ UiSubList *sublist = list->first(list);
+ while(sublist) {
+ add_sublist(uilistbox, new_sublists, sublist);
+ sublist = list->next(list);
+ }
+
+ ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
+}
+
+void ui_listbox_update(UiListBox *listbox, int from, int to) {
+ CxIterator i = cxListIterator(listbox->sublists);
+ size_t pos = 0;
+ cx_foreach(UiListBoxSubList *, sublist, i) {
+ if(i.index < from) {
+ pos += sublist->numitems;
+ continue;
+ }
+ if(i.index > to) {
+ break;
+ }
+
+ // reload sublist
+ sublist->startpos = pos;
+ ui_listbox_update_sublist(listbox, sublist, pos);
+ pos += sublist->numitems;
+ }
+
+ ui_sourcelist_update_finished();
+}
+
+static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) {
+ UiListBoxSubList *sublist = data->customdata0;
+
+ UiSubListEventData eventdata;
+ eventdata.list = sublist->var->value;
+ eventdata.sublist_index = sublist->index;
+ eventdata.row_index = data->value0;
+ eventdata.sublist_userdata = sublist->userdata;
+ eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index);
+ eventdata.event_data = data->customdata2;
+
+ UiEvent event;
+ event.obj = data->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &eventdata;
+ event.eventdatatype = UI_EVENT_DATA_SUBLIST;
+ event.intval = data->value0;
+ event.set = ui_get_setop();
+
+ if(data->callback2) {
+ data->callback2(&event, data->userdata2);
+ }
+
+ if(data->customdata3) {
+ UIMENU menu = data->customdata3;
+ g_object_set_data(G_OBJECT(button), "ui-button-popup", menu);
+ gtk_popover_popup(GTK_POPOVER(menu));
+ }
+}
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+static void button_popover_closed(GtkPopover *popover, GtkWidget *button) {
+ g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL);
+ if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) {
+ g_object_set_data(G_OBJECT(button), "ui-button-invisible", NULL);
+ gtk_widget_set_visible(button, FALSE);
+ }
+}
+#endif
+
+static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
+ UiBool is_header = index < 0;
+
+ GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
+ if(item->icon) {
+ GtkWidget *icon = ICON_IMAGE(item->icon);
+ BOX_ADD(hbox, icon);
+ }
+ GtkWidget *label = gtk_label_new(item->label);
+ if(is_header) {
+ WIDGET_ADD_CSS_CLASS(label, "ui-listbox-header-row");
+ }
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ BOX_ADD_EXPAND(hbox, label);
+ if(item->badge) {
+
+ }
+ LISTBOX_ROW_SET_CHILD(row, hbox);
+
+ // signals
+ UiEventDataExt *event = malloc(sizeof(UiEventDataExt));
+ memset(event, 0, sizeof(UiEventDataExt));
+ event->obj = listbox->obj;
+ event->customdata0 = sublist;
+ event->customdata1 = sublist->var;
+ event->customdata2 = item->eventdata;
+ event->callback = listbox->onactivate;
+ event->userdata = listbox->onactivatedata;
+ event->callback2 = listbox->onbuttonclick;
+ event->userdata2 = listbox->onbuttonclickdata;
+ event->value0 = index;
+
+ // TODO: semi-memory leak when listbox_fill_row is called again for the same row
+ // each row update will create a new UiEventDataExt object and a separate destroy handler
+
+ g_signal_connect(
+ row,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+
+ g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event);
+
+ // badge
+ if(item->badge) {
+ GtkWidget *badge = gtk_label_new(item->badge);
+ WIDGET_ADD_CSS_CLASS(badge, "ui-badge");
+#if GTK_CHECK_VERSION(3, 14, 0)
+ gtk_widget_set_valign(badge, GTK_ALIGN_CENTER);
+ BOX_ADD(hbox, badge);
+#else
+ GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0);
+ gtk_container_add(GTK_CONTAINER(align), badge);
+ BOX_ADD(hbox, align);
+#endif
+ }
+ // button
+ if(item->button_icon || item->button_label) {
+ GtkWidget *button = gtk_button_new();
+ gtk_button_set_label(GTK_BUTTON(button), item->button_label);
+ ui_button_set_icon_name(button, item->button_icon);
+ WIDGET_ADD_CSS_CLASS(button, "flat");
+ BOX_ADD(hbox, button);
+ g_signal_connect(
+ button,
+ "clicked",
+ G_CALLBACK(listbox_button_clicked),
+ event
+ );
+ gtk_widget_set_visible(button, FALSE);
+
+ g_object_set_data(G_OBJECT(row), "ui-listbox-row-button", button);
+
+ // menu
+ if(item->button_menu) {
+ UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button);
+ event->customdata3 = menu;
+ g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button);
+ ui_menubuilder_unref(item->button_menu);
+ }
+ }
+}
+
+static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) {
+ int header_row = listbox->header_is_item && sublist->header ? 1 : 0;
+ GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index + header_row);
+ if(!row) {
+ return;
+ }
+ UiList *list = sublist->var->value;
+ if(!list) {
+ return;
+ }
+
+ void *elm = list->get(list, index);
+ UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
+ if(listbox->getvalue) {
+ listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
+ } else {
+ item.label = strdup(elm);
+ }
+
+ LISTBOX_ROW_REMOVE_CHILD(row);
+
+ listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index);
+
+ // cleanup
+ free(item.label);
+ free(item.icon);
+ free(item.button_label);
+ free(item.button_icon);
+ free(item.badge);
+}
+
+static void listbox_row_on_enter(GtkWidget *row) {
+ GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button");
+ if(button) {
+ gtk_widget_set_visible(button, TRUE);
+ }
+}
+
+static void listbox_row_on_leave(GtkWidget *row) {
+ GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button");
+ if(button) {
+ if(!g_object_get_data(G_OBJECT(button), "ui-button-popup")) {
+ gtk_widget_set_visible(button, FALSE);
+ } else {
+ g_object_set_data(G_OBJECT(button), "ui-button-invisible", (void*)1);
+ }
+ }
+}
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void listbox_row_enter(
+ GtkEventControllerMotion* self,
+ gdouble x,
+ gdouble y,
+ GtkWidget *row)
+{
+ listbox_row_on_enter(row);
+}
+
+static void listbox_row_leave(
+ GtkEventControllerMotion* self,
+ GtkWidget *row)
+{
+ listbox_row_on_leave(row);
+}
+#else
+static gboolean listbox_row_enter(
+ GtkWidget *row,
+ GdkEventCrossing event,
+ gpointer user_data)
+{
+ listbox_row_on_enter(row);
+ return FALSE;
+}
+
+
+static gboolean listbox_row_leave(
+ GtkWidget *row,
+ GdkEventCrossing *event,
+ gpointer user_data)
+{
+ listbox_row_on_leave(row);
+ return FALSE;
+}
+
+#endif
+
+void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
+ // clear sublist
+ CxIterator r = cxListIterator(sublist->widgets);
+ cx_foreach(GtkWidget*, widget, r) {
+ LISTBOX_REMOVE(listbox->listbox, widget);
+ }
+ cxListClear(sublist->widgets);
+
+ sublist->numitems = 0;
+
+ // create items for each UiList element
+ if(!sublist->var) {
+ return;
+ }
+ UiList *list = sublist->var->value;
+ if(!list) {
+ return;
+ }
+
+ int index = 0;
+ void *elm = list->first(list);
+ void *first = elm;
+
+ if(sublist->header && !listbox->header_is_item && !elm) {
+ // empty row for header
+ GtkWidget *row = gtk_list_box_row_new();
+ cxListAdd(sublist->widgets, row);
+ g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
+ g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
+ //intptr_t rowindex = listbox_insert_index + index;
+ //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
+ gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
+ sublist->numitems = 1;
+ return;
+ }
+
+ int first_index = 0;
+ int header_row = 0;
+ if(listbox->header_is_item && sublist->header) {
+ index = -1;
+ first_index = -1;
+ header_row = 1;
+ elm = sublist->header;
+ }
+
+ while(elm) {
+ UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
+ if(listbox->getvalue) {
+ listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
+ } else {
+ item.label = strdup(elm);
+ }
+
+ if(item.label == NULL && index == -1 && sublist->header) {
+ item.label = strdup(sublist->header);
+ }
+
+ // create listbox item
+ GtkWidget *row = gtk_list_box_row_new();
+#if GTK_CHECK_VERSION(4, 0, 0)
+ GtkEventController *motion_controller = gtk_event_controller_motion_new();
+ gtk_widget_add_controller(GTK_WIDGET(row), motion_controller);
+ g_signal_connect(motion_controller, "enter", G_CALLBACK(listbox_row_enter), row);
+ g_signal_connect(motion_controller, "leave", G_CALLBACK(listbox_row_leave), row);
+#else
+ gtk_widget_set_events(GTK_WIDGET(row), GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+ g_signal_connect(row, "enter-notify-event", G_CALLBACK(listbox_row_enter), NULL);
+ g_signal_connect(row, "leave-notify-event", G_CALLBACK(listbox_row_leave), NULL);
+#endif
+
+ listbox_fill_row(listbox, row, sublist, &item, index);
+ if(index == first_index) {
+ // first row in the sublist, set ui_listbox data to the row
+ // which is then used by the headerfunc
+ g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
+ g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
+
+ if(listbox_insert_index == 0) {
+ // first row in the GtkListBox
+ listbox->first_row = GTK_LIST_BOX_ROW(row);
+ }
+ }
+ //intptr_t rowindex = listbox_insert_index + index;
+ //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
+ gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index + header_row);
+ cxListAdd(sublist->widgets, row);
+
+ // cleanup
+ free(item.label);
+ free(item.icon);
+ free(item.button_label);
+ free(item.button_icon);
+ free(item.badge);
+
+ // next row
+ elm = index >= 0 ? list->next(list) : first;
+ index++;
+ }
+
+ sublist->numitems = cxListSize(sublist->widgets);
+}
+
+void ui_listbox_list_update(UiList *list, int i) {
+ UiListBoxSubList *sublist = list->obj;
+ if(i < 0) {
+ ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
+ size_t pos = 0;
+ CxIterator it = cxListIterator(sublist->listbox->sublists);
+ cx_foreach(UiListBoxSubList *, ls, it) {
+ ls->startpos = pos;
+ pos += ls->numitems;
+ }
+ } else {
+ update_sublist_item(sublist->listbox, sublist, i);
+ }
+
+ ui_sourcelist_update_finished();
+}
+
+void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
+ UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata");
+ if(!data) {
+ return;
+ }
+ UiListBoxSubList *sublist = data->customdata0;
+
+ UiSubListEventData eventdata;
+ eventdata.list = sublist->var->value;
+ eventdata.sublist_index = sublist->index;
+ eventdata.row_index = data->value0;
+ eventdata.sublist_userdata = sublist->userdata;
+ eventdata.row_data = eventdata.row_index >= 0 ? eventdata.list->get(eventdata.list, eventdata.row_index) : NULL;
+ eventdata.event_data = data->customdata2;
+
+ UiEvent event;
+ event.obj = data->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = &eventdata;
+ event.eventdatatype = UI_EVENT_DATA_SUBLIST;
+ event.intval = data->value0;
+ event.set = ui_get_setop();
+
+ if(data->callback) {
+ data->callback(&event, data->userdata);
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TREE_H
+#define TREE_H
+
+#include "../ui/tree.h"
+#include "toolkit.h"
+
+#include <cx/array_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiColData UiColData;
+
+#if GTK_CHECK_VERSION(4, 10, 0)
+typedef struct UiRowItems {
+ int bound;
+ GtkListItem *items[];
+} UiRowItems;
+#endif
+
+typedef struct UiListView {
+ UiObject *obj;
+ GtkWidget *widget;
+ UiVar *var;
+ UiModel *model;
+ ui_getvaluefunc2 getvalue;
+ void *getvaluedata;
+ ui_getstylefunc getstyle;
+ void *getstyledata;
+ char **elements;
+ size_t nelm;
+ int current_row;
+ UiTextStyle row_style;
+ UiBool apply_row_style;
+#if GTK_CHECK_VERSION(4, 10, 0)
+ CxMap *bound_rows;
+ GListStore *liststore;
+ GtkSelectionModel *selectionmodel;
+ UiColData *columns;
+ int numcolumns;
+ PangoAttrList *current_row_attributes;
+#else
+ int style_offset;
+#endif
+ ui_callback onactivate;
+ void *onactivatedata;
+ ui_callback onselection;
+ void *onselectiondata;
+ ui_callback ondragstart;
+ void *ondragstartdata;
+ ui_callback ondragcomplete;
+ void *ondragcompletedata;
+ ui_callback ondrop;
+ void *ondropdata;
+ ui_list_savefunc onsave;
+ void *onsavedata;
+ UiListSelection selection;
+
+} UiListView;
+
+struct UiColData {
+ UiListView *listview;
+ int model_column;
+ int data_column;
+};
+
+typedef struct UiTreeEventData {
+ UiObject *obj;
+ ui_callback activate;
+ ui_callback selection;
+ void *activatedata;
+ void *selectiondata;
+} UiTreeEventData;
+
+typedef struct UiListBox UiListBox;
+
+typedef struct UiListBoxSubList {
+ UiVar *var;
+ size_t numitems;
+ char *header;
+ UiBool separator;
+ CxList *widgets;
+ UiListBox *listbox;
+ void *userdata;
+ size_t index;
+ size_t startpos;
+} UiListBoxSubList;
+
+struct UiListBox {
+ UiObject *obj;
+ GtkListBox *listbox;
+ CxList *sublists; // contains UiListBoxSubList elements
+ ui_sublist_getvalue_func getvalue;
+ void *getvaluedata;
+ ui_callback onactivate;
+ void *onactivatedata;
+ ui_callback onbuttonclick;
+ void *onbuttonclickdata;
+ GtkListBoxRow *first_row;
+ UiBool header_is_item;
+};
+
+
+#if GTK_CHECK_VERSION(4, 10, 0)
+
+void ui_update_liststore(GListStore *liststore, UiList *list);
+void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm);
+
+void ui_listview_update2(UiList *list, int i);
+UiListSelection ui_listview_getselection2(UiList *list);
+void ui_listview_setselection2(UiList *list, UiListSelection selection);
+
+void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata);
+void ui_columnview_activate(void *ignore, guint position, gpointer userdata);
+void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer user_data);
+
+void ui_dropdown_activate(GtkDropDown* self, gpointer userdata);
+
+#endif
+
+void* ui_strmodel_getvalue(void *elm, int column);
+
+UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata);
+UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb);
+
+GtkWidget* ui_get_tree_widget(UIWIDGET widget);
+
+void ui_listview_update(UiList *list, int i);
+UiListSelection ui_listview_getselection(UiList *list);
+void ui_listview_setselection(UiList *list, UiListSelection selection);
+
+void ui_combobox_destroy(GtkWidget *w, UiListView *v);
+void ui_listview_destroy(GtkWidget *w, UiListView *v);
+
+#if GTK_CHECK_VERSION(4, 10, 0)
+
+#else
+void ui_listview_activate_event(
+ GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ UiTreeEventData *event);
+void ui_listview_selection_event(
+ GtkTreeSelection *treeselection,
+ UiTreeEventData *event);
+UiListSelection ui_listview_selection(
+ GtkTreeSelection *selection,
+ UiTreeEventData *event);
+int ui_tree_path_list_index(GtkTreePath *path);
+#endif
+
+void ui_listview_add_dnd(UiListView *listview, UiListArgs *args);
+void ui_listview_enable_drop(UiListView *listview, UiListArgs *args);
+
+UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata);
+GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata);
+void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e);
+void ui_combobox_modelupdate(UiList *list, int i);
+UiListSelection ui_combobox_getselection(UiList *list);
+void ui_combobox_setselection(UiList *list, UiListSelection selection);
+
+void ui_listbox_dynamic_update(UiList *list, int i);
+void ui_listbox_update(UiListBox *listbox, int from, int to);
+void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index);
+void ui_listbox_list_update(UiList *list, int i);
+
+void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TREE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+#include "menu.h"
+#include "toolkit.h"
+#include "widget.h"
+#include "../common/context.h"
+#include "../common/menu.h"
+#include "../common/types.h"
+#include "../ui/properties.h"
+#include "../ui/window.h"
+#include "container.h"
+
+#include <cx/linked_list.h>
+#include <cx/array_list.h>
+#include <cx/printf.h>
+
+#if GTK_MAJOR_VERSION <= 3
+
+static ui_menu_add_f createMenuItem[] = {
+ /* UI_MENU */ add_menu_widget,
+ /* UI_MENU_ITEM */ add_menuitem_widget,
+ /* UI_MENU_CHECK_ITEM */ add_checkitem_widget,
+ /* UI_MENU_RADIO_ITEM */ add_radioitem_widget,
+ /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_CHECKITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_RADIOITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_SEPARATOR */ add_menuseparator_widget
+};
+
+// private menu functions
+GtkWidget *ui_create_menubar(UiObject *obj) {
+ UiMenu *menus_begin = uic_get_menu_list();
+ if(menus_begin == NULL) {
+ return NULL;
+ }
+
+ GtkWidget *mb = gtk_menu_bar_new();
+
+ UiMenu *ls = menus_begin;
+ while(ls) {
+ UiMenu *menu = ls;
+ add_menu_widget(mb, 0, &menu->item, obj);
+
+ ls = (UiMenu*)ls->item.next;
+ }
+
+ 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;
+
+ GtkWidget *menu_widget = gtk_menu_new();
+ GtkWidget *menu_item = gtk_menu_item_new_with_mnemonic(menu->label);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu_widget);
+
+ ui_add_menu_items(menu_widget, i, menu, obj);
+
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(parent), menu_item);
+}
+
+void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuItem *i = (UiMenuItem*)item;
+
+ //GtkWidget *widget = gtk_menu_item_new_with_label(i->title);
+ GtkWidget *widget = gtk_menu_item_new_with_mnemonic(i->label);
+
+ if(i->callback != NULL) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = i->userdata;
+ event->callback = i->callback;
+ event->value = 0;
+ event->customdata = NULL;
+
+ g_signal_connect(
+ widget,
+ "activate",
+ G_CALLBACK(ui_menu_event_wrapper),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
+
+ if(i->groups) {
+ 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);
+ cxListFree(groups);
+ }
+}
+
+void add_menuseparator_widget(
+ GtkWidget *parent,
+ int index,
+ UiMenuItemI *item,
+ UiObject *obj)
+{
+ gtk_menu_shell_append(
+ GTK_MENU_SHELL(parent),
+ gtk_separator_menu_item_new());
+}
+
+void add_checkitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuCheckItem *ci = (UiMenuCheckItem*)item;
+ GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
+ gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
+
+ if(ci->callback) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = ci->userdata;
+ event->callback = ci->callback;
+ event->value = 0;
+ event->customdata = NULL;
+
+ g_signal_connect(
+ widget,
+ "toggled",
+ G_CALLBACK(ui_menu_event_toggled),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+}
+
+void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
+ // TODO
+}
+
+static void menuitem_list_remove_binding(void *obj) {
+ UiActiveMenuItemList *ls = obj;
+ UiList *list = ls->var->value;
+ CxList *bindings = list->obj;
+ if(bindings) {
+ (void)cxListFindRemove(bindings, obj);
+ if(cxListSize(bindings) == 0) {
+ cxListFree(bindings);
+ list->obj = NULL;
+ list->update = NULL;
+ }
+ }
+}
+
+void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuItemList *il = (UiMenuItemList*)item;
+ const CxAllocator *a = obj->ctx->allocator;
+
+ UiActiveMenuItemList *ls = cxMalloc(
+ a,
+ sizeof(UiActiveMenuItemList));
+
+ ls->object = obj;
+ ls->menu = GTK_MENU_SHELL(p);
+ ls->index = index;
+ ls->oldcount = 0;
+ ls->getvalue = il->getvalue;
+
+ UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+ //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+ ls->var = var;
+ if(var) {
+ UiList *list = var->value;
+ list->update = ui_menulist_update;
+ list->getselection = NULL;
+ list->setselection = NULL;
+
+ // It is possible, that the UiVar is from a global shared context,
+ // used by multiple windows. To support this usecase, the list->obj
+ // binding object is a list of all connected UiActiveMenuItemList.
+ CxList *bindings = list->obj;
+ if(!bindings) {
+ bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ list->obj = bindings;
+ }
+ cxListAdd(bindings, ls);
+
+ // The destruction of the toplevel obj must remove the menulist binding
+ uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls);
+
+ ui_update_menuitem_list(ls);
+ }
+
+ ls->callback = il->callback;
+ ls->userdata = il->userdata;
+}
+
+void ui_menulist_update(UiList *list, int ignored) {
+ CxList *bindings = list->obj;
+ CxIterator i = cxListIterator(bindings);
+ cx_foreach(UiActiveMenuItemList *, ls, i) {
+ ui_update_menuitem_list(ls);
+ }
+}
+
+
+void ui_update_menuitem_list(UiActiveMenuItemList *list) {
+ // remove old items
+ if(list->oldcount > 0) {
+ int i = 0;
+ GList *mi = gtk_container_get_children(GTK_CONTAINER(list->menu));
+ while(mi) {
+ if(i >= list->index && i < list->index + list->oldcount) {
+ //gtk_container_remove(GTK_CONTAINER(list->menu), mi->data);
+ gtk_widget_destroy(mi->data);
+ }
+ mi = mi->next;
+ i++;
+ }
+ }
+
+ UiList *ls = list->var->value;
+
+ void* elm = ui_list_first(ls);
+ if(elm) {
+ GtkWidget *widget = gtk_separator_menu_item_new();
+ gtk_menu_shell_insert(list->menu, widget, list->index);
+ gtk_widget_show(widget);
+ }
+
+ ui_getvaluefunc getvalue = list->getvalue;
+ int i = 1;
+ while(elm) {
+ char *label = (char*) (getvalue ? getvalue(elm, 0) : elm);
+
+ GtkWidget *widget = gtk_menu_item_new_with_label(label);
+ gtk_menu_shell_insert(list->menu, widget, list->index + i);
+ gtk_widget_show(widget);
+
+ if(list->callback) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = list->object;
+ event->userdata = list->userdata;
+ event->callback = list->callback;
+ event->value = i - 1;
+ event->customdata = elm;
+
+ g_signal_connect(
+ widget,
+ "activate",
+ G_CALLBACK(ui_menu_event_wrapper),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+
+ elm = ui_list_next(ls);
+ i++;
+ }
+
+ list->oldcount = i;
+}
+
+void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event) {
+ UiEvent evt;
+ evt.obj = event->obj;
+ evt.window = event->obj->window;
+ evt.document = event->obj->ctx->document;
+ evt.eventdata = event->customdata;
+ evt.intval = event->value;
+ event->callback(&evt, event->userdata);
+}
+
+void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) {
+ UiEvent evt;
+ evt.obj = event->obj;
+ evt.window = event->obj->window;
+ evt.document = event->obj->ctx->document;
+ evt.eventdata = NULL;
+ evt.intval = gtk_check_menu_item_get_active(ci);
+ event->callback(&evt, event->userdata);
+}
+
+int64_t ui_checkitem_get(UiInteger *i) {
+ int state = gtk_check_menu_item_get_active(i->obj);
+ i->value = state;
+ return state;
+}
+
+void ui_checkitem_set(UiInteger *i, int64_t value) {
+ i->value = value;
+ gtk_check_menu_item_set_active(i->obj, value);
+}
+
+
+/*
+ * widget menu functions
+ */
+
+UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget) {
+ GtkWidget *menu_widget = gtk_menu_new();
+ ui_add_menu_items(menu_widget, 0, builder->menus_begin, obj);
+ return GTK_MENU(menu_widget);
+}
+
+static gboolean ui_button_press_event(GtkWidget *widget, GdkEvent *event, GtkMenu *menu) {
+ if(event->type == GDK_BUTTON_PRESS) {
+ GdkEventButton *e = (GdkEventButton*)event;
+ if(e->button == 3) {
+ gtk_widget_show_all(GTK_WIDGET(menu));
+ ui_contextmenu_popup(menu, widget, 0, 0);
+ }
+ }
+ return FALSE;
+}
+
+void ui_widget_set_contextmenu(GtkWidget *widget, GtkMenu *menu) {
+ g_signal_connect(widget, "button-press-event", (GCallback) ui_button_press_event, menu);
+}
+
+void ui_contextmenu_popup(UIMENU menu, GtkWidget *widget, int x, int y) {
+#if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16
+ gtk_menu_popup_at_pointer(menu, NULL);
+#else
+ gtk_menu_popup(menu, NULL, NULL, 0, 0, 0, gtk_get_current_event_time());
+#endif
+}
+
+#endif /* GTK_MAJOR_VERSION <= 3 */
+
+
+
+#if GTK_MAJOR_VERSION >= 4
+
+GtkWidget *ui_create_menubar(UiObject *obj) {
+ UiMenu *menus_begin = uic_get_menu_list();
+ if(menus_begin == NULL) {
+ return NULL;
+ }
+
+ GMenu *menu = g_menu_new();
+ UiMenu *ls = menus_begin;
+ while(ls) {
+ GMenu *sub_menu = g_menu_new();
+ ui_gmenu_add_menu_items(sub_menu, 0, ls, obj);
+ g_menu_append_submenu(menu, ls->label, G_MENU_MODEL(sub_menu));
+ ls = (UiMenu*)ls->item.next;
+ }
+
+
+ // Create a menubar from the menu model
+ return gtk_popover_menu_bar_new_from_model(G_MENU_MODEL(menu));
+}
+
+static ui_gmenu_add_f createMenuItem[] = {
+ /* UI_MENU */ ui_gmenu_add_menu,
+ /* UI_MENU_ITEM */ ui_gmenu_add_menuitem,
+ /* UI_MENU_CHECK_ITEM */ ui_gmenu_add_checkitem,
+ /* UI_MENU_RADIO_ITEM */ ui_gmenu_add_radioitem,
+ /* UI_MENU_ITEM_LIST */ ui_gmenu_add_menuitem_list,
+ /* UI_MENU_CHECKITEM_LIST */ ui_gmenu_add_menuitem_list,
+ /* UI_MENU_RADIOITEM_LIST */ ui_gmenu_add_menuitem_list,
+ /* UI_MENU_SEPARATOR */ ui_gmenu_add_menuseparator
+};
+
+void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj) {
+ UiMenuItemI *it = menu->items_begin;
+ int index = 0;
+ int index_section = 0;
+ GMenu *section = NULL;
+ while(it) {
+ if(it->type == UI_MENU_SEPARATOR) {
+ if(section) {
+ g_object_unref(section);
+ }
+ section = g_menu_new();
+ g_menu_append_section(parent, NULL, G_MENU_MODEL(section));
+ index++;
+ index_section = 0;
+ } else {
+ if(section) {
+ createMenuItem[it->type](section, index_section++, it, obj);
+ } else {
+ createMenuItem[it->type](parent, index++, it, obj);
+ }
+ }
+ it = it->next;
+ }
+ if(section) {
+ g_object_unref(section);
+ }
+}
+
+void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenu *mi = (UiMenu*)item;
+ GMenu *menu = g_menu_new();
+ ui_gmenu_add_menu_items(menu, 0, mi, obj);
+ g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu));
+ g_object_unref(menu);
+}
+
+static void action_enable(GSimpleAction *action, int enabled) {
+ g_simple_action_set_enabled(action, enabled);
+}
+
+void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuItem *i = (UiMenuItem*)item;
+
+ GSimpleAction *action = g_simple_action_new(item->id, NULL);
+ g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
+ g_object_unref(action);
+
+ if(i->groups) {
+ CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups);
+ cxListAddArray(groups, i->groups, i->ngroups);
+ uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
+ cxListFree(groups);
+ }
+
+ if(i->callback != NULL) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = i->userdata;
+ event->callback = i->callback;
+ event->value = 0;
+ event->customdata = NULL;
+ event->customint = 0;
+
+ g_signal_connect(
+ action,
+ "activate",
+ G_CALLBACK(ui_activate_event_wrapper),
+ event);
+ g_signal_connect(
+ obj->widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+
+ char action_name[32];
+ snprintf(action_name, 32, "win.%s", item->id);
+
+ g_menu_append(parent, i->label, action_name);
+}
+
+void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) {
+
+}
+
+void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item;
+
+ // TODO
+}
+
+
+
+typedef struct UiCallbackData {
+ ui_callback callback;
+ void *userdata;
+} UiCallbackData;
+
+static void radiogroup_remove_binding(void *obj) {
+ UiMenuRadioGroup *group = obj;
+ if(group->var) {
+ UiInteger *i = group->var->value;
+ CxList *bindings = i->obj;
+ if(bindings) {
+ (void)cxListFindRemove(bindings, obj);
+ }
+ }
+}
+
+static void stateful_action_notify_group(UiMenuRadioGroup *group, UiInteger *i) {
+ UiEvent event;
+ event.obj = group->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ event.intval = (int)i->value;
+ event.set = ui_get_setop();
+
+ CxIterator iter = cxListIterator(group->callbacks);
+ cx_foreach(UiCallbackData *, cb, iter) {
+ if(cb->callback) {
+ cb->callback(&event, cb->userdata);
+ }
+ }
+
+ UiObserver *obs = i->observers;
+ while(obs) {
+ if(obs->callback) {
+ obs->callback(&event, obs->data);
+ }
+ obs = obs->next;
+ }
+}
+
+static void ui_action_set_state(UiInteger *i, int64_t value) {
+ i->value = value;
+ CxList *bindings = i->obj;
+ CxIterator iter = cxListIterator(bindings);
+ cx_foreach(UiMenuRadioGroup *, group, iter) {
+ char buf[32];
+ snprintf(buf, 32, "%d", (int)value);
+ GVariant *state = g_variant_new_string(buf);
+ g_action_change_state(G_ACTION(group->action), state);
+ stateful_action_notify_group(group, i);
+ }
+}
+
+static int64_t ui_action_get_state(UiInteger *i) {
+ return i->value;
+}
+
+static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item, GSimpleAction *action) {
+ UiMenuRadioGroup *group = cxZalloc(obj->ctx->allocator, sizeof(UiMenuRadioGroup));
+ group->callbacks = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(UiCallbackData), 8);
+ group->var = uic_create_var(ui_global_context(), item->varname, UI_VAR_INTEGER);
+ group->obj = obj;
+ group->action = action;
+ if(group->var) {
+ UiInteger *i = group->var->value;
+ CxList *bindings = i->obj;
+ if(!bindings) {
+ bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ i->obj = bindings;
+ i->set = ui_action_set_state;
+ i->get = ui_action_get_state;
+ }
+ cxListAdd(bindings, group);
+ // the destruction of the toplevel obj must remove the binding
+ uic_context_add_destructor(obj->ctx, radiogroup_remove_binding, group);
+ }
+ return group;
+}
+
+static void stateful_action_activate(
+ GSimpleAction *action,
+ GVariant *state,
+ UiMenuRadioGroup *group)
+{
+ g_simple_action_set_state(action, state);
+
+ UiVar *var = group->var;
+ if(!var) {
+ return;
+ }
+ UiInteger *i = var->value;
+
+ gsize len;
+ const char *s = g_variant_get_string(state, &len);
+ cxstring value = cx_strn(s, len);
+ int v;
+ if(!cx_strtoi(value, &v, 10)) {
+ i->value = v;
+ }
+
+ CxList *bindings = i->obj;
+ CxIterator iter = cxListIterator(bindings);
+ cx_foreach(UiMenuRadioGroup *, gr, iter) {
+ stateful_action_notify_group(gr, i);
+ }
+}
+
+
+void ui_gmenu_add_radioitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuRadioItem *i = (UiMenuRadioItem*)item;
+
+ if(!i->varname) {
+ return;
+ }
+
+ // All radio buttons with the name varname use the same GAction
+ UiMenuRadioGroup *group = NULL;
+ GAction *action = g_action_map_lookup_action(obj->ctx->action_map, i->varname);
+ if(!action) {
+ GVariant *state = g_variant_new_string("0");
+ GSimpleAction *newAction = g_simple_action_new_stateful(i->varname, G_VARIANT_TYPE_STRING, state);
+ g_action_map_add_action(obj->ctx->action_map, G_ACTION(newAction));
+ g_object_unref(newAction);
+
+ group = create_radio_group(obj, i, newAction);
+ g_object_set_data(G_OBJECT(newAction), "ui_radiogroup", group);
+
+ g_signal_connect(
+ newAction,
+ "change-state",
+ G_CALLBACK(stateful_action_activate),
+ group);
+ } else {
+ group = g_object_get_data(G_OBJECT(action), "ui_radiogroup");
+ if(!group) {
+ fprintf(stderr, "Error: missing ui_radiogroup property for action %s\n", i->varname);
+ return; // error, should not happen
+ }
+ }
+
+ size_t item_index = cxListSize(group->callbacks);
+
+ UiCallbackData cb;
+ cb.callback = i->callback;
+ cb.userdata = i->userdata;
+ cxListAdd(group->callbacks, &cb);
+
+
+ cxmutstr action_name = cx_asprintf("win.%s::%d", i->varname, (int)item_index);
+ g_menu_append(parent, i->label, action_name.ptr);
+ free(action_name.ptr);
+}
+
+static void menuitem_list_remove_binding(void *obj) {
+ UiActiveGMenuItemList *ls = obj;
+ UiList *list = ls->var->value;
+ CxList *bindings = list->obj;
+ if(bindings) {
+ (void)cxListFindRemove(bindings, obj);
+ if(cxListSize(bindings) == 0) {
+ cxListFree(bindings);
+ list->obj = NULL;
+ list->update = NULL;
+ }
+ }
+}
+
+void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuItemList *il = (UiMenuItemList*)item;
+
+ const CxAllocator *a = obj->ctx->allocator;
+
+ UiActiveGMenuItemList *ls = cxMalloc(
+ a,
+ sizeof(UiActiveGMenuItemList));
+
+ ls->object = obj;
+ ls->menu = p;
+ ls->index = index;
+ ls->oldcount = 0;
+ ls->getvalue = il->getvalue;
+
+ GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i"));
+ g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
+ g_object_unref(action);
+ snprintf(ls->action, 32, "win.%s", item->id);
+
+ UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+ //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+ ls->var = var;
+ if(var) {
+ UiList *list = var->value;
+ list->update = ui_menulist_update;
+ list->getselection = NULL;
+ list->setselection = NULL;
+
+ // It is possible, that the UiVar is from a global shared context,
+ // used by multiple windows. To support this usecase, the list->obj
+ // binding object is a list of all connected UiActiveMenuItemList.
+ CxList *bindings = list->obj;
+ if(!bindings) {
+ bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ list->obj = bindings;
+ }
+ cxListAdd(bindings, ls);
+
+ // The destruction of the toplevel obj must remove the menulist binding
+ uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls);
+
+ ui_update_gmenu_item_list(ls);
+ }
+
+ ls->callback = il->callback;
+ ls->userdata = il->userdata;
+
+
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = il->userdata;
+ event->callback = il->callback;
+ event->customdata = var;
+ event->customint = 0;
+ event->value = 0;
+
+ g_signal_connect(
+ action,
+ "activate",
+ G_CALLBACK(ui_menu_list_item_activate_event_wrapper),
+ event);
+ g_signal_connect(
+ obj->widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+}
+
+void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
+ int intval = event->value;
+ if(parameter && g_variant_is_of_type(parameter, G_VARIANT_TYPE_INT32)) {
+ intval = g_variant_get_int32(parameter);
+ }
+
+ UiEvent evt;
+ evt.obj = event->obj;
+ evt.window = event->obj->window;
+ evt.document = event->obj->ctx->document;
+ evt.eventdata = event->customdata;
+ evt.eventdatatype = event->customint;
+ evt.intval = intval;
+ event->callback(&evt, event->userdata);
+}
+
+void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
+ int index = g_variant_get_int32(parameter);
+ UiVar *var = event->customdata;
+ UiList *list = var->value;
+
+ if(!event->callback) {
+ return;
+ }
+
+ UiEvent evt;
+ evt.obj = event->obj;
+ evt.window = event->obj->window;
+ evt.document = event->obj->ctx->document;
+ evt.eventdata = ui_list_get(list, index);
+ evt.eventdatatype = UI_EVENT_DATA_LIST_ELM;
+ evt.intval = index;
+ event->callback(&evt, event->userdata);
+
+}
+
+void ui_menulist_update(UiList *list, int ignored) {
+ CxList *bindings = list->obj;
+ CxIterator i = cxListIterator(bindings);
+ cx_foreach(UiActiveGMenuItemList *, ls, i) {
+ ui_update_gmenu_item_list(ls);
+ }
+}
+
+void ui_update_gmenu_item_list(UiActiveGMenuItemList *list) {
+ // remove old items
+ for(int i=0;i<list->oldcount;i++) {
+ g_menu_remove(list->menu, list->index);
+ }
+ UiList *ls = list->var->value;
+
+ // add list items
+ ui_getvaluefunc getvalue = list->getvalue;
+ int i = 0;
+ void* elm = ui_list_first(ls);
+ while(elm) {
+ char *label = (char*) (getvalue ? getvalue(elm, 0) : elm);
+
+ GMenuItem *item = g_menu_item_new(label, NULL);
+ GVariant *v = g_variant_new("i", i);
+ g_menu_item_set_action_and_target_value(item, list->action, v);
+ g_menu_insert_item(list->menu, list->index+i, item);
+ g_object_unref(item);
+
+ elm = ui_list_next(ls);
+ i++;
+ }
+
+ list->oldcount = i;
+}
+
+
+/* --------------------- context menu / menubuilder --------------------- */
+
+static void remove_popover(GtkWidget *object, GtkPopoverMenu *menu) {
+ gtk_widget_unparent(GTK_WIDGET(menu));
+}
+
+UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *widget) {
+ GMenu *menu = g_menu_new();
+ ui_gmenu_add_menu_items(menu, 0, builder->menus_begin, obj);
+ GtkWidget *contextmenu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu));
+ g_object_unref(menu);
+ gtk_popover_set_has_arrow(GTK_POPOVER(contextmenu), FALSE);
+ gtk_widget_set_halign(contextmenu, GTK_ALIGN_START);
+ gtk_widget_set_parent(GTK_WIDGET(contextmenu), widget);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(remove_popover),
+ contextmenu);
+ return GTK_POPOVER_MENU(contextmenu);
+}
+
+static void gesture_button_press(GtkGestureClick *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) {
+ gtk_popover_set_pointing_to(GTK_POPOVER(user_data), &(GdkRectangle){ x, y, 0, 0 });
+ gtk_popover_popup(GTK_POPOVER(user_data));
+}
+
+void ui_widget_set_contextmenu(GtkWidget *widget, GtkPopoverMenu *menu) {
+ GtkGesture *gesture = gtk_gesture_click_new();
+ gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 3);
+ gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(gesture));
+ g_signal_connect(gesture, "pressed", G_CALLBACK(gesture_button_press), menu);
+}
+
+void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y) {
+ gtk_popover_set_pointing_to(GTK_POPOVER(menu), &(GdkRectangle){ x, y, 0, 0 });
+ gtk_popover_popup(GTK_POPOVER(menu));
+}
+
+#endif
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MENU_H
+#define MENU_H
+
+#include "../ui/menu.h"
+#include "../common/menu.h"
+#include <cx/list.h>
+#include "toolkit.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+UIMENU ui_create_menu(UiMenuBuilder *builder, UiObject *obj);
+void ui_widget_set_contextmenu(GtkWidget *widget, UIMENU menu);
+
+GtkWidget *ui_create_menubar(UiObject *obj);
+
+#if GTK_MAJOR_VERSION <= 3
+
+typedef struct UiActiveMenuItemList UiActiveMenuItemList;
+
+typedef void(*ui_menu_add_f)(GtkWidget *, int, UiMenuItemI*, UiObject*);
+
+struct UiActiveMenuItemList {
+ UiObject *object;
+ GtkMenuShell *menu;
+ int index;
+ int oldcount;
+ UiVar *var;
+ ui_getvaluefunc getvalue;
+ ui_callback callback;
+ void *userdata;
+};
+
+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);
+void add_menuseparator_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj);
+void add_checkitem_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj);
+void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj);
+void add_checkitemnv_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuitem_list_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj);
+
+void ui_menulist_update(UiList *list, int ignored);
+void ui_update_menuitem_list(UiActiveMenuItemList *list);
+void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event);
+void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event);
+int64_t ui_checkitem_get(UiInteger *i);
+void ui_checkitem_set(UiInteger *i, int64_t value);
+
+#endif /* GTK_MAJOR_VERSION <= 3 */
+
+#if GTK_MAJOR_VERSION >= 4
+
+typedef void(*ui_gmenu_add_f)(GMenu *, int, UiMenuItemI*, UiObject*);
+
+typedef struct UiActiveGMenuItemList UiActiveGMenuItemList;
+struct UiActiveGMenuItemList {
+ UiObject *object;
+ GMenu *menu;
+ char action[32];
+ int index;
+ int oldcount;
+ UiVar *var;
+ ui_getvaluefunc getvalue;
+ ui_callback callback;
+ void *userdata;
+};
+
+typedef struct UiMenuRadioGroup {
+ UiObject *obj;
+ CxList *callbacks;
+ UiVar *var;
+ GSimpleAction *action;
+} UiMenuRadioGroup;
+
+
+void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj);
+
+void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj);
+void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj);
+void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj);
+void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj);
+void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj);
+void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj);
+
+void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event);
+void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event);
+void ui_menulist_update(UiList *list, int ignored);
+void ui_update_gmenu_item_list(UiActiveGMenuItemList *list);
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MENU_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2017 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+GTK_SRC_DIR = ui/gtk/
+GTK_OBJPRE = $(OBJ_DIR)$(GTK_SRC_DIR)
+
+# some objects are defined in config.mk
+GTKOBJ += toolkit.o
+GTKOBJ += window.o
+GTKOBJ += container.o
+GTKOBJ += menu.o
+GTKOBJ += toolbar.o
+GTKOBJ += button.o
+GTKOBJ += display.o
+GTKOBJ += text.o
+GTKOBJ += list.o
+GTKOBJ += image.o
+GTKOBJ += icon.o
+GTKOBJ += graphics.o
+GTKOBJ += range.o
+GTKOBJ += entry.o
+GTKOBJ += dnd.o
+GTKOBJ += headerbar.o
+GTKOBJ += webview.o
+GTKOBJ += widget.o
+
+TOOLKITOBJS += $(GTKOBJ:%=$(GTK_OBJPRE)%)
+TOOLKITSOURCE += $(GTKOBJ:%.o=gtk/%.c)
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "range.h"
+#include "container.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+
+static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange *range, ui_callback f, void *userdata) {
+#if GTK_MAJOR_VERSION >= 3
+ GtkWidget *scrollbar = gtk_scrollbar_new(orientation == UI_HORIZONTAL ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, NULL);
+#else
+ GtkWidget *scrollbar;
+ if(orientation == UI_HORIZONTAL) {
+ scrollbar = gtk_hscrollbar_new(NULL);
+ } else {
+ scrollbar = gtk_hscrollbar_new(NULL);
+ }
+#endif
+
+ if(range) {
+ range->get = ui_scrollbar_get;
+ range->set = ui_scrollbar_set;
+ range->setrange = ui_scrollbar_setrange;
+ range->setextent = ui_scrollbar_setextent;
+ range->obj = scrollbar;
+ }
+
+ if(f) {
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = userdata;
+ event->callback = f;
+ event->value = 0;
+ event->customdata = NULL;
+ event->customint = 0;
+
+ g_signal_connect(
+ G_OBJECT(scrollbar),
+ "value-changed",
+ G_CALLBACK(ui_scrollbar_value_changed),
+ event);
+ g_signal_connect(
+ scrollbar,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ //UiLayout layout = UI_ARGS2LAYOUT(args);
+ UiLayout layout = {0};
+ ct->add(ct, scrollbar, &layout);
+
+ return scrollbar;
+}
+
+UIWIDGET ui_hscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) {
+ return ui_scrollbar(obj, UI_HORIZONTAL, range, f, userdata);
+}
+
+UIWIDGET ui_vscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) {
+ return ui_scrollbar(obj, UI_VERTICAL, range, f, userdata);
+}
+
+gboolean ui_scrollbar_value_changed(GtkRange *range, UiEventData *event) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = event->value;
+ event->callback(&e, event->userdata);
+ return TRUE;
+}
+
+double ui_scrollbar_get(UiRange *range) {
+ double value = gtk_range_get_value(GTK_RANGE(range->obj));
+ range->value = value;
+ return value;
+}
+
+void ui_scrollbar_set(UiRange *range, double value) {
+ gtk_range_set_value(GTK_RANGE(range->obj), value);
+ range->value = value;
+}
+
+void ui_scrollbar_setrange(UiRange *range, double min, double max) {
+ gtk_range_set_range(GTK_RANGE(range->obj), min, max);
+ range->min = min;
+ range->max = max;
+}
+
+void ui_scrollbar_setextent(UiRange *range, double extent) {
+ GtkAdjustment *a = gtk_range_get_adjustment(GTK_RANGE(range->obj));
+#ifdef UI_GTK2LEGACY
+ a->page_size = extent;
+#else
+ gtk_adjustment_set_page_size(a, extent);
+#endif
+#if !GTK_CHECK_VERSION(3, 18, 0)
+ gtk_adjustment_changed(a);
+#endif
+ range->extent = extent;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RANGE_H
+#define RANGE_H
+
+#include "toolkit.h"
+#include "../ui/range.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gboolean ui_scrollbar_value_changed(GtkRange *range, UiEventData *event);
+
+double ui_scrollbar_get(UiRange *range);
+void ui_scrollbar_set(UiRange *range, double value);
+void ui_scrollbar_setrange(UiRange *range, double min, double max);
+void ui_scrollbar_setextent(UiRange *range, double extent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RANGE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "text.h"
+#include "container.h"
+
+#include <cx/printf.h>
+
+#include <gdk/gdkkeysyms.h>
+
+
+#include "../common/types.h"
+
+static void selection_handler(
+ GtkTextBuffer *buf,
+ GtkTextIter *location,
+ GtkTextMark *mark,
+ UiTextArea *textview)
+{
+ const char *mname = gtk_text_mark_get_name(mark);
+ if(mname) {
+ GtkTextIter begin;
+ GtkTextIter end;
+ int sel = gtk_text_buffer_get_selection_bounds (buf, &begin, &end);
+ if(sel != textview->last_selection_state) {
+ if(sel) {
+ ui_set_group(textview->ctx, UI_GROUP_SELECTION);
+ } else {
+ ui_unset_group(textview->ctx, UI_GROUP_SELECTION);
+ }
+ }
+ textview->last_selection_state = sel;
+ }
+}
+
+static void textarea_set_text_funcs(UiText *value) {
+
+}
+
+#if GTK_MAJOR_VERSION == 2
+static void textarea_set_undomgr(GtkWidget *text_area, UiText *value) {
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_area));
+
+ if(!value->data2) {
+ value->data2 = ui_create_undomgr();
+ }
+
+ // register undo manager
+ g_signal_connect(
+ buf,
+ "insert-text",
+ G_CALLBACK(ui_textbuf_insert),
+ var);
+ g_signal_connect(
+ buf,
+ "delete-range",
+ G_CALLBACK(ui_textbuf_delete),
+ var);
+ g_signal_connect(
+ buf,
+ "mark-set",
+ G_CALLBACK(selection_handler),
+ uitext);
+}
+#endif
+
+static GtkTextBuffer* create_textbuffer(UiTextArea *textarea) {
+ GtkTextBuffer *buf = gtk_text_buffer_new(NULL);
+ if(textarea) {
+ g_signal_connect(
+ buf,
+ "changed",
+ G_CALLBACK(ui_textbuf_changed),
+ textarea);
+ } else {
+ fprintf(stderr, "Error: create_textbuffer: textarea == NULL\n");
+ }
+ return buf;
+}
+
+UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_TEXT);
+
+ GtkWidget *text_area = gtk_text_view_new();
+ ui_set_name_and_style(text_area, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, text_area, args->groups);
+
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_area), GTK_WRAP_WORD_CHAR);
+ g_signal_connect(
+ text_area,
+ "realize",
+ G_CALLBACK(ui_textarea_realize_event),
+ NULL);
+
+ UiTextArea *uitext = malloc(sizeof(UiTextArea));
+ uitext->obj = obj;
+ uitext->ctx = obj->ctx;
+ uitext->var = var;
+ uitext->last_selection_state = 0;
+ uitext->onchange = args->onchange;
+ uitext->onchangedata = args->onchangedata;
+
+ g_object_set_data(G_OBJECT(text_area), "ui_textarea", uitext);
+
+ g_signal_connect(
+ text_area,
+ "destroy",
+ G_CALLBACK(ui_textarea_destroy),
+ uitext);
+
+ GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(scroll_area),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
+ SCROLLEDWINDOW_SET_CHILD(scroll_area, text_area);
+
+ if(args->width > 0 || args->height > 0) {
+ int width = args->width;
+ int height = args->height;
+ if(width == 0) {
+ width = -1;
+ }
+ if(height == 0) {
+ height = -1;
+ }
+ gtk_widget_set_size_request(scroll_area, width, height);
+ }
+
+ // font and padding
+ //PangoFontDescription *font;
+ //font = pango_font_description_from_string("Monospace");
+ //gtk_widget_modify_font(text_area, font); // TODO
+ //pango_font_description_free(font);
+
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_area), 2);
+ gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_area), 2);
+
+ // add
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, scroll_area, &layout);
+
+ // bind value
+ if(var) {
+ UiText *value = var->value;
+ GtkTextBuffer *buf;
+ if(value->data1 && value->datatype == UI_TEXT_TYPE_BUFFER) {
+ buf = value->data1;
+ } else {
+ buf = create_textbuffer(uitext);
+ if(value->value.ptr) {
+ gtk_text_buffer_set_text(buf, value->value.ptr, -1);
+ value->value.free(value->value.ptr);
+ }
+ }
+ gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_area), buf);
+ value->obj = text_area;
+ value->save = ui_textarea_save;
+ value->restore = ui_textarea_restore;
+ value->destroy = ui_textarea_text_destroy;
+ value->get = ui_textarea_get;
+ value->set = ui_textarea_set;
+ value->getsubstr = ui_textarea_getsubstr;
+ value->insert = ui_textarea_insert;
+ value->setposition = ui_textarea_setposition;
+ value->position = ui_textarea_position;
+ value->selection = ui_textarea_selection;
+ value->length = ui_textarea_length;
+ value->remove = ui_textarea_remove;
+ value->data1 = buf;
+ value->data2 = NULL;
+ value->datatype == UI_TEXT_TYPE_BUFFER;
+ value->value.ptr = NULL;
+ value->value.free = NULL;
+
+#if GTK_MAJOR_VERSION == 2
+ textarea_set_undomgr(text_area, value);
+#endif
+ }
+
+ return scroll_area;
+}
+
+void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea) {
+ if(textarea->var) {
+ ui_destroy_boundvar(textarea->ctx, textarea->var);
+ }
+ free(textarea);
+}
+
+UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea) {
+ return SCROLLEDWINDOW_GET_CHILD(textarea);
+}
+
+void ui_textarea_save(UiText *text) {
+ // NOOP
+}
+
+void ui_textarea_restore(UiText *text) {
+ GtkWidget *textarea = text->obj;
+ if(!text->data1) {
+ text->data1 = create_textbuffer(g_object_get_data(G_OBJECT(textarea), "ui_textarea"));
+ text->datatype = UI_TEXT_TYPE_BUFFER;
+ }
+ gtk_text_view_set_buffer(GTK_TEXT_VIEW(textarea), text->data1);
+}
+
+void ui_textarea_text_destroy(UiText *text) {
+ GtkTextBuffer *buf = text->data1;
+ g_object_unref(buf);
+}
+
+char* ui_textarea_get(UiText *text) {
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+ GtkTextBuffer *buf = text->data1;
+ GtkTextIter start;
+ GtkTextIter end;
+ gtk_text_buffer_get_bounds(buf, &start, &end);
+ char *str = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
+ text->value.ptr = g_strdup(str);
+ text->value.free = (ui_freefunc)g_free;
+ return str;
+}
+
+void ui_textarea_set(UiText *text, const char *str) {
+ gtk_text_buffer_set_text((GtkTextBuffer*)text->data1, str, -1);
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+ text->value.ptr = NULL;
+ text->value.free = NULL;
+}
+
+char* ui_textarea_getsubstr(UiText *text, int begin, int end) {
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+ GtkTextBuffer *buf = text->data1;
+ GtkTextIter ib;
+ GtkTextIter ie;
+ gtk_text_buffer_get_iter_at_offset(text->data1, &ib, begin);
+ gtk_text_buffer_get_iter_at_offset(text->data1, &ie, end);
+ char *str = gtk_text_buffer_get_text(buf, &ib, &ie, FALSE);
+ text->value.ptr = g_strdup(str);
+ text->value.free = (ui_freefunc)g_free;
+ return str;
+}
+
+void ui_textarea_insert(UiText *text, int pos, char *str) {
+ GtkTextIter offset;
+ gtk_text_buffer_get_iter_at_offset(text->data1, &offset, pos);
+ gtk_text_buffer_insert(text->data1, &offset, str, -1);
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+ text->value.ptr = NULL;
+ text->value.free = NULL;
+}
+
+void ui_textarea_setposition(UiText *text, int pos) {
+ GtkTextIter iter;
+ gtk_text_buffer_get_iter_at_offset(text->data1, &iter, pos);
+ gtk_text_buffer_place_cursor(text->data1, &iter);
+}
+
+int ui_textarea_position(UiText *text) {
+ GtkTextIter begin;
+ GtkTextIter end;
+ gtk_text_buffer_get_selection_bounds(text->data1, &begin, &end);
+ text->pos = gtk_text_iter_get_offset(&begin);
+ return text->pos;
+}
+
+void ui_textarea_selection(UiText *text, int *begin, int *end) {
+ GtkTextIter b;
+ GtkTextIter e;
+ gtk_text_buffer_get_selection_bounds(text->data1, &b, &e);
+ *begin = gtk_text_iter_get_offset(&b);
+ *end = gtk_text_iter_get_offset(&e);
+}
+
+int ui_textarea_length(UiText *text) {
+ GtkTextBuffer *buf = text->data1;
+ GtkTextIter start;
+ GtkTextIter end;
+ gtk_text_buffer_get_bounds(buf, &start, &end);
+ return gtk_text_iter_get_offset(&end);
+}
+
+void ui_textarea_remove(UiText *text, int begin, int end) {
+ GtkTextBuffer *buf = text->data1;
+ GtkTextIter ib;
+ GtkTextIter ie;
+ gtk_text_buffer_get_iter_at_offset(buf, &ib, begin);
+ gtk_text_buffer_get_iter_at_offset(buf, &ie, end);
+ gtk_text_buffer_delete(buf, &ib, &ie);
+}
+
+void ui_textarea_realize_event(GtkWidget *widget, gpointer data) {
+ gtk_widget_grab_focus(widget);
+}
+
+
+
+void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) {
+ UiText *value = textarea->var->value;
+
+ UiEvent e;
+ e.obj = textarea->obj;
+ e.window = e.obj->window;
+ e.document = textarea->ctx->document;
+ e.eventdata = value;
+ e.eventdatatype = UI_EVENT_DATA_TEXT_VALUE;
+ e.intval = 0;
+ e.set = ui_get_setop();
+
+ if(textarea->onchange) {
+ textarea->onchange(&e, textarea->onchangedata);
+ }
+
+ if(value->observers) {
+ ui_notify_evt(value->observers, &e);
+ }
+}
+
+// undo manager functions
+
+void ui_textbuf_insert(
+ GtkTextBuffer *textbuffer,
+ GtkTextIter *location,
+ char *text,
+ int length,
+ void *data)
+{
+ UiVar *var = data;
+ UiText *value = var->value;
+ if(!value->data2) {
+ value->data2 = ui_create_undomgr();
+ }
+ UiUndoMgr *mgr = value->data2;
+ if(!mgr->event) {
+ return;
+ }
+
+ if(mgr->cur) {
+ UiTextBufOp *elm = mgr->cur->next;
+ if(elm) {
+ mgr->cur->next = NULL;
+ mgr->end = mgr->cur;
+ while(elm) {
+ elm->prev = NULL;
+ UiTextBufOp *next = elm->next;
+ ui_free_textbuf_op(elm);
+ elm = next;
+ }
+ }
+
+ UiTextBufOp *last_op = mgr->cur;
+ if(
+ last_op->type == UI_TEXTBUF_INSERT &&
+ ui_check_insertstr(last_op->text, last_op->len, text, length) == 0)
+ {
+ // append text to last op
+ int ln = last_op->len;
+ char *newtext = malloc(ln + length + 1);
+ memcpy(newtext, last_op->text, ln);
+ memcpy(newtext+ln, text, length);
+ newtext[ln+length] = '\0';
+
+ last_op->text = newtext;
+ last_op->len = ln + length;
+ last_op->end += length;
+
+ return;
+ }
+ }
+
+ char *dpstr = malloc(length + 1);
+ memcpy(dpstr, text, length);
+ dpstr[length] = 0;
+
+ UiTextBufOp *op = malloc(sizeof(UiTextBufOp));
+ op->prev = NULL;
+ op->next = NULL;
+ op->type = UI_TEXTBUF_INSERT;
+ op->start = gtk_text_iter_get_offset(location);
+ op->end = op->start+length;
+ op->len = length;
+ op->text = dpstr;
+
+ cx_linked_list_add(
+ (void**)&mgr->begin,
+ (void**)&mgr->end,
+ offsetof(UiTextBufOp, prev),
+ offsetof(UiTextBufOp, next),
+ op);
+
+ mgr->cur = op;
+}
+
+void ui_textbuf_delete(
+ GtkTextBuffer *textbuffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ void *data)
+{
+ UiVar *var = data;
+ UiText *value = var->value;
+ if(!value->data2) {
+ value->data2 = ui_create_undomgr();
+ }
+ UiUndoMgr *mgr = value->data2;
+ if(!mgr->event) {
+ return;
+ }
+
+ if(mgr->cur) {
+ UiTextBufOp *elm = mgr->cur->next;
+ if(elm) {
+ mgr->cur->next = NULL;
+ mgr->end = mgr->cur;
+ while(elm) {
+ elm->prev = NULL;
+ UiTextBufOp *next = elm->next;
+ ui_free_textbuf_op(elm);
+ elm = next;
+ }
+ }
+ }
+
+ char *text = gtk_text_buffer_get_text(value->obj, start, end, FALSE);
+
+ UiTextBufOp *op = malloc(sizeof(UiTextBufOp));
+ op->prev = NULL;
+ op->next = NULL;
+ op->type = UI_TEXTBUF_DELETE;
+ op->start = gtk_text_iter_get_offset(start);
+ op->end = gtk_text_iter_get_offset(end);
+ op->len = op->end - op->start;
+
+ char *dpstr = malloc(op->len + 1);
+ memcpy(dpstr, text, op->len);
+ dpstr[op->len] = 0;
+ op->text = dpstr;
+
+ cx_linked_list_add(
+ (void**)&mgr->begin,
+ (void**)&mgr->end,
+ offsetof(UiTextBufOp, prev),
+ offsetof(UiTextBufOp, next),
+ op);
+
+ mgr->cur = op;
+}
+
+UiUndoMgr* ui_create_undomgr() {
+ UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr));
+ mgr->begin = NULL;
+ mgr->end = NULL;
+ mgr->cur = NULL;
+ mgr->length = 0;
+ mgr->event = 1;
+ return mgr;
+}
+
+void ui_destroy_undomgr(UiUndoMgr *mgr) {
+ UiTextBufOp *op = mgr->begin;
+ while(op) {
+ UiTextBufOp *nextOp = op->next;
+ if(op->text) {
+ free(op->text);
+ }
+ free(op);
+ op = nextOp;
+ }
+ free(mgr);
+}
+
+void ui_free_textbuf_op(UiTextBufOp *op) {
+ if(op->text) {
+ free(op->text);
+ }
+ free(op);
+}
+
+int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) {
+ // return 1 if oldstr + newstr are one word
+
+ int has_space = 0;
+ for(int i=0;i<oldlen;i++) {
+ if(oldstr[i] < 33) {
+ has_space = 1;
+ break;
+ }
+ }
+
+ for(int i=0;i<newlen;i++) {
+ if(has_space && newstr[i] > 32) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void ui_text_undo(UiText *value) {
+ UiUndoMgr *mgr = value->data2;
+
+ if(mgr->cur) {
+ UiTextBufOp *op = mgr->cur;
+ mgr->event = 0;
+ switch(op->type) {
+ case UI_TEXTBUF_INSERT: {
+ GtkTextIter begin;
+ GtkTextIter end;
+ gtk_text_buffer_get_iter_at_offset(value->obj, &begin, op->start);
+ gtk_text_buffer_get_iter_at_offset(value->obj, &end, op->end);
+ gtk_text_buffer_delete(value->obj, &begin, &end);
+ break;
+ }
+ case UI_TEXTBUF_DELETE: {
+ GtkTextIter begin;
+ GtkTextIter end;
+ gtk_text_buffer_get_iter_at_offset(value->obj, &begin, op->start);
+ gtk_text_buffer_get_iter_at_offset(value->obj, &end, op->end);
+ gtk_text_buffer_insert(value->obj, &begin, op->text, op->len);
+ break;
+ }
+ }
+ mgr->event = 1;
+ mgr->cur = mgr->cur->prev;
+ }
+}
+
+void ui_text_redo(UiText *value) {
+ UiUndoMgr *mgr = value->data2;
+
+ UiTextBufOp *elm = NULL;
+ if(mgr->cur) {
+ if(mgr->cur->next) {
+ elm = mgr->cur->next;
+ }
+ } else if(mgr->begin) {
+ elm = mgr->begin;
+ }
+
+ if(elm) {
+ UiTextBufOp *op = elm;
+ mgr->event = 0;
+ switch(op->type) {
+ case UI_TEXTBUF_INSERT: {
+ GtkTextIter begin;
+ GtkTextIter end;
+ gtk_text_buffer_get_iter_at_offset(value->obj, &begin, op->start);
+ gtk_text_buffer_get_iter_at_offset(value->obj, &end, op->end);
+ gtk_text_buffer_insert(value->obj, &begin, op->text, op->len);
+ break;
+ }
+ case UI_TEXTBUF_DELETE: {
+ GtkTextIter begin;
+ GtkTextIter end;
+ gtk_text_buffer_get_iter_at_offset(value->obj, &begin, op->start);
+ gtk_text_buffer_get_iter_at_offset(value->obj, &end, op->end);
+ gtk_text_buffer_delete(value->obj, &begin, &end);
+ break;
+ }
+ }
+ mgr->event = 1;
+ mgr->cur = elm;
+ }
+}
+
+
+
+
+static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool password, UiTextFieldArgs *args) {
+ GtkWidget *textfield = gtk_entry_new();
+ ui_set_name_and_style(textfield, args->name, args->style_class);
+ ui_set_widget_groups(obj->ctx, textfield, args->groups);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+
+ UiTextField *uitext = malloc(sizeof(UiTextField));
+ uitext->obj = obj;
+ uitext->var = var;
+ uitext->onchange = args->onchange;
+ uitext->onchangedata = args->onchangedata;
+ uitext->onactivate = args->onactivate;
+ uitext->onactivatedata = args->onactivatedata;
+
+ g_signal_connect(
+ textfield,
+ "destroy",
+ G_CALLBACK(ui_textfield_destroy),
+ uitext);
+
+ if(args->width > 0) {
+ // An early implementation used gtk_entry_set_width_chars,
+ // but that is not available on gtk4 and other toolkits
+ // also don't have this. Setting the width in pixels can
+ // be implemented on all platforms
+ gtk_widget_set_size_request(textfield, args->width, -1);
+ }
+ if(frameless) {
+ // TODO: gtk2legacy workaroud
+ gtk_entry_set_has_frame(GTK_ENTRY(textfield), FALSE);
+ }
+ if(password) {
+ gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE);
+ }
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, textfield, &layout);
+
+ if(var) {
+ UiString *value = var->value;
+ if(value->value.ptr) {
+ ENTRY_SET_TEXT(textfield, value->value.ptr);
+ value->value.free(value->value.ptr);
+ value->value.ptr = NULL;
+ value->value.free = NULL;
+ }
+
+ value->get = ui_textfield_get;
+ value->set = ui_textfield_set;
+ value->value.ptr = NULL;
+ value->value.free = NULL;
+ value->obj = GTK_ENTRY(textfield);
+ }
+
+ if(args->onchange || var) {
+ g_signal_connect(
+ textfield,
+ "changed",
+ G_CALLBACK(ui_textfield_changed),
+ uitext);
+ }
+
+ if(args->onactivate) {
+ g_signal_connect(
+ textfield,
+ "activate",
+ G_CALLBACK(ui_textfield_activate),
+ uitext);
+ }
+
+ return textfield;
+}
+
+UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) {
+ return create_textfield(obj, FALSE, FALSE, args);
+}
+
+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) {
+ free(textfield);
+}
+
+void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) {
+ UiString *value = textfield->var->value;
+
+ UiEvent e;
+ e.obj = textfield->obj;
+ e.window = e.obj->window;
+ e.document = textfield->obj->ctx->document;
+ e.eventdata = value;
+ e.eventdatatype = UI_EVENT_DATA_TEXT_VALUE;
+ e.intval = 0;
+ e.set = ui_get_setop();
+
+ if(textfield->onchange) {
+ textfield->onchange(&e, textfield->onchangedata);
+ }
+
+ if(textfield->var) {
+ ui_notify_evt(value->observers, &e);
+ }
+}
+
+void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) {
+ if(textfield->onactivate) {
+ UiEvent e;
+ e.obj = textfield->obj;
+ e.window = e.obj->window;
+ e.document = textfield->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = 0;
+ e.set = ui_get_setop();
+ textfield->onactivate(&e, textfield->onactivatedata);
+ }
+}
+
+char* ui_textfield_get(UiString *str) {
+ if(str->value.ptr) {
+ str->value.free(str->value.ptr);
+ }
+ str->value.ptr = g_strdup(ENTRY_GET_TEXT(str->obj));
+ str->value.free = (ui_freefunc)g_free;
+ return str->value.ptr;
+}
+
+void ui_textfield_set(UiString *str, const char *value) {
+ ENTRY_SET_TEXT(str->obj, value);
+ if(str->value.ptr) {
+ str->value.free(str->value.ptr);
+ str->value.ptr = NULL;
+ 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 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) {
+ g_object_unref(pathtf->entry);
+ free(pathtf);
+}
+
+void ui_path_button_clicked(GtkWidget *widget, UiEventDataExt *event) {
+ UiPathTextField *pathtf = event->customdata1;
+ for(int i=0;i<event->value1;i++) {
+ if(i <= event->value0) {
+ WIDGET_REMOVE_CSS_CLASS(pathtf->current_path_buttons[i], "pathbar-button-inactive");
+ } else {
+ WIDGET_ADD_CSS_CLASS(pathtf->current_path_buttons[i], "pathbar-button-inactive");
+ }
+ }
+
+ UiPathElm *elm = event->customdata0;
+ 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.eventdatatype = UI_EVENT_DATA_STRING;
+ evt.intval = event->value0;
+ evt.set = ui_get_setop();
+ 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);
+ if(full_path_len == 0) {
+ return 1;
+ }
+
+ 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);
+ free(pathtf->current_path_buttons);
+ pathtf->current_path_buttons = calloc(nelm, sizeof(GtkWidget*));
+ pathtf->current_pathelms = path_elm;
+ pathtf->current_nelm = nelm;
+
+ return ui_pathtextfield_update_widget(pathtf);
+}
+
+static GtkWidget* ui_path_elm_button(UiPathTextField *pathtf, UiPathElm *elm, int i) {
+ cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len));
+ GtkWidget *button = gtk_button_new_with_label(name.ptr);
+ pathtf->current_path_buttons[i] = button;
+ free(name.ptr);
+
+ if(pathtf->onactivate) {
+ UiEventDataExt *eventdata = malloc(sizeof(UiEventDataExt));
+ memset(eventdata, 0, sizeof(UiEventDataExt));
+ eventdata->callback = pathtf->onactivate;
+ eventdata->userdata = pathtf->onactivatedata;
+ eventdata->obj = pathtf->obj;
+ eventdata->customdata0 = elm;
+ eventdata->customdata1 = pathtf;
+ eventdata->value0 = i;
+ eventdata->value1 = pathtf->current_nelm;
+
+ g_signal_connect(
+ button,
+ "clicked",
+ G_CALLBACK(ui_path_button_clicked),
+ eventdata);
+
+ g_signal_connect(
+ button,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ eventdata);
+ }
+
+ return button;
+}
+
+static void ui_path_textfield_activate(GtkWidget *entry, UiPathTextField *pathtf) {
+ const gchar *text = ENTRY_GET_TEXT(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.eventdatatype = UI_EVENT_DATA_STRING;
+ evt.intval = -1;
+ pathtf->onactivate(&evt, pathtf->onactivatedata);
+ }
+}
+
+#if GTK_MAJOR_VERSION >= 4
+
+static void pathbar_show_hbox(GtkWidget *widget, UiPathTextField *pathtf) {
+ if(pathtf->current_path) {
+ gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox);
+ ENTRY_SET_TEXT(pathtf->entry, pathtf->current_path);
+ }
+}
+
+static gboolean ui_path_textfield_key_controller(
+ GtkEventControllerKey* self,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state,
+ UiPathTextField *pathtf)
+{
+ if(keyval == GDK_KEY_Escape) {
+ pathbar_show_hbox(NULL, pathtf);
+ }
+ return FALSE;
+}
+
+UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
+ 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;
+ }
+
+ pathtf->stack = gtk_stack_new();
+ gtk_widget_set_name(pathtf->stack, "path-textfield-box");
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, pathtf->stack, &layout);
+
+ pathtf->entry_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ pathtf->entry = gtk_entry_new();
+ gtk_box_append(GTK_BOX(pathtf->entry_box), pathtf->entry);
+ gtk_widget_set_hexpand(pathtf->entry, TRUE);
+
+ GtkWidget *cancel_button = gtk_button_new_from_icon_name("window-close-symbolic");
+ gtk_widget_add_css_class(cancel_button, "flat");
+ gtk_widget_add_css_class(cancel_button, "pathbar-extra-button");
+ gtk_box_append(GTK_BOX(pathtf->entry_box), cancel_button);
+ g_signal_connect(
+ cancel_button,
+ "clicked",
+ G_CALLBACK(pathbar_show_hbox),
+ pathtf);
+
+ gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->entry_box);
+ g_object_ref(pathtf->entry); // for compatibility with older pathbar version
+ g_signal_connect(
+ pathtf->entry,
+ "activate",
+ G_CALLBACK(ui_path_textfield_activate),
+ pathtf);
+
+ GtkEventController *entry_cancel = gtk_event_controller_key_new();
+ gtk_widget_add_controller(pathtf->entry, entry_cancel);
+ g_signal_connect(entry_cancel, "key-pressed", G_CALLBACK(ui_path_textfield_key_controller), pathtf);
+
+ gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box);
+
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if (var) {
+ UiString* value = (UiString*)var->value;
+ value->obj = pathtf;
+ 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 pathtf->stack;
+}
+
+static void pathbar_pressed(
+ GtkGestureClick* self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ UiPathTextField *pathtf)
+{
+ gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box);
+ gtk_widget_grab_focus(pathtf->entry);
+}
+
+int ui_pathtextfield_update_widget(UiPathTextField* pathtf) {
+ // recreate button hbox
+ if(pathtf->hbox) {
+ gtk_stack_remove(GTK_STACK(pathtf->stack), pathtf->hbox);
+ }
+ pathtf->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous(GTK_BOX(pathtf->hbox), FALSE);
+ gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->hbox);
+ gtk_widget_set_name(pathtf->hbox, "pathbar");
+
+ // add buttons for path elements
+ for (int i=0;i<pathtf->current_nelm;i++) {
+ UiPathElm *elm = &pathtf->current_pathelms[i];
+
+ GtkWidget *button = ui_path_elm_button(pathtf, elm, i);
+ gtk_widget_add_css_class(button, "flat");
+
+ gtk_box_append(GTK_BOX(pathtf->hbox), button);
+
+ if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), CX_STR("/"))) {
+ GtkWidget *path_separator = gtk_label_new("/");
+ gtk_widget_add_css_class(path_separator, "pathbar-button-inactive");
+ gtk_box_append(GTK_BOX(pathtf->hbox), path_separator);
+ }
+ }
+ gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox);
+
+ // create a widget for receiving button press events
+ GtkWidget *event_area = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ GtkGesture *handler = gtk_gesture_click_new();
+ gtk_widget_add_controller(event_area, GTK_EVENT_CONTROLLER(handler));
+ g_signal_connect(
+ handler,
+ "pressed",
+ G_CALLBACK(pathbar_pressed),
+ pathtf);
+ gtk_widget_set_hexpand(event_area, TRUE);
+ gtk_widget_set_vexpand(event_area, TRUE);
+ gtk_box_append(GTK_BOX(pathtf->hbox), event_area);
+
+ return 0;
+}
+
+#else
+
+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);
+ pathtf->buttonbox = NULL;
+
+ gtk_widget_show(pathtf->entry);
+ gtk_widget_grab_focus(pathtf->entry);
+
+ return TRUE;
+}
+
+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) {
+ 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);
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, eventbox, &layout);
+
+ // hbox as parent for the GtkEntry and GtkButtonBox
+ GtkWidget *hbox = ui_gtk_hbox_new(0);
+ 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, obj->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;
+}
+
+int ui_pathtextfield_update_widget(UiPathTextField* pathtf) {
+ GtkWidget *buttonbox = create_path_button_box();
+
+ // switch from entry to buttonbox or remove current buttonbox
+ if(pathtf->buttonbox) {
+ gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox);
+ } else {
+ gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->entry);
+ }
+ gtk_box_pack_start(GTK_BOX(pathtf->hbox), buttonbox, FALSE, FALSE, 0);
+ pathtf->buttonbox = buttonbox;
+
+ for (int i=0;i<pathtf->current_nelm;i++) {
+ UiPathElm *elm = &pathtf->current_pathelms[i];
+ GtkWidget *button = ui_path_elm_button(pathtf, elm, i);
+ gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
+ }
+
+ gtk_widget_show_all(buttonbox);
+
+ return 0;
+}
+
+#endif
+
+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(ENTRY_GET_TEXT(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;
+ ENTRY_SET_TEXT(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;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TEXT_H
+#define TEXT_H
+
+#include "../ui/text.h"
+#include "toolkit.h"
+#include <cx/linked_list.h>
+#include "../common/context.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_TEXTBUF_INSERT 0
+#define UI_TEXTBUF_DELETE 1
+
+typedef struct UiTextBufOp UiTextBufOp;
+struct UiTextBufOp {
+ UiTextBufOp *prev;
+ UiTextBufOp *next;
+ int type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE
+ int start;
+ int end;
+ int len;
+ char *text;
+};
+
+typedef struct UiUndoMgr {
+ UiTextBufOp *begin;
+ UiTextBufOp *end;
+ UiTextBufOp *cur;
+ int length;
+ int event;
+} UiUndoMgr;
+
+typedef struct UiTextArea {
+ UiObject *obj;
+ UiContext *ctx;
+ UiVar *var;
+ int last_selection_state;
+ ui_callback onchange;
+ void *onchangedata;
+} UiTextArea;
+
+typedef struct UiTextField {
+ UiObject *obj;
+ UiVar *var;
+ ui_callback onchange;
+ void *onchangedata;
+ ui_callback onactivate;
+ void *onactivatedata;
+} UiTextField;
+
+typedef struct UiPathTextField {
+ UiObject *obj;
+
+ GtkWidget *stack;
+ GtkWidget *hbox;
+ GtkWidget *entry_box;
+ GtkWidget *entry;
+#if GTK_MAJOR_VERSION == 3
+ GtkWidget *buttonbox;
+#endif
+
+ char *current_path;
+ UiPathElm *current_pathelms;
+ GtkWidget **current_path_buttons;
+ 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);
+
+void ui_textarea_save(UiText *text);
+void ui_textarea_restore(UiText *text);
+void ui_textarea_text_destroy(UiText *text);
+char* ui_textarea_get(UiText *text);
+void ui_textarea_set(UiText *text, const char *str);
+char* ui_textarea_getsubstr(UiText *text, int begin, int end);
+void ui_textarea_insert(UiText *text, int pos, char *str);
+void ui_textarea_setposition(UiText *text, int pos);
+int ui_textarea_position(UiText *text);
+void ui_textarea_selection(UiText *text, int *begin, int *end);
+int ui_textarea_length(UiText *text);
+void ui_textarea_remove(UiText *text, int begin, int end);
+
+void ui_textarea_realize_event(GtkWidget *widget, gpointer data);
+void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea);
+
+void ui_textbuf_insert(
+ GtkTextBuffer *textbuffer,
+ GtkTextIter *location,
+ char *text,
+ int len,
+ void *data);
+void ui_textbuf_delete(
+ GtkTextBuffer *textbuffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ void *data);
+UiUndoMgr* ui_create_undomgr();
+void ui_destroy_undomgr(UiUndoMgr *mgr);
+void ui_free_textbuf_op(UiTextBufOp *op);
+int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen);
+
+void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield);
+void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield);
+void ui_textfield_activate(GtkEntry* self, UiTextField *textfield);
+
+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);
+int ui_pathtextfield_update_widget(UiPathTextField* pathtf);
+char* ui_path_textfield_get(UiString *str);
+void ui_path_textfield_set(UiString *str, const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TEXT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "toolbar.h"
+#include "menu.h"
+#include "button.h"
+#include "icon.h"
+#include "list.h"
+#include <cx/mempool.h>
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
+#include <cx/array_list.h>
+#include "../common/context.h"
+
+
+#if UI_GTK2 || UI_GTK3
+
+GtkWidget* ui_create_toolbar(UiObject *obj) {
+ GtkWidget *toolbar = gtk_toolbar_new();
+#ifdef UI_GTK3
+ gtk_style_context_add_class(
+ gtk_widget_get_style_context(toolbar),
+ 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) {
+ UiToolItemI *item = cxMapGet(toolbar_items, def);
+ if(item) {
+ item->add_to(tb, item, obj);
+ } else if(!strcmp(def, "@separator")) {
+ gtk_toolbar_insert(tb, gtk_separator_tool_item_new(), -1);
+ } else {
+ fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def);
+ }
+ }
+ */
+
+ return toolbar;
+}
+
+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;
+ button = gtk_tool_button_new(NULL, item->args.label);
+ if(item->args.tooltip) {
+ gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
+ }
+
+ gtk_tool_item_set_homogeneous(button, FALSE);
+ if(item->args.icon) {
+ set_toolbutton_icon(button, item->args.icon);
+ }
+ gtk_tool_item_set_is_important(button, TRUE);
+
+ ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
+
+ if(item->args.onclick) {
+ UiEventData *event = cxMalloc(
+ obj->ctx->allocator,
+ sizeof(UiEventData));
+ event->obj = obj;
+ event->callback = item->args.onclick;
+ event->userdata = item->args.onclickdata;
+ event->customdata = NULL;
+ event->customint = 0;
+ event->value = 0;
+
+ 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_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj) {
+ GtkToolItem *button;
+ button = gtk_toggle_tool_button_new();
+ gtk_tool_item_set_homogeneous(button, FALSE);
+ if(item->args.label) {
+ gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label);
+ }
+ if(item->args.icon) {
+ set_toolbutton_icon(button, item->args.icon);
+ }
+ if(item->args.tooltip) {
+ gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
+ }
+ ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
+
+ 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);
+ }
+ }
+ }
+
+ UiVarEventData *event = cxMalloc(
+ obj->ctx->allocator,
+ sizeof(UiVarEventData));
+ event->obj = obj;
+ 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);
+
+ // 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, UiVarEventData *event) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = gtk_toggle_tool_button_get_active(widget);
+
+ if(event->callback) {
+ event->callback(&e, event->userdata);
+ }
+
+ 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) {
+ integer->value = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(integer->obj));
+ return integer->value;
+}
+
+void ui_tool_toggle_button_set(UiInteger *integer, int64_t value) {
+ gboolean s = value != 0 ? TRUE : FALSE;
+ gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(integer->obj), s);
+ 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;
+ button = gtk_tool_button_new(NULL, item->args.label);
+
+ gtk_tool_item_set_homogeneous(button, FALSE);
+ if(item->args.icon) {
+ set_toolbutton_icon(button, item->args.icon);
+ }
+ if(item->args.tooltip) {
+ gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
+ }
+ gtk_tool_item_set_is_important(button, TRUE);
+
+ gtk_toolbar_insert(tb, button, -1);
+
+ // 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;
+ UiListModel *model = ui_list_model_new(obj, cb->var, modelinfo);
+
+ GtkWidget *combobox = ui_create_combobox(obj, model, cb->callback, cb->userdata);
+ GtkToolItem *item = gtk_tool_item_new();
+ gtk_container_add(GTK_CONTAINER(item), combobox);
+ gtk_toolbar_insert(tb, item, -1);
+}
+
+void add_toolbar_combobox_nv(GtkToolbar *tb, UiToolbarComboBoxNV *cb, UiObject *obj) {
+ UiVar *var = uic_create_var(obj->ctx, cb->listname, UI_VAR_LIST);
+ if(var) {
+ UiModel *modelinfo = ui_model(obj->ctx, UI_STRING, "", -1);
+ modelinfo->getvalue = cb->getvalue;
+ UiListModel *model = ui_list_model_new(obj, var, modelinfo);
+
+ GtkWidget *combobox = ui_create_combobox(obj, model, cb->callback, cb->userdata);
+ GtkToolItem *item = gtk_tool_item_new();
+ gtk_container_add(GTK_CONTAINER(item), combobox);
+ 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_toolbar_headerbar_add_items(obj, headerbar, items, left_defaults);
+ ui_toolbar_headerbar_add_items(obj, headerbar, items, center_defaults);
+ ui_toolbar_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_toolbar_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);
+ }
+ ui_set_widget_groups(obj->ctx, button, item->args.groups);
+
+ 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 /* UI_GTK3 */
+
+#endif /* UI_GTK2 || UI_GTK3 */
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TOOLBAR_H
+#define TOOLBAR_H
+
+#include "../ui/toolbar.h"
+#include "../common/toolbar.h"
+#include <cx/map.h>
+#include <cx/list.h>
+
+#include "list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if UI_GTK2 || UI_GTK3
+
+typedef struct UiToolItemI UiToolItemI;
+typedef struct UiToolItem UiToolItem;
+typedef struct UiStToolItem UiStToolItem;
+typedef struct UiToggleToolItem UiToggleToolItem;
+
+typedef struct UiToolbarComboBox UiToolbarComboBox;
+typedef struct UiToolbarComboBoxNV UiToolbarComboBoxNV;
+
+typedef void(*ui_toolbar_add_f)(GtkToolbar*, UiToolItemI*, UiObject*);
+
+struct UiToolItemI {
+ ui_toolbar_add_f add_to;
+};
+
+struct UiToolItem {
+ UiToolItemI item;
+ const char *label;
+ const char *image;
+ ui_callback callback;
+ void *userdata;
+ const char *varname;
+ CxList *groups;
+ int isimportant;
+};
+
+struct UiStToolItem {
+ UiToolItemI item;
+ const char *stockid;
+ ui_callback callback;
+ void *userdata;
+ const char *varname;
+ CxList *groups;
+ int isimportant;
+};
+
+struct UiToggleToolItem {
+ UiToolItemI item;
+ const char *label;
+ const char *image;
+ const char *stockid;
+ UiInteger *value;
+ const char *var;
+ CxList *groups;
+ int isimportant;
+};
+
+struct UiToolbarComboBox {
+ UiToolItemI item;
+ UiVar *var;
+ ui_getvaluefunc getvalue;
+ ui_callback callback;
+ void *userdata;
+};
+
+struct UiToolbarComboBoxNV {
+ UiToolItemI item;
+ char *listname;
+ ui_getvaluefunc getvalue;
+ ui_callback callback;
+ void *userdata;
+};
+
+
+void ui_toolitem_vstgr(
+ char *name,
+ char *stockid,
+ int isimportant,
+ ui_callback f,
+ void *userdata,
+ va_list ap);
+
+GtkWidget* ui_create_toolbar(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_toolbar_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);
+*/
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TOOLBAR_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "toolkit.h"
+#include "toolbar.h"
+#include "icon.h"
+#include "../common/document.h"
+#include "../common/properties.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
+#include "../common/threadpool.h"
+
+#include <cx/string.h>
+#include <cx/printf.h>
+
+#include <pthread.h>
+
+#ifdef UI_APPLICATION
+UI_APPLICATION app;
+#endif
+
+static const char *application_name;
+
+static ui_callback startup_func;
+static void *startup_data;
+static ui_callback open_func;
+void *open_data;
+static ui_callback exit_func;
+void *exit_data;
+
+static ui_callback appclose_fnc;
+static void *appclose_udata;
+
+static UiObject *active_window;
+
+static int scale_factor = 1;
+
+static UiBool exit_on_shutdown;
+
+UIEXPORT void ui_init(const char *appname, int argc, char **argv) {
+ application_name = appname;
+ uic_init_global_context();
+
+#if GTK_MAJOR_VERSION >= 4
+ gtk_init();
+#else
+ gtk_init(&argc, &argv);
+#endif
+
+ ui_css_init();
+ uic_menu_init();
+ uic_toolbar_init();
+ ui_image_init();
+ uic_load_app_properties();
+
+#if GTK_MAJOR_VERSION >= 4
+ scale_factor = 1; // TODO
+#elif defined(UI_SUPPORTS_SCALE)
+ scale_factor = gdk_monitor_get_scale_factor(
+ gdk_display_get_primary_monitor(gdk_display_get_default()));
+#endif
+}
+
+const char* ui_appname() {
+ return application_name;
+}
+
+void ui_onstartup(ui_callback f, void *userdata) {
+ startup_func = f;
+ startup_data = userdata;
+}
+
+void ui_onopen(ui_callback f, void *userdata) {
+ open_func = f;
+ open_data = userdata;
+}
+
+void ui_onexit(ui_callback f, void *userdata) {
+ exit_func = f;
+ exit_data = userdata;
+}
+
+void ui_app_exit_on_shutdown(UiBool exitapp) {
+ exit_on_shutdown = exitapp;
+}
+
+#ifdef UI_APPLICATION
+static void app_startup(GtkApplication* app, gpointer userdata) {
+ if(startup_func) {
+ startup_func(NULL, startup_data);
+ }
+}
+
+static void app_activate(GtkApplication* app, gpointer userdata) {
+ //printf("activate\n");
+}
+
+static void app_shutdown(GtkApplication *app, gpointer userdata) {
+ if(exit_func) {
+ exit_func(NULL, exit_data);
+ }
+ ui_app_save_settings();
+}
+
+#endif
+
+void ui_main() {
+#ifdef UI_APPLICATION
+ cxmutstr appid = cx_asprintf(
+ "ui.%s",
+ application_name ? application_name : "application1");
+ app = UI_APPLICATION_NEW(appid.ptr);
+ g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
+ g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
+ g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
+ g_application_run(G_APPLICATION (app), 0, NULL);
+ g_object_unref (app);
+
+ free(appid.ptr);
+#else
+ if(startup_func) {
+ startup_func(NULL, startup_data);
+ }
+ gtk_main();
+ if(exit_func) {
+ exit_func(NULL, exit_data);
+ }
+ ui_app_save_settings();
+#endif
+ if(exit_on_shutdown) {
+ exit(0);
+ }
+}
+
+#ifndef UI_GTK2
+void ui_app_quit() {
+ g_application_quit(G_APPLICATION(app));
+}
+
+GtkApplication* ui_get_application() {
+ return GTK_APPLICATION(app);
+}
+#endif
+
+void ui_show(UiObject *obj) {
+ gboolean visible = gtk_widget_is_visible(obj->widget);
+
+ uic_check_group_widgets(obj->ctx);
+#if GTK_MAJOR_VERSION >= 4
+ gtk_window_present(GTK_WINDOW(obj->widget));
+#elif GTK_MAJOR_VERSION <= 3
+ gtk_widget_show_all(obj->widget);
+#endif
+
+ if(!visible) {
+ obj->ref++;
+ }
+}
+
+void ui_close(UiObject *obj) {
+ uic_context_prepare_close(obj->ctx);
+#if GTK_CHECK_VERSION(4, 0, 0)
+ gtk_window_close(GTK_WINDOW(obj->widget));
+#else
+ gtk_widget_destroy(obj->widget);
+#endif
+}
+
+
+static gboolean ui_job_finished(void *data) {
+ UiJob *job = data;
+
+ UiEvent event;
+ event.obj = job->obj;
+ event.window = job->obj->window;
+ event.document = job->obj->ctx->document;
+ event.intval = 0;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+
+ job->finish_callback(&event, job->finish_data);
+ free(job);
+ return FALSE;
+}
+
+static void* ui_jobthread(void *data) {
+ UiJob *job = data;
+ int result = job->job_func(job->job_data);
+ if(!result && job->finish_callback) {
+ g_idle_add(ui_job_finished, job);
+ } else {
+ free(job);
+ }
+ 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;
+ job->job_func = tf;
+ job->job_data = td;
+ job->finish_callback = f;
+ job->finish_data = fd;
+ pthread_t pid;
+ pthread_create(&pid, NULL, ui_jobthread, job);
+}
+
+void ui_set_enabled(UIWIDGET widget, int enabled) {
+ gtk_widget_set_sensitive(widget, enabled);
+}
+
+void ui_set_show_all(UIWIDGET widget, int value) {
+ // TODO: gtk4
+#if GTK_MAJOR_VERSION <= 3
+ gtk_widget_set_no_show_all(widget, !value);
+#endif
+}
+
+void ui_set_visible(UIWIDGET widget, int visible) {
+#if GTK_MAJOR_VERSION >= 4
+ gtk_widget_set_visible(widget, visible);
+#else
+ if(visible) {
+ gtk_widget_set_no_show_all(widget, FALSE);
+ gtk_widget_show_all(widget);
+ } else {
+ gtk_widget_hide(widget);
+ }
+#endif
+}
+
+void ui_clipboard_set(char *str) {
+#if GTK_MAJOR_VERSION >= 4
+ // TODO: gtk4: needs widget
+#else
+ GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text(cb, str, strlen(str));
+#endif
+}
+
+char* ui_clipboard_get() {
+#if GTK_MAJOR_VERSION >= 4
+ // TODO: gtk4: needs widget
+ return NULL;
+#else
+ GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ char *str = gtk_clipboard_wait_for_text(cb);
+ if(str) {
+ char *copy = strdup(str);
+ g_free(str);
+ return copy;
+ } else {
+ return NULL;
+ }
+#endif
+}
+
+int ui_get_scalefactor() {
+ return scale_factor;
+}
+
+void ui_destroy_userdata(GtkWidget *object, void *userdata) {
+ free(userdata);
+}
+
+void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data) {
+ if(data->var) {
+ ui_destroy_boundvar(data->obj->ctx, data->var);
+ }
+ free(data);
+}
+
+void ui_destroy_widget_var(GtkWidget *object, UiVar *var) {
+ ui_destroy_boundvar(NULL, var);
+}
+
+// TODO: move to common
+void ui_destroy_boundvar(UiContext *ctx, UiVar *var) {
+ uic_save_var(var);
+ uic_unbind_var(var);
+
+ // UI_VAR_SPECIAL: anonymous value variable, that is not registered
+ // in ctx->vars
+ if(var->type == UI_VAR_SPECIAL) {
+ ui_free(var->from_ctx, var);
+ }
+}
+
+void ui_set_active_window(UiObject *obj) {
+ active_window = obj;
+}
+
+UiObject *ui_get_active_window() {
+ return active_window;
+}
+
+
+#if GTK_MAJOR_VERSION >= 3
+
+#if GTK_MAJOR_VERSION == 4
+static const char *ui_gtk_css =
+"#path-textfield-box {\n"
+" background-color: alpha(currentColor, 0.1);"
+" border-radius: 6px;"
+" padding: 0px;"
+"}\n"
+".pathbar-extra-button {\n"
+" border-top-right-radius: 6px;"
+" border-bottom-right-radius: 6px;"
+" border-top-left-radius: 0px;"
+" border-bottom-left-radius: 0px;"
+"}\n"
+"#pathbar button {\n"
+" margin: 3px;"
+" border-radius: 4px;"
+" padding-top: 0px;"
+" padding-bottom: 0px;"
+" padding-left: 8px;"
+" padding-right: 8px;"
+"}\n"
+"#path-textfield-box entry {\n"
+" background-color: #00000000;"
+" border-top-left-radius: 6px;"
+" border-bottom-left-radius: 6px;"
+" border-top-right-radius: 0px;"
+" border-bottom-right-radius: 0px;"
+"}\n"
+".pathbar-button-inactive {\n"
+" color: alpha(currentColor, 0.5);"
+"}\n"
+".ui_test {\n"
+" background-color: red;\n"
+"}\n"
+".ui_label_title {\n"
+" font-weight: bold;\n"
+"}\n"
+".ui-listbox-header {\n"
+" font-weight: bold;\n"
+" margin-left: 10px;\n"
+" margin-top: 12px;\n"
+" margin-bottom: 10px;\n"
+"}\n"
+".ui-listbox-header-first {\n"
+" font-weight: bold;\n"
+" margin-left: 10px;\n"
+" margin-top: 4px;\n"
+" margin-bottom: 10px;\n"
+"}\n"
+".ui-listbox-header-row {\n"
+" font-weight: bold;\n"
+"}\n"
+".ui-badge {\n"
+" background-color: #e53935;\n"
+" color: white;\n"
+" border-radius: 9999px;\n"
+" padding: 0px 10px 0px 10px;\n"
+" font-weight: bold;\n"
+" margin-left: 4px;"
+" margin-right: 4px;"
+"}\n"
+".ui-nopadding {"
+" padding: 0;"
+"}\n"
+".ui-table-entry {"
+" border: none;"
+" box-shadow: none;"
+" background: transparent;"
+"}\n"
+;
+
+#elif GTK_MAJOR_VERSION == 3
+static const char *ui_gtk_css =
+"#path-textfield-box {\n"
+" background-color: @theme_base_color;\n"
+" border-radius: 5px;\n"
+" padding: 0px;\n"
+"}\n"
+".pathbar-button-inactive {\n"
+" color: alpha(currentColor, 0.5);"
+"}\n"
+".ui_test {\n"
+" background-color: red;\n"
+"}\n"
+".ui_label_title {\n"
+" font-weight: bold;\n"
+"}\n"
+"placessidebar row {\n"
+" padding-left: 10px;\n"
+"}\n"
+".ui-listbox-header {\n"
+" font-weight: bold;\n"
+" margin-left: 10px;\n"
+" margin-top: 12px;\n"
+" margin-bottom: 10px;\n"
+"}\n"
+".ui-listbox-header-first {\n"
+" font-weight: bold;\n"
+" margin-left: 10px;\n"
+" margin-top: 4px;\n"
+" margin-bottom: 10px;\n"
+"}\n"
+".ui-listbox-header-row {\n"
+" font-weight: bold;\n"
+"}\n"
+".ui-badge {\n"
+" background-color: #e53935;\n"
+" color: white;\n"
+" border-radius: 9999px;\n"
+" padding: 0px 10px 0px 10px;\n"
+" font-weight: bold;\n"
+" margin-left: 4px;"
+" margin-right: 4px;"
+"}\n"
+".ui-nopadding {"
+" padding: 0;"
+"}\n"
+;
+#endif
+
+void ui_css_init(void) {
+ ui_add_styledata(ui_gtk_css, -1);
+}
+
+void ui_add_styledata(const char *styledata, int len) {
+ GtkCssProvider *css = gtk_css_provider_new();
+
+#ifdef UI_GTK3
+ gtk_css_provider_load_from_data(css, styledata, len, NULL);
+
+ GdkScreen *screen = gdk_screen_get_default();
+ gtk_style_context_add_provider_for_screen(
+ screen,
+ GTK_STYLE_PROVIDER(css),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+#endif /* UI_GTK3 */
+
+#ifdef UI_GTK4
+
+
+#if GTK_MINOR_VERSION < 12
+ gtk_css_provider_load_from_data(css, styledata, len);
+#else
+ if(len < 0) {
+ gtk_css_provider_load_from_string(css, ui_gtk_css);
+ } else {
+ GBytes *style_data = g_bytes_new(styledata, len);
+ gtk_css_provider_load_from_bytes(css, style_data);
+ g_bytes_unref(style_data);
+
+ }
+#endif /* GTK_MINOR_VERSION < 12 */
+
+ GdkDisplay *display = gdk_display_get_default();
+ gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+#endif /* UI_GTK4 */
+}
+
+
+
+#endif
+
+void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style_classes) {
+ if(name) {
+ gtk_widget_set_name(widget, name);
+ }
+ if(style_classes) {
+ cxstring *cls = NULL;
+ size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), CX_STR(" "), 128, &cls);
+ for(int i=0;i<numClasses;i++) {
+ cxmutstr m = cx_strdup(cls[i]);
+#if GTK_MAJOR_VERSION >= 4
+ gtk_widget_add_css_class(widget, m.ptr);
+#elif GTK_MAJOR_VERSION >= 3
+ GtkStyleContext *ctx = gtk_widget_get_style_context(widget);
+ gtk_style_context_add_class(ctx, m.ptr);
+#endif
+ free(m.ptr);
+ }
+ free(cls);
+
+ }
+}
+
+void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups) {
+ if(!groups) {
+ return;
+ }
+ size_t ngroups = uic_group_array_size(groups);
+ ui_set_widget_ngroups(ctx, widget, groups, ngroups);
+}
+
+void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups) {
+ if(ngroups > 0) {
+ uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
+ ui_set_enabled(widget, FALSE);
+ }
+}
+
+void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states) {
+ if(!states) {
+ return;
+ }
+ size_t nstates = uic_group_array_size(states);
+ ui_set_widget_nvisibility_states(ctx, widget, states, nstates);
+}
+
+
+void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups) {
+ if(ngroups > 0) {
+ uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups);
+ ui_set_visible(widget, FALSE);
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TOOLKIT_H
+#define TOOLKIT_H
+
+#include "../ui/toolkit.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+
+#if GLIB_MAJOR_VERSION * 1000 + GLIB_MINOR_VERSION > 2074
+#define UI_G_APPLICATION_FLAGS G_APPLICATION_DEFAULT_FLAGS
+#else
+#define UI_G_APPLICATION_FLAGS G_APPLICATION_FLAGS_NONE
+#endif
+
+#ifdef UI_LIBADWAITA
+#define UI_APPLICATION AdwApplication*
+#define UI_APPLICATION_NEW(id) adw_application_new(id, UI_G_APPLICATION_FLAGS)
+#elif GTK_MAJOR_VERSION >= 3
+#define UI_APPLICATION GtkApplication*
+#define UI_APPLICATION_NEW(id) gtk_application_new(id, UI_G_APPLICATION_FLAGS)
+#endif
+
+#if GTK_MAJOR_VERSION >= 4
+#define WINDOW_SHOW(window) gtk_window_present(GTK_WINDOW(window))
+#define WINDOW_DESTROY(window) gtk_window_destroy(GTK_WINDOW(window))
+#define WINDOW_SET_CONTENT(window, child) gtk_window_set_child(GTK_WINDOW(window), child)
+#define BOX_ADD(box, child) gtk_box_append(GTK_BOX(box), child)
+#define BOX_ADD_EXPAND(box, child) gtk_widget_set_hexpand(child, TRUE); gtk_widget_set_vexpand(child, TRUE); gtk_box_append(GTK_BOX(box), child)
+#define BOX_ADD_NO_EXPAND(box, child) gtk_box_append(GTK_BOX(box), child)
+#define BOX_REMOVE(box, child) gtk_box_remove(GTK_BOX(box), child)
+#define ENTRY_SET_TEXT(entry, text) gtk_editable_set_text(GTK_EDITABLE(entry), text)
+#define ENTRY_GET_TEXT(entry) gtk_editable_get_text(GTK_EDITABLE(entry))
+#define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new()
+#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), child)
+#define SCROLLEDWINDOW_GET_CHILD(sw) gtk_scrolled_window_get_child(GTK_SCROLLED_WINDOW(sw))
+#define FRAME_SET_CHILD(frame, child) gtk_frame_set_child(GTK_FRAME(frame), child)
+#define EXPANDER_SET_CHILD(expander, child) gtk_expander_set_child(GTK_EXPANDER(expander), child)
+#define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_widget_add_css_class(w, cssclass)
+#define WIDGET_REMOVE_CSS_CLASS(w, cssclass) gtk_widget_remove_css_class(w, cssclass)
+#define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon)
+#define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row)
+#define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child)
+#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), NULL)
+#define PANED_SET_CHILD1(paned, child) gtk_paned_set_start_child(GTK_PANED(paned), child)
+#define PANED_SET_CHILD2(paned, child) gtk_paned_set_end_child(GTK_PANED(paned), child)
+#else
+#define WINDOW_SHOW(window) gtk_widget_show_all(window)
+#define WINDOW_DESTROY(window) gtk_widget_destroy(window)
+#define WINDOW_SET_CONTENT(window, child) gtk_container_add(GTK_CONTAINER(window), child)
+#define BOX_ADD(box, child) gtk_container_add(GTK_CONTAINER(box), child)
+#define BOX_ADD_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, TRUE, 0)
+#define BOX_ADD_NO_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, FALSE, 0)
+#define BOX_REMOVE(box, child) gtk_container_remove(GTK_CONTAINER(box), child)
+#define ENTRY_SET_TEXT(entry, text) gtk_entry_set_text(GTK_ENTRY(entry), text)
+#define ENTRY_GET_TEXT(entry) gtk_entry_get_text(GTK_ENTRY(entry))
+#define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new(NULL, NULL)
+#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_container_add(GTK_CONTAINER(sw), child)
+#define SCROLLEDWINDOW_GET_CHILD(sw) gtk_bin_get_child(GTK_BIN(sw))
+#define FRAME_SET_CHILD(frame, child) gtk_container_add(GTK_CONTAINER(frame), child)
+#define EXPANDER_SET_CHILD(expander, child) gtk_container_add(GTK_CONTAINER(expander), child)
+#define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_style_context_add_class(gtk_widget_get_style_context(w), cssclass)
+#define WIDGET_REMOVE_CSS_CLASS(w, cssclass) gtk_style_context_remove_class(gtk_widget_get_style_context(w), cssclass)
+#define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON)
+#define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row)
+#define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child)
+#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_container_remove(GTK_CONTAINER(row), gtk_bin_get_child(GTK_BIN(row)))
+#define PANED_SET_CHILD1(paned, child) gtk_paned_pack1(GTK_PANED(paned), child, TRUE, TRUE)
+#define PANED_SET_CHILD2(paned, child) gtk_paned_pack2(GTK_PANED(paned), child, TRUE, TRUE)
+#endif
+
+#ifdef UI_GTK2
+#undef SCROLLEDWINDOW_SET_CHILD
+#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), child)
+#endif
+
+#if GTK_MAJOR_VERSION >= 4
+#define UI_GTK_SINCE_V4(st) st
+#define UI_GTK_SINCE_V3(st)
+#define UI_GTK_V2(st)
+#define UI_GTK_V3(st)
+#define UI_GTK_V4(st) st
+#elif GTK_MAJOR_VERSION >= 3
+#define UI_GTK_SINCE_V4(st) st
+#define UI_GTK_SINCE_V3(st) st
+#define UI_GTK_V2(st)
+#define UI_GTK_V3(st) st
+#define UI_GTK_V4(st)
+#else
+#define UI_GTK_SINCE_V4(st)
+#define UI_GTK_SINCE_V3(st)
+#define UI_GTK_V2(st) st
+#define UI_GTK_V3(st)
+#define UI_GTK_V4(st)
+#endif
+
+
+typedef struct UiEventData {
+ UiObject *obj;
+ ui_callback callback;
+ void *userdata;
+ int value;
+ int customint;
+ void *customdata;
+} UiEventData;
+
+typedef struct UiEventDataExt {
+ UiObject *obj;
+ ui_callback callback;
+ void *userdata;
+ ui_callback callback2;
+ void *userdata2;
+ int value0;
+ int value1;
+ int value2;
+ int value3;
+ void *customdata0;
+ void *customdata1;
+ void *customdata2;
+ void *customdata3;
+} UiEventDataExt;
+
+typedef struct UiVarEventData {
+ UiObject *obj;
+ UiVar *var;
+ UiObserver **observers;
+ ui_callback callback;
+ void *userdata;
+} UiVarEventData;
+
+typedef enum UiOrientation UiOrientation;
+enum UiOrientation { UI_HORIZONTAL = 0, UI_VERTICAL };
+
+#ifndef UI_GTK4
+struct UiSelection {
+ GtkSelectionData *data;
+};
+#endif
+
+#ifdef UI_APPLICATION
+void ui_app_quit();
+GtkApplication* ui_get_application();
+#endif
+
+int ui_get_scalefactor();
+
+void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style);
+void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups);
+void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups);
+void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states);
+void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups);
+
+void ui_destroy_userdata(GtkWidget *object, void *userdata);
+void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data);
+void ui_destroy_widget_var(GtkWidget *object, UiVar *var);
+void ui_destroy_boundvar(UiContext *ctx, UiVar *var);
+
+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
+
+#endif /* TOOLKIT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "toolkit.h"
+#include "container.h"
+
+#include "webview.h"
+
+#ifdef UI_WEBVIEW
+
+UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) {
+ GtkWidget *webview = webkit_web_view_new();
+
+ ui_set_name_and_style(webview, args->name, args->style_class);
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC);
+ if(var) {
+ WebViewData *data = malloc(sizeof(WebViewData));
+ memset(data, 0, sizeof(WebViewData));
+ data->webview = WEBKIT_WEB_VIEW(webview);
+ WebKitSettings *settings = webkit_web_view_get_settings(data->webview);
+ data->javascript = webkit_settings_get_enable_javascript(settings);
+ data->zoom = webkit_web_view_get_zoom_level(data->webview);
+
+ UiGeneric *value = var->value;
+ value->get = ui_webview_get;
+ value->get_type = ui_webview_get_type;
+ value->set = ui_webview_set;
+ value->obj = data;
+ if(value->value && value->type && !strcmp(value->type, UI_WEBVIEW_OBJECT_TYPE)) {
+ ui_webview_set(value, value->value, UI_WEBVIEW_OBJECT_TYPE);
+ }
+ }
+
+ ui_set_widget_groups(obj->ctx, webview, args->groups);
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, webview, &layout);
+
+ return webview;
+}
+
+void* ui_webview_get(UiGeneric *g) {
+ return g->value;
+}
+
+const char* ui_webview_get_type(UiGeneric *g) {
+ return UI_WEBVIEW_OBJECT_TYPE;
+}
+
+int ui_webview_set(UiGeneric *g, void *value, const char *type) {
+ if(!type || strcmp(type, UI_WEBVIEW_OBJECT_TYPE)) {
+ return 1;
+ }
+
+ WebViewData *obj = g->obj;
+ if(!obj->webview) {
+ return 1;
+ }
+
+ WebViewData *data = value;
+ if(data->type == WEBVIEW_CONTENT_URL) {
+ webkit_web_view_load_uri(obj->webview, data->uri);
+ } else {
+ if(!data->content) {
+ return 1;
+ }
+
+ GBytes *bytes = g_bytes_new(data->content, data->contentlength);
+ webkit_web_view_load_bytes(obj->webview, bytes, data->mimetype, data->encoding, data->uri);
+ }
+
+ ui_webview_enable_javascript(g, data->javascript);
+ ui_webview_set_zoom(g, data->zoom);
+
+ return 0;
+}
+
+void ui_webview_load_url(UiGeneric *g, const char *url) {
+ WebViewData data = { .uri = (char*)url, .type = WEBVIEW_CONTENT_URL, .javascript = TRUE, .zoom = 1 };
+ g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE);
+}
+
+void ui_webview_load_content(
+ UiGeneric *g,
+ const char *uri,
+ const char *content,
+ size_t contentlength,
+ const char *mimetype,
+ const char *encoding)
+{
+ WebViewData *data0 = g->obj;
+ if(!data0) {
+ return;
+ }
+
+ WebViewData data;
+ memset(&data, 0, sizeof(WebViewData));
+ data.webview = data0->webview;
+ data.uri = (char*)uri;
+ data.content = (char*)content;
+ data.contentlength = contentlength;
+ data.mimetype = (char*)mimetype;
+ data.encoding = (char*)encoding;
+ data.type = WEBVIEW_CONTENT_CONTENT;
+ data.javascript = FALSE;
+ data.zoom = 1;
+ g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE);
+}
+
+void ui_webview_reload(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_reload(webview->webview);
+}
+
+UiBool ui_webview_can_go_back(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ return webkit_web_view_can_go_back(webview->webview);
+}
+
+UiBool ui_webview_can_go_forward(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ return webkit_web_view_can_go_forward(webview->webview);
+}
+
+void ui_webview_go_back(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_go_back(webview->webview);
+}
+
+void ui_webview_go_forward(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_go_forward(webview->webview);
+}
+
+const char* ui_webview_get_uri(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ return webkit_web_view_get_uri(webview->webview);
+}
+
+void ui_webview_enable_javascript(UiGeneric *g, UiBool enable) {
+ WebViewData *webview = g->obj;
+ WebKitSettings *settings = webkit_web_view_get_settings(webview->webview);
+ webkit_settings_set_enable_javascript(settings, enable);
+}
+
+void ui_webview_set_zoom(UiGeneric *g, double zoom) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_set_zoom_level(webview->webview, zoom);
+ webview->zoom = zoom;
+}
+
+double ui_webview_get_zoom(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webview->zoom = webkit_web_view_get_zoom_level(webview->webview);
+ return webview->zoom;
+}
+
+
+#endif
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef WEBVIEW_H
+#define WEBVIEW_H
+
+#ifdef UI_WEBVIEW
+
+#include "../ui/webview.h"
+
+#if GTK_MAJOR_VERSION >= 4
+#include <webkit/webkit.h>
+#else
+#include <webkit2/webkit2.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum WebViewDataType {
+ WEBVIEW_CONTENT_URL,
+ WEBVIEW_CONTENT_CONTENT
+};
+
+typedef struct WebViewData {
+ WebKitWebView *webview;
+ char *uri;
+ char *mimetype;
+ char *encoding;
+ char *content;
+ size_t contentlength;
+ enum WebViewDataType type;
+
+ double zoom;
+ UiBool javascript;
+} WebViewData;
+
+void* ui_webview_get(UiGeneric *g);
+const char* ui_webview_get_type(UiGeneric *g);
+int ui_webview_set(UiGeneric *g, void *value, const char *type);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_WEBVIEW */
+
+#endif /* WEBVIEW_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "widget.h"
+#include "container.h"
+
+#include "../common/object.h"
+
+UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
+ UIWIDGET widget = create_widget(obj, args, userdata);
+
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+
+ return widget;
+}
+
+UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
+ GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
+ ui_set_name_and_style(widget, args->name, args->style_class);
+ UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ct->add(ct, widget, &layout);
+ return widget;
+}
+
+void ui_widget_set_size(UIWIDGET w, int width, int height) {
+ gtk_widget_set_size_request(w, width, height);
+}
+
+void ui_widget_redraw(UIWIDGET w) {
+ gtk_widget_queue_draw(w);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include "../ui/widget.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WIDGET_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../ui/window.h"
+#include "../ui/properties.h"
+#include "../common/context.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
+
+#include <cx/mempool.h>
+
+#include "menu.h"
+#include "toolbar.h"
+#include "container.h"
+#include "headerbar.h"
+#include "button.h"
+
+static int nwindows = 0;
+
+static int window_default_width = 650;
+static int window_default_height = 550;
+
+static int splitview_window_default_pos = -1;
+static UiBool splitview_window_use_prop = TRUE;
+
+static gboolean ui_window_destroy(void *data) {
+ UiObject *obj = data;
+ uic_object_destroy(obj);
+
+ nwindows--;
+#ifdef UI_GTK2
+ if(nwindows == 0) {
+ gtk_main_quit();
+ }
+#endif
+
+ return FALSE;
+}
+
+void ui_window_widget_destroy(UiObject *obj) {
+#if GTK_MAJOR_VERSION >= 4
+ gtk_window_destroy(GTK_WINDOW(obj->widget));
+#else
+ gtk_widget_destroy(obj->widget);
+#endif
+}
+
+void ui_exit_event(GtkWidget *widget, gpointer data) {
+ // delay exit handler
+ g_idle_add(ui_window_destroy, data);
+}
+
+static gboolean ui_window_close_request(UiObject *obj) {
+ if(obj->widget) {
+ void *appwindow = g_object_get_data(G_OBJECT(obj->widget), "ui.appwindow");
+ if(appwindow) {
+ int width = 0;
+ int height = 0;
+#if GTK_CHECK_VERSION(4, 10, 0)
+ graphene_rect_t bounds;
+ if(gtk_widget_compute_bounds(obj->widget, obj->widget, &bounds)) {
+ width = bounds.size.width;
+ height = bounds.size.height;
+ }
+#elif GTK_CHECK_VERSION(4, 0, 0)
+ GtkAllocation alloc;
+ gtk_widget_get_allocation(GTK_WIDGET(obj->widget), &alloc);
+ width = alloc.width;
+ height = alloc.height;
+#else
+ gtk_window_get_size(GTK_WINDOW(obj->widget), &width, &height);
+#endif
+ if(width > 0 && height > 0) {
+ char width_str[32];
+ char height_str[32];
+ snprintf(width_str, 32, "%d", width);
+ snprintf(height_str, 32, "%d", height);
+ ui_set_property("ui.window.width", width_str);
+ ui_set_property("ui.window.height", height_str);
+ }
+ }
+ }
+
+ uic_context_prepare_close(obj->ctx);
+ obj->ref--;
+ if(obj->ref > 0) {
+#if GTK_CHECK_VERSION(2, 18, 0)
+ gtk_widget_set_visible(obj->widget, FALSE);
+#else
+ gtk_widget_hide(obj->widget);
+#endif
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+#if GTK_MAJOR_VERSION >= 4
+static gboolean close_request(GtkWindow* self, UiObject *obj) {
+ return ui_window_close_request(obj);
+}
+#else
+static gboolean close_request(GtkWidget* self, GdkEvent* event, UiObject *obj) {
+ return ui_window_close_request(obj);
+}
+#endif
+
+static void save_window_splitview_pos(GtkWidget *widget, void *unused) {
+ int pos = gtk_paned_get_position(GTK_PANED(widget));
+ char buf[32];
+ snprintf(buf, 32, "%d", pos);
+ ui_set_property("ui.window.splitview.pos", buf);
+}
+
+static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool splitview, UiBool simple) {
+ UiObject *obj = uic_object_new_toplevel();
+
+#ifdef UI_LIBADWAITA
+ obj->widget = adw_application_window_new(ui_get_application());
+#elif !defined(UI_GTK2)
+ obj->widget = gtk_application_window_new(ui_get_application());
+#else
+ obj->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+#endif
+
+ obj->window = window_data;
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+ obj->ctx->action_map = G_ACTION_MAP(obj->widget);
+#endif
+
+ if(title != NULL) {
+ gtk_window_set_title(GTK_WINDOW(obj->widget), title);
+ }
+
+ if(!simple) {
+ g_object_set_data(G_OBJECT(obj->widget), "ui.appwindow", obj);
+ }
+
+ int window_width = window_default_width;
+ int window_height = window_default_height;
+ if(!simple) {
+ const char *width = ui_get_property("ui.window.width");
+ const char *height = ui_get_property("ui.window.height");
+ if(width && height) {
+ int w = atoi(width);
+ int h = atoi(height);
+ if(w > 0 && h > 0) {
+ window_width = w;
+ window_height = h;
+ }
+ }
+ }
+ gtk_window_set_default_size(
+ GTK_WINDOW(obj->widget),
+ window_width,
+ window_height);
+
+ obj->destroy = ui_window_widget_destroy;
+ g_signal_connect(
+ obj->widget,
+ "destroy",
+ G_CALLBACK(ui_exit_event),
+ obj);
+#if GTK_MAJOR_VERSION >= 4
+ g_signal_connect(
+ obj->widget,
+ "close-request",
+ G_CALLBACK(close_request),
+ obj);
+#else
+ g_signal_connect(
+ obj->widget,
+ "delete-event",
+ G_CALLBACK(close_request),
+ obj);
+#endif
+
+ GtkWidget *vbox = ui_gtk_vbox_new(0);
+#ifdef UI_LIBADWAITA
+ GtkWidget *toolbar_view = adw_toolbar_view_new();
+ adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), vbox);
+
+ GtkWidget *headerbar_sidebar = NULL;
+ GtkWidget *headerbar_main = adw_header_bar_new();
+ GtkWidget *headerbar_right = NULL;
+
+ GtkWidget *content = toolbar_view;
+ if(splitview) {
+ content = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ g_signal_connect(
+ content,
+ "destroy",
+ G_CALLBACK(save_window_splitview_pos),
+ NULL);
+
+ const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos");
+ int pos = splitview_window_default_pos;
+ if(pos < 0) {
+ pos = window_width / 2;
+ }
+ if(splitview_pos_str && splitview_window_use_prop) {
+ int splitview_pos = atoi(splitview_pos_str);
+ if(splitview_pos > 0) {
+ pos = splitview_pos;
+ }
+ }
+ gtk_paned_set_position(GTK_PANED(content), pos);
+
+ GtkWidget *right_panel = adw_toolbar_view_new();
+ GtkWidget *right_vbox = ui_gtk_vbox_new(0);
+ adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(right_panel), right_vbox);
+
+ headerbar_right = adw_header_bar_new();
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_right), FALSE);
+ adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(right_panel), headerbar_right);
+
+ adw_header_bar_set_show_end_title_buttons(ADW_HEADER_BAR(headerbar_main), FALSE);
+
+ gtk_paned_set_start_child(GTK_PANED(content), toolbar_view);
+ gtk_paned_set_end_child(GTK_PANED(content), right_panel);
+
+ g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", vbox);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_vbox);
+ }
+
+ GtkWidget *content_box = vbox;
+
+ if(sidebar) {
+ GtkWidget *splitview = adw_overlay_split_view_new();
+ adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), splitview);
+
+ GtkWidget *sidebar_toolbar_view = adw_toolbar_view_new();
+ adw_overlay_split_view_set_sidebar(ADW_OVERLAY_SPLIT_VIEW(splitview), sidebar_toolbar_view);
+ headerbar_sidebar = adw_header_bar_new();
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+ adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), headerbar_sidebar);
+
+ adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), content);
+
+ g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_toolbar_view);
+ } else {
+ adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), content);
+ }
+
+ const char *show_title = ui_get_property("ui.gtk.window.showtitle");
+ if(show_title) {
+ if(!strcmp(show_title, "main") && sidebar) {
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+ } else if(!strcmp(show_title, "sidebar")) {
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
+ } else if(!strcmp(show_title, "false")) {
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
+ } else {
+ fprintf(stderr, "Unknown value '%s' for property ui.gtk.window.showtitle\n", show_title);
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+ }
+ } else {
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
+ if(sidebar) {
+ adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
+ }
+ }
+
+ adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar_main);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar_main);
+
+ if(!simple) {
+ ui_fill_headerbar(obj, headerbar_sidebar, headerbar_main, headerbar_right);
+ }
+#elif GTK_MAJOR_VERSION >= 4
+ GtkWidget *content_box = ui_gtk_vbox_new(0);
+ WINDOW_SET_CONTENT(obj->widget, vbox);
+ if(!simple) {
+ if(uic_get_menu_list()) {
+ GtkWidget *mb = ui_create_menubar(obj);
+ if(mb) {
+ BOX_ADD(vbox, mb);
+ }
+ }
+ }
+ if(sidebar) {
+ GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
+ gtk_paned_set_start_child(GTK_PANED(paned), sidebar_vbox);
+ gtk_paned_set_end_child(GTK_PANED(paned), content_box);
+ BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
+ } else {
+ BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
+ }
+#else
+ if(!simple) {
+ // menu
+ if(uic_get_menu_list()) {
+ GtkWidget *mb = ui_create_menubar(obj);
+ if(mb) {
+ gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, FALSE, 0);
+ }
+ }
+
+ // toolbar
+ if(uic_toolbar_isenabled()) {
+ GtkWidget *tb = ui_create_toolbar(obj);
+ if(tb) {
+ 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);
+ }
+
+ GtkWidget *content_box = ui_gtk_vbox_new(0);
+ WINDOW_SET_CONTENT(obj->widget, vbox);
+ if(sidebar) {
+ GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
+ gtk_paned_add1(GTK_PANED(paned), sidebar_vbox);
+ gtk_paned_add2(GTK_PANED(paned), content_box);
+ BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
+ gtk_paned_set_position (GTK_PANED(paned), 200);
+ } else {
+ BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
+ }
+
+#endif
+
+ // window content
+ // the content has a (TODO: not yet) configurable frame
+ // TODO: really? why
+ /*
+ GtkWidget *frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+
+ // content vbox
+ GtkWidget *content_box = ui_gtk_vbox_new(0);
+ gtk_container_add(GTK_CONTAINER(frame), content_box);
+ obj->container = ui_box_container(obj, content_box);
+ */
+ UiContainerX *container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX);
+ uic_object_push_container(obj, container);
+
+ nwindows++;
+ return obj;
+}
+
+
+UiObject* ui_window(const char *title, void *window_data) {
+ return create_window(title, window_data, FALSE, FALSE, FALSE);
+}
+
+UiObject *ui_sidebar_window(const char *title, void *window_data) {
+ return create_window(title, window_data, TRUE, FALSE, FALSE);
+}
+
+UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar) {
+ return create_window(title, NULL, sidebar, TRUE, FALSE);
+}
+
+UiObject* ui_simple_window(const char *title, void *window_data) {
+ return create_window(title, window_data, FALSE, FALSE, TRUE);
+}
+
+void ui_window_size(UiObject *obj, int width, int height) {
+ gtk_window_set_default_size(
+ GTK_WINDOW(obj->widget),
+ width,
+ height);
+}
+
+void ui_window_default_size(int width, int height) {
+ window_default_width = width;
+ window_default_height = height;
+}
+
+void ui_splitview_window_set_pos(UiObject *obj, int pos) {
+ GtkWidget *splitview = g_object_get_data(G_OBJECT(obj->widget), "ui_window_splitview");
+ if(splitview) {
+ gtk_paned_set_position(GTK_PANED(splitview), pos);
+ } else {
+ fprintf(stderr, "Error: window has no splitview\n");
+ }
+}
+
+int ui_splitview_window_get_pos(UiObject *obj) {
+ GtkWidget *splitview = g_object_get_data(G_OBJECT(obj->widget), "ui_window_splitview");
+ if(splitview) {
+ return gtk_paned_get_position(GTK_PANED(splitview));
+ } else {
+ fprintf(stderr, "Error: window has no splitview\n");
+ }
+ return 0;
+}
+
+void ui_splitview_window_set_default_pos(int pos) {
+ splitview_window_default_pos = pos;
+}
+
+void ui_splitview_window_use_property(UiBool enable) {
+ splitview_window_use_prop = enable;
+}
+
+UIEXPORT void ui_splitview_window_set_visible(UiObject *obj, int pane, UiBool visible) {
+ GtkWidget *panel = NULL;
+ if(pane == 0) {
+ panel = g_object_get_data(G_OBJECT(obj->widget), "ui_left_panel");
+ } else if(pane == 1) {
+ panel = g_object_get_data(G_OBJECT(obj->widget), "ui_right_panel");
+ }
+
+ if(panel == NULL) {
+ fprintf(stderr, "Error: obj is not a splitview window or invalid pane %d specified\n", pane);
+ return;
+ }
+
+ gtk_widget_set_visible(panel, visible);
+}
+
+#ifdef UI_LIBADWAITA
+
+static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData *data) {
+ UiEvent evt;
+ evt.obj = data->obj;
+ evt.document = evt.obj->ctx->document;
+ evt.window = evt.obj->window;
+ evt.eventdata = NULL;
+ evt.eventdatatype = 0;
+ evt.intval = 0;
+
+ if(!strcmp(response, "btn1")) {
+ evt.intval = 1;
+ } else if(!strcmp(response, "btn2")) {
+ evt.intval = 2;
+ }
+
+ if(data->customdata) {
+ GtkWidget *entry = data->customdata;
+ evt.eventdata = (void*)ENTRY_GET_TEXT(GTK_ENTRY(entry));
+ evt.eventdatatype = UI_EVENT_DATA_STRING;
+ }
+
+ if(data->callback) {
+ data->callback(&evt, data->userdata);
+ }
+}
+
+void ui_dialog_create(UiObject *parent, UiDialogArgs *args) {
+ AdwDialog *dialog = adw_alert_dialog_new(args->title, args->content);
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->callback = args->result;
+ event->userdata = args->resultdata;
+ event->customdata = NULL;
+ event->customint = 0;
+ event->value = 0;
+ event->obj = parent;
+
+ if(args->button1_label) {
+ adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn1", args->button1_label);
+ }
+ if(args->button2_label) {
+ adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn2", args->button2_label);
+ }
+ if(args->closebutton_label) {
+ adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "close", args->closebutton_label);
+ adw_alert_dialog_set_close_response(ADW_ALERT_DIALOG(dialog), "close");
+ }
+
+ GtkWidget *entry = NULL;
+ if(args->input || args->password) {
+ entry = gtk_entry_new();
+ if(args->password) {
+ gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+ }
+ if(args->input_value) {
+ ENTRY_SET_TEXT(entry, args->input_value);
+ }
+ adw_alert_dialog_set_extra_child(ADW_ALERT_DIALOG(dialog), entry);
+ event->customdata = entry;
+ event->customint = 0;
+ }
+
+ g_signal_connect(
+ dialog,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+
+ g_signal_connect(dialog, "response", G_CALLBACK(dialog_response), event);
+ adw_dialog_present(dialog, parent->widget);
+
+ if(entry) {
+ gtk_entry_grab_focus_without_selecting(GTK_ENTRY(entry));
+ }
+}
+#else
+
+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*)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);
+ }
+
+ WINDOW_DESTROY(GTK_WIDGET(self));
+}
+
+void ui_dialog_create(UiObject *parent, UiDialogArgs *args) {
+ GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new());
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget));
+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+
+ GtkWidget *dialog_w = GTK_WIDGET(dialog);
+ if(args->title) {
+ gtk_window_set_title(GTK_WINDOW(dialog), args->title);
+ }
+ if(args->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);
+ BOX_ADD(content_area, label);
+ }
+
+ GtkWidget *textfield = NULL;
+ if(args->input || args->password) {
+ textfield = gtk_entry_new();
+ if(args->password) {
+ gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE);
+ }
+ if(args->input_value) {
+ ENTRY_SET_TEXT(textfield, args->input_value);
+ }
+ BOX_ADD(content_area, textfield);
+ }
+
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = parent;
+ event->callback = args->result;
+ event->userdata = args->resultdata;
+ event->value = 0;
+ event->customdata = textfield;
+
+ g_signal_connect(dialog_w,
+ "response",
+ G_CALLBACK(ui_dialog_response),
+ event);
+
+ WINDOW_SHOW(GTK_WIDGET(dialog_w));
+}
+#endif
+
+
+#if GTK_MAJOR_VERSION >= 3
+UiFileList listmodel2filelist(GListModel *selection) {
+ UiFileList flist;
+ flist.files = NULL;
+ flist.nfiles = 0;
+ flist.nfiles = g_list_model_get_n_items(selection);
+ flist.files = calloc(flist.nfiles, sizeof(char*));
+ for(int i=0;i<flist.nfiles;i++) {
+ GFile *file = g_list_model_get_item(selection, i);
+ char *path = g_file_get_path(file);
+ flist.files[i] = path ? strdup(path) : NULL;
+ g_object_unref(file);
+ }
+ return flist;
+}
+#endif
+
+
+#if GTK_CHECK_VERSION(4, 10, 0)
+
+#define UI_GTK_FILEDIALOG_OPEN 16
+#define UI_GTK_FILEDIALOG_SAVE 32
+
+static void filechooser_opened(GObject *source, GAsyncResult *result, void *data) {
+ UiEventData *event = data;
+
+ GFile *file = NULL;
+ GListModel *selection = NULL;
+ GError *error = NULL;
+
+ int mode = event->value;
+ int multi = mode & UI_FILEDIALOG_SELECT_MULTI;
+ if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) {
+ if(multi) {
+ selection = gtk_file_dialog_select_multiple_folders_finish(GTK_FILE_DIALOG(source), result, &error);
+ } else {
+ file = gtk_file_dialog_select_folder_finish(GTK_FILE_DIALOG(source), result, &error);
+ }
+ } else if((mode & UI_GTK_FILEDIALOG_OPEN) == UI_GTK_FILEDIALOG_OPEN) {
+ if(multi) {
+ selection = gtk_file_dialog_open_multiple_finish(GTK_FILE_DIALOG(source), result, &error);
+ } else {
+ file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(source), result, &error);
+ }
+ } else {
+ file = gtk_file_dialog_save_finish(GTK_FILE_DIALOG(source), result, &error);
+ }
+
+ UiEvent evt;
+ evt.obj = event->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;
+ evt.eventdatatype = UI_EVENT_DATA_FILE_LIST;
+
+ if(selection) {
+ flist = listmodel2filelist(selection);
+ g_object_unref(selection);
+ } else if(file) {
+ char *path = g_file_get_path(file);
+ if(path) {
+ flist.nfiles = 1;
+ flist.files = calloc(flist.nfiles, sizeof(char*));
+ flist.files[0] = strdup(path);
+ }
+ g_object_unref(file);
+ }
+
+ if(event->callback) {
+ event->callback(&evt, event->userdata);
+ }
+
+ for(int i=0;i<flist.nfiles;i++) {
+ free(flist.files[i]);
+ }
+}
+
+static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsigned int mode, const char *name, ui_callback file_selected_callback, void *cbdata) {
+ if(action == GTK_FILE_CHOOSER_ACTION_OPEN) {
+ mode |= UI_GTK_FILEDIALOG_OPEN;
+ } else {
+ mode |= UI_GTK_FILEDIALOG_SAVE;
+ }
+
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->callback = file_selected_callback;
+ event->userdata = cbdata;
+ event->customdata = NULL;
+ event->customint = 0;
+ event->value = mode;
+ event->obj = obj;
+
+ GtkWindow *parent = GTK_WINDOW(gtk_widget_get_root(obj->widget));
+ GtkFileDialog *dialog = gtk_file_dialog_new();
+ if(name) {
+ gtk_file_dialog_set_initial_name(dialog, name);
+ }
+
+ int multi = mode & UI_FILEDIALOG_SELECT_MULTI;
+ if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) {
+ if(multi) {
+ gtk_file_dialog_select_multiple_folders(dialog, parent, NULL, filechooser_opened, event);
+ } else {
+ gtk_file_dialog_select_folder(dialog, parent, NULL, filechooser_opened, event);
+ }
+ } else if(action == GTK_FILE_CHOOSER_ACTION_OPEN) {
+ if(multi) {
+ gtk_file_dialog_open_multiple(dialog, parent, NULL, filechooser_opened, event);
+ } else {
+ gtk_file_dialog_open(dialog, parent, NULL, filechooser_opened, event);
+ }
+ } else {
+ gtk_file_dialog_save(dialog, parent, NULL, filechooser_opened, event);
+ }
+
+ g_object_unref(dialog);
+}
+#else
+
+
+
+static void filechooser_response(GtkDialog* self, gint response_id, UiEventData *data) {
+ UiEvent evt;
+ evt.obj = data->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;
+
+ if(response_id == GTK_RESPONSE_ACCEPT) {
+#if GTK_CHECK_VERSION(4, 0, 0)
+ GListModel *selection = gtk_file_chooser_get_files(GTK_FILE_CHOOSER(self));
+ flist = flist = listmodel2filelist(selection);
+ g_object_unref(selection);
+#else
+ GSList *selection = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(self));
+ flist.nfiles = g_slist_length(selection);
+ flist.files = calloc(flist.nfiles, sizeof(char*));
+ int i = 0;
+ while(selection) {
+ char *file = selection->data;
+ flist.files[i] = strdup(file);
+ g_free(file);
+ selection = selection->next;
+ i++;
+ }
+ g_slist_free(selection);
+#endif
+ }
+
+
+ if(data->callback) {
+ data->callback(&evt, data->userdata);
+ }
+
+ for(int i=0;i<flist.nfiles;i++) {
+ free(flist.files[i]);
+ }
+
+ WINDOW_DESTROY(GTK_WIDGET(self));
+}
+
+static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsigned int mode, const char *name, ui_callback file_selected_callback, void *cbdata) {
+ char *button;
+ char *title;
+
+ 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 {
+ dialog = gtk_file_chooser_dialog_new (
+ "Save File",
+ GTK_WINDOW(obj->widget),
+ action,
+ "Cancel",
+ GTK_RESPONSE_CANCEL,
+ "Save File",
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ }
+
+ if((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) {
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
+ }
+
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = cbdata;
+ event->callback = file_selected_callback;
+ event->value = 0;
+ event->customdata = NULL;
+
+ g_signal_connect(
+ dialog,
+ "response",
+ G_CALLBACK(filechooser_response),
+ event);
+ g_signal_connect(
+ dialog,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+
+ gtk_widget_show(dialog);
+}
+#endif
+
+void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) {
+ ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_OPEN, mode, NULL, 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, name, file_selected_callback, cbdata);
+}
+
+#if GTK_CHECK_VERSION(4, 10, 0)
+#define DIALOG_NEW() gtk_window_new()
+#else
+#define DIALOG_NEW() gtk_dialog_new()
+
+static void ui_dialogwindow_response(GtkDialog* self, gint response_id, gpointer user_data) {
+ UiEventData *event = user_data;
+ // TODO: do we need to check if response_id == GTK_RESPONSE_DELETE_EVENT?
+ if(event->callback) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.eventdata = NULL;
+ e.intval = event->value;
+ event->callback(&e, event->userdata);
+ }
+}
+
+#endif
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+#define HEADERBAR_SHOW_CLOSEBUTTON(headerbar, set) gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(headerbar), set)
+#define DEFAULT_BUTTON(window, button) gtk_window_set_default_widget(GTK_WINDOW(window), button)
+#else
+#define HEADERBAR_SHOW_CLOSEBUTTON(headerbar, set) gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(headerbar), set)
+#define DEFAULT_BUTTON(window, button) gtk_widget_set_can_default(button, TRUE); gtk_window_set_default(GTK_WINDOW(window), button)
+#endif
+
+
+
+UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) {
+ GtkWidget *dialog = DIALOG_NEW();
+ if(args->width > 0 || args->height > 0) {
+ gtk_window_set_default_size(
+ GTK_WINDOW(dialog),
+ args->width,
+ args->height);
+ }
+
+
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget));
+ if(args->modal != UI_OFF) {
+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+ }
+
+ UiObject *obj = uic_object_new_toplevel();
+ obj->widget = dialog;
+ obj->ref = 0;
+ obj->destroy = ui_window_widget_destroy;
+ nwindows++;
+
+ if(args->title != NULL) {
+ gtk_window_set_title(GTK_WINDOW(dialog), args->title);
+ }
+
+#if ! GTK_CHECK_VERSION(4, 10, 0)
+ UiEventData *event = malloc(sizeof(UiEventData));
+ event->obj = obj;
+ event->userdata = args->onclickdata;
+ event->callback = args->onclick;
+ event->value = 0;
+ event->customdata = NULL;
+
+ g_signal_connect(dialog, "response", G_CALLBACK(ui_dialogwindow_response), event);
+ g_signal_connect(
+ dialog,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+#endif
+
+ g_signal_connect(
+ dialog,
+ "destroy",
+ G_CALLBACK(ui_exit_event),
+ obj);
+#if GTK_MAJOR_VERSION >= 4
+ g_signal_connect(
+ obj->widget,
+ "close-request",
+ G_CALLBACK(close_request),
+ obj);
+#else
+ g_signal_connect(
+ obj->widget,
+ "delete-event",
+ G_CALLBACK(close_request),
+ obj);
+#endif
+
+#if GTK_MAJOR_VERSION < 4
+ GtkWidget *c = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+ gtk_container_remove(GTK_CONTAINER(dialog), c);
+#endif
+
+ GtkWidget *content_vbox = ui_gtk_vbox_new(0);
+ UiContainerX *container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX);
+ uic_object_push_container(obj, container);
+ if(args->lbutton1 || args->lbutton2 || args->rbutton3 || args->rbutton4) {
+#if GTK_CHECK_VERSION(3, 10, 0)
+ if(args->titlebar_buttons != UI_OFF) {
+ GtkWidget *headerbar = gtk_header_bar_new();
+ gtk_window_set_titlebar(GTK_WINDOW(dialog), headerbar);
+ if(args->show_closebutton == UI_OFF) {
+ HEADERBAR_SHOW_CLOSEBUTTON(headerbar, FALSE);
+ }
+
+ if(args->lbutton1) {
+ GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1);
+ gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button);
+ if(args->default_button == 1) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+ if(args->lbutton2) {
+ GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2);
+ gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button);
+ if(args->default_button == 2) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+
+ if(args->rbutton4) {
+ GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4);
+ gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button);
+ if(args->default_button == 4) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+ if(args->rbutton3) {
+ GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3);
+ gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button);
+ if(args->default_button == 3) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+ WINDOW_SET_CONTENT(obj->widget, content_vbox);
+ return obj;
+ }
+#endif
+ GtkWidget *vbox = ui_gtk_vbox_new(0);
+ WINDOW_SET_CONTENT(obj->widget, vbox);
+
+ GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
+
+ GtkWidget *grid = ui_create_grid_widget(10, 10);
+ GtkWidget *widget = ui_gtk_set_margin(grid, 16, 0, 0, 0, 0);
+ gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
+
+ if(args->lbutton1) {
+ GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1);
+ gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1);
+ if(args->default_button == 1) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+ if(args->lbutton2) {
+ GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2);
+ gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1);
+ if(args->default_button == 2) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+ GtkWidget *space = gtk_label_new(NULL);
+ gtk_widget_set_hexpand(space, TRUE);
+ gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1);
+ if(args->rbutton3) {
+ GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3);
+ gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1);
+ if(args->default_button == 3) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+ if(args->rbutton4) {
+ GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4);
+ gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1);
+ if(args->default_button == 4) {
+ WIDGET_ADD_CSS_CLASS(button, "suggested-action");
+ DEFAULT_BUTTON(dialog, button);
+ }
+ }
+
+ BOX_ADD_EXPAND(vbox, content_vbox);
+ BOX_ADD_NO_EXPAND(vbox, separator);
+ BOX_ADD_NO_EXPAND(vbox, widget);
+ } else {
+ WINDOW_SET_CONTENT(obj->widget, content_vbox);
+ }
+
+ return obj;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+/*
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Grid.h"
+
+#include <X11/Xlib.h>
+
+
+
+static XtActionsRec actionslist[] = {
+ {"getfocus",grid_getfocus},
+ {"loosefocus",grid_loosefocus},
+ {"NULL",NULL}
+};
+
+//static char defaultTranslations[] = "<BtnDown>: mousedown()\n";
+static char defaultTranslations[] = "\
+<EnterWindow>: getfocus()\n\
+<LeaveWindow>: loosefocus()\n";
+
+static XtResource resources[] =
+{
+ {
+ gridColumnSpacing,
+ gridColumnSpacing,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridRec,
+ mywidget.columnspacing),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridRowSpacing,
+ gridRowSpacing,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridRec,
+ mywidget.rowspacing),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridMargin,
+ gridMargin,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridRec,
+ mywidget.margin),
+ XmRImmediate,
+ (XtPointer) 0
+ }
+};
+
+///*
+static XtResource constraints[] =
+{
+ {
+ gridColumn,
+ gridColumn,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.x),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridRow,
+ gridRow,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.y),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridColspan,
+ gridColspan,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.colspan),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridRowspan,
+ gridRowspan,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.rowspan),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridMarginLeft,
+ gridMarginLeft,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.margin_left),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridMarginRight,
+ gridMarginRight,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.margin_right),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridMarginTop,
+ gridMarginTop,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.margin_top),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridMarginBottom,
+ gridMarginBottom,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.margin_bottom),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridHExpand,
+ gridHExpand,
+ XmRBoolean,
+ sizeof (Boolean),
+ XtOffsetOf( GridConstraintRec,
+ grid.hexpand),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridVExpand,
+ gridVExpand,
+ XmRBoolean,
+ sizeof (Boolean),
+ XtOffsetOf( GridConstraintRec,
+ grid.vexpand),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridHFill,
+ gridHFill,
+ XmRBoolean,
+ sizeof (Boolean),
+ XtOffsetOf( GridConstraintRec,
+ grid.hfill),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridVFill,
+ gridVFill,
+ XmRBoolean,
+ sizeof (Boolean),
+ XtOffsetOf( GridConstraintRec,
+ grid.vfill),
+ XmRImmediate,
+ (XtPointer) 0
+ },
+ {
+ gridMinWidth,
+ gridMinWidth,
+ XmRDimension,
+ sizeof (Dimension),
+ XtOffsetOf( GridConstraintRec,
+ grid.min_width),
+ XmRImmediate,
+ (XtPointer) 0
+ }
+};
+//*/
+
+GridClassRec gridClassRec = {
+ // Core Class
+ {
+ //(WidgetClass)&constraintClassRec, // superclass
+ (WidgetClass)&xmManagerClassRec,
+ "Grid", // class_name
+ sizeof(GridRec), // widget_size
+ grid_class_initialize, // class_initialize
+ NULL, // class_part_initialize
+ FALSE, // class_inited
+ (XtInitProc)grid_initialize, // initialize
+ NULL, // initialize_hook
+ grid_realize, // realize
+ actionslist, // actions
+ XtNumber(actionslist), // num_actions
+ resources, // resources
+ XtNumber(resources), // num_resources
+ NULLQUARK, // xrm_class
+ True, // compress_motion
+ True, // compress_exposure
+ True, // compress_enterleave
+ False, // visible_interest
+ (XtWidgetProc)grid_destroy, // destroy
+ (XtWidgetProc)grid_resize, // resize
+ (XtExposeProc)grid_expose, // expose
+ grid_set_values, // set_values
+ NULL, // set_values_hook
+ XtInheritSetValuesAlmost, // set_values_almost
+ NULL, // get_values_hook
+ (XtAcceptFocusProc)grid_acceptfocus, // accept_focus
+ XtVersion, // version
+ NULL, // callback_offsets
+ //NULL, // tm_table
+ defaultTranslations,
+ XtInheritQueryGeometry, // query_geometry
+ NULL, // display_accelerator
+ NULL, // extension
+ },
+ // Composite Class
+ {
+ GridGeometryManager, /* geometry_manager */
+ GridChangeManaged, /* change_managed */
+ XtInheritInsertChild, /* insert_child */
+ XtInheritDeleteChild, /* delete_child */
+ NULL, /* extension */
+ },
+ // Constraint Class
+ {
+ constraints, /* resources */
+ XtNumber(constraints), /* num_resources */
+ sizeof(GridConstraintRec), /* constraint_size */
+ grid_constraint_init, /* initialize */
+ NULL, /* destroy */
+ ConstraintSetValues, /* set_values */
+ NULL, /* extension */
+ },
+ // XmManager Class
+ ///*
+ {
+ XtInheritTranslations,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ XmInheritParentProcess,
+ NULL
+ },
+ //*/
+ // MyWidget Class
+ {
+ 0
+ }
+};
+
+WidgetClass gridClass = (WidgetClass)&gridClassRec;
+
+
+void grid_class_initialize(void) {
+
+}
+void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) {
+ Grid mn = (Grid)new;
+
+ mn->mywidget.max_col = 0;
+ mn->mywidget.max_row = 0;
+
+}
+void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) {
+ Grid grid = (Grid)w;
+ XtMakeResizeRequest(w, 400, 400, NULL, NULL);
+ (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes);
+ grid_place_children(grid);
+}
+
+
+void grid_destroy(Grid widget) {
+
+}
+void grid_resize(Grid widget) {
+ grid_place_children(widget);
+}
+
+void grid_expose(Grid widget, XEvent *event, Region region) {
+
+}
+
+
+Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
+ return False;
+}
+
+Boolean grid_acceptfocus(Widget w, Time *t) {
+
+}
+
+void grid_getfocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) {
+
+}
+
+void grid_loosefocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) {
+
+}
+
+
+
+XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) {
+ GridRec *grid = (GridRec*)XtParent(widget);
+ GridConstraintRec *constraints = widget->core.constraints;
+ //XtVaSetValues(widget, XmNwidth, request->width, XmNheight, request->height, NULL);
+ if((request->request_mode & CWWidth) == CWWidth) {
+ widget->core.width = request->width;
+ constraints->grid.pref_width = request->width;
+ }
+ if((request->request_mode & CWHeight) == CWHeight) {
+ widget->core.height = request->height;
+ constraints->grid.pref_height = request->height;
+ }
+ grid_place_children((Grid)XtParent(widget));
+ return XtGeometryYes;
+}
+
+void GridChangeManaged(Widget widget) {
+
+}
+
+Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
+ GridConstraintRec *constraints = neww->core.constraints;
+ Grid grid = (Grid)XtParent(neww);
+ if(constraints->grid.x > grid->mywidget.max_col) {
+ grid->mywidget.max_col = constraints->grid.x;
+ }
+ if(constraints->grid.y > grid->mywidget.max_row) {
+ grid->mywidget.max_row = constraints->grid.y;
+ }
+}
+
+
+void grid_constraint_init(
+ Widget request,
+ Widget neww,
+ ArgList args,
+ Cardinal* num_args
+)
+{
+ GridConstraintRec *constraints = neww->core.constraints;
+
+ Grid grid = (Grid)XtParent(neww);
+ if(constraints->grid.x > grid->mywidget.max_col) {
+ grid->mywidget.max_col = constraints->grid.x;
+ }
+ if(constraints->grid.y > grid->mywidget.max_row) {
+ grid->mywidget.max_row = constraints->grid.y;
+ }
+ constraints->grid.pref_width = neww->core.width;
+ constraints->grid.pref_height = neww->core.height;
+}
+
+void grid_place_children(Grid w) {
+ int ncols = w->mywidget.max_col+1;
+ int nrows = w->mywidget.max_row+1;
+ GridDef *cols = calloc(ncols, sizeof(GridDef));
+ GridDef *rows = calloc(nrows, sizeof(GridDef));
+ int num_cols_expanding = 0;
+ int num_rows_expanding = 0;
+ int req_width = 0;
+ int req_height = 0;
+
+ //printf("container width: %d\n", (int)w->core.width);
+
+ // calculate the minimum size requirements for all columns and rows
+ // we need to run this 2 times: for widgets without colspan/rowspan first
+ // and then again for colspan/rowspan > 1
+ int span_max = 1;
+ for(int r=0;r<2;r++) {
+ for(int i=0;i<w->composite.num_children;i++) {
+ Widget child = w->composite.children[i];
+ GridConstraintRec *constraints = child->core.constraints;
+ if(constraints->grid.pref_width == 0) {
+ constraints->grid.pref_width = child->core.width;
+ }
+ if(constraints->grid.pref_height == 0) {
+ constraints->grid.pref_height = child->core.height;
+ }
+ if(constraints->grid.pref_width < constraints->grid.min_width) {
+ constraints->grid.pref_width = constraints->grid.min_width;
+ }
+
+ if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) {
+ continue;
+ }
+
+ int x = constraints->grid.x;
+ int y = constraints->grid.y;
+ // make sure ncols/nrows is correct
+ // errors shouldn't happen, unless someone messes up the grid internals
+ if(x >= ncols) {
+ fprintf(stderr, "Error: widget x out of bounds\n");
+ continue;
+ }
+ if(y >= nrows) {
+ fprintf(stderr, "Error: widget y out of bounds\n");
+ continue;
+ }
+ GridDef *col = &cols[x];
+ GridDef *row = &rows[y];
+
+ if(constraints->grid.hexpand) {
+ if(constraints->grid.colspan > 1) {
+ // check if any column in the span is expanding
+ // if not, make the last column expanding
+ GridDef *last_col = col;
+ for(int c=x;c<ncols;c++) {
+ last_col = &cols[c];
+ if(last_col->expand) {
+ break;
+ }
+ }
+ last_col->expand = TRUE;
+ } else {
+ col->expand = TRUE;
+ }
+ }
+ if(constraints->grid.vexpand) {
+ if(constraints->grid.rowspan > 1) {
+ GridDef *last_row = row;
+ for(int c=x;c<nrows;c++) {
+ last_row = &rows[c];
+ if(last_row->expand) {
+ break;
+ }
+ }
+ last_row->expand = TRUE;
+ } else {
+ row->expand = TRUE;
+ }
+ }
+
+ // column size
+ if(constraints->grid.colspan > 1) {
+ // check size of all columns in span
+ Dimension span_width = col->size;
+ GridDef *last_col = col;
+ for(int s=x+1;s<ncols;s++) {
+ last_col = &cols[s];
+ span_width = last_col->size;
+
+ }
+ int diff = constraints->grid.pref_width - span_width;
+ if(diff > 0) {
+ last_col->size += diff;
+ }
+ } else if(constraints->grid.pref_width > col->size) {
+ col->size = constraints->grid.pref_width;
+ }
+ // row size
+ if(constraints->grid.rowspan > 1) {
+ Dimension span_height = row->size;
+ GridDef *last_row = row;
+ for(int s=x+1;s<nrows;s++) {
+ last_row = &rows[s];
+ span_height = last_row->size;
+
+ }
+ int diff = constraints->grid.pref_height - span_height;
+ if(diff > 0) {
+ last_row->size += diff;
+ }
+ } else if(constraints->grid.pref_height > row->size) {
+ row->size = constraints->grid.pref_height;
+ }
+ }
+ span_max = 50000; // not sure if this is unreasonable low or high
+ }
+
+ // calc required size
+ for(int i=0;i<ncols;i++) {
+ if(cols[i].expand) {
+ num_cols_expanding++;
+ }
+ req_width += cols[i].size;
+ }
+ for(int i=0;i<nrows;i++) {
+ if(rows[i].expand) {
+ num_rows_expanding++;
+ }
+ req_height += rows[i].size;
+ }
+
+ if(req_width > 0 && req_height > 0) {
+ // add col/row spacing
+ req_width += (ncols-1)*w->mywidget.columnspacing;
+ req_height += (nrows-1)*w->mywidget.rowspacing;
+
+ Widget parent = w->core.parent;
+ Dimension rwidth = req_width;
+ Dimension rheight = req_height;
+ if(rwidth < w->core.width) {
+ //rwidth = w->core.width;
+ }
+ if(rheight < w->core.height) {
+ //rheight = w->core.height;
+ }
+
+ if(!w->mywidget.sizerequest) {
+ Dimension actual_width, actual_height;
+ w->mywidget.sizerequest = TRUE;
+
+ //printf("sizerequest: %d x %d\n", (int)req_width, (int)req_height);
+
+ //XtWidgetGeometry request;
+ //request.width = req_width;
+ //request.request_mode = CWWidth;
+ //XtWidgetGeometry reply;
+ //XtGeometryResult result = XtMakeGeometryRequest((Widget)w, &request, &reply);
+
+ XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height);
+ w->mywidget.sizerequest = FALSE;
+ //printf("size request: %d %d\n", (int)actual_width, (int)actual_height);
+ }
+
+
+
+ }
+
+ // how much space can we add to each expanding col/row
+ int hexpand = 0;
+ int width_diff = (int)w->core.width - req_width;
+ int hexpand2 = 0;
+ if(width_diff > 0 && num_cols_expanding > 0) {
+ hexpand = width_diff / num_cols_expanding;
+ hexpand2 = width_diff-hexpand*num_cols_expanding;
+ }
+ int x = 0;
+ for(int i=0;i<ncols;i++) {
+ cols[i].pos = x;
+ if(cols[i].expand) {
+ cols[i].size += hexpand + hexpand2;
+ }
+ x += cols[i].size + w->mywidget.columnspacing;
+
+ hexpand2 = 0;
+ }
+
+ int vexpand = 0;
+ int height_diff = (int)w->core.height - req_height;
+ int vexpand2 = 0;
+ if(height_diff > 0 && num_rows_expanding > 0) {
+ vexpand = height_diff / num_rows_expanding;
+ vexpand2 = height_diff-vexpand*num_rows_expanding;
+ }
+ int y = 0;
+ for(int i=0;i<nrows;i++) {
+ rows[i].pos = y;
+ if(rows[i].expand) {
+ rows[i].size += vexpand + vexpand2;
+ }
+ y += rows[i].size += w->mywidget.rowspacing;
+
+ vexpand2 = 0;
+ }
+
+ for(int i=0;i<w->composite.num_children;i++) {
+ Widget child = w->composite.children[i];
+ GridConstraintRec *constraints = child->core.constraints;
+ GridDef c = cols[constraints->grid.x];
+ GridDef r = rows[constraints->grid.y];
+ int x = c.pos;
+ int y = r.pos;
+ int width = constraints->grid.pref_width;
+ int height = constraints->grid.pref_height;
+ if(constraints->grid.hfill) {
+ if(constraints->grid.colspan > 1) {
+ Dimension cwidth = 0;
+ for(int j=0;j<constraints->grid.colspan;j++) {
+ if(constraints->grid.x+j < ncols) {
+ cwidth += cols[constraints->grid.x+j].size + (j > 0 ? w->mywidget.columnspacing : 0);
+ }
+ }
+ width = cwidth;
+ } else {
+ width = c.size - w->mywidget.columnspacing;
+ }
+ }
+ if(constraints->grid.vfill) {
+ if(constraints->grid.rowspan > 1) {
+ Dimension cheight = 0;
+ for(int j=0;j<constraints->grid.rowspan;j++) {
+ if(constraints->grid.y+j < nrows) {
+ cheight += rows[constraints->grid.y+j].size + (j > 0 ? w->mywidget.rowspacing : 0);
+ }
+ }
+ height = cheight;
+ } else {
+ height = r.size - w->mywidget.rowspacing;
+ }
+ }
+
+ if(width > 0 && height > 0) {
+ XtConfigureWidget(child, x, y, width, height, child->core.border_width);
+ }
+ //printf("child %d %d - %d %d\n", (int)child->core.x, (int)child->core.y, (int)child->core.width, (int)child->core.height);
+ }
+
+ free(cols);
+ free(rows);
+}
+
--- /dev/null
+/*
+ * 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 GRID_H
+#define GRID_H
+
+#include <X11/Intrinsic.h>
+#include <X11/IntrinsicP.h>
+#include <Xm/XmAll.h>
+#include <Xm/Primitive.h>
+#include <Xm/PrimitiveP.h>
+#include <Xm/ManagerP.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// resources
+#define gridColumnSpacing "gridColumnSpacing"
+#define gridRowSpacing "gridRowSpacing"
+#define gridMargin "gridMargin"
+
+// constraints
+#define gridColumn "gridColumn"
+#define gridRow "gridRow"
+#define gridColspan "gridColspan"
+#define gridRowspan "gridRowspan"
+#define gridHExpand "gridHExpand"
+#define gridVExpand "gridVExpand"
+#define gridHFill "gridHFill"
+#define gridVFill "gridVFill"
+#define gridMarginLeft "gridMarginLeft"
+#define gridMarginRight "gridMarginRight"
+#define gridMarginTop "gridMarginTop"
+#define gridMarginBottom "gridMarginBottom"
+#define gridMinWidth "gridMinWidth"
+
+
+typedef struct GridDef {
+ Dimension size;
+ Dimension pos;
+ Boolean expand;
+} GridDef;
+
+typedef struct GridClassPart {
+ int test;
+} GridClassPart;
+
+typedef struct GridClassRec {
+ CoreClassPart core_class;
+ CompositeClassPart composite_class;
+ ConstraintClassPart constraint_class;
+ XmManagerClassPart manager_class;
+ GridClassPart mywidgetclass;
+} GridClassRec;
+
+
+typedef struct GridPart {
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int max_col;
+ int max_row;
+ Dimension columnspacing;
+ Dimension rowspacing;
+ Dimension margin;
+
+ Boolean sizerequest;
+} GridPart;
+
+typedef struct GridRec {
+ CorePart core;
+ CompositePart composite;
+ ConstraintPart constraint;
+ XmManagerPart manager;
+ GridPart mywidget;
+} GridRec;
+
+typedef struct GridContraintPart {
+ Dimension x;
+ Dimension y;
+ Dimension margin_left;
+ Dimension margin_right;
+ Dimension margin_top;
+ Dimension margin_bottom;
+ Boolean hexpand;
+ Boolean vexpand;
+ Boolean hfill;
+ Boolean vfill;
+ Dimension colspan;
+ Dimension rowspan;
+ Dimension pref_width;
+ Dimension pref_height;
+ Dimension min_width;
+} GridContraintPart;
+
+typedef struct GridConstraintRec {
+ XmManagerConstraintPart manager;
+ GridContraintPart grid;
+} GridConstraintRec;
+
+typedef GridRec* Grid;
+
+extern WidgetClass gridClass;
+
+void grid_class_initialize(void);
+void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args);
+void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes);
+void grid_destroy(Grid widget);
+void grid_resize(Grid widget);
+void grid_expose(Grid widget, XEvent *event, Region region);
+Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args);
+Boolean grid_acceptfocus(Widget , Time*);
+
+void grid_place_children(Grid w);
+
+void grid_getfocus(Widget myw, XEvent *event, String *params, Cardinal *nparam);
+void grid_loosefocus(Widget myw, XEvent *event, String *params, Cardinal *nparam);
+
+void grid_constraint_init(
+ Widget request,
+ Widget neww,
+ ArgList args,
+ Cardinal* num_args
+);
+
+XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply);
+void GridChangeManaged(Widget widget);
+Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRID_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+$(MOTIF_OBJPRE)%.o: motif/%.c
+ $(CC) -o $@ -c -I../ucx $(CFLAGS) $(SHLIB_CFLAGS) $(TK_CFLAGS) $<
+
+$(UI_LIB): $(OBJ)
+ $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ)
+
+$(UI_SHLIB): $(OBJ)
+ $(CC) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) -L../build/lib -lucx
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "button.h"
+#include "container.h"
+#include "../common/context.h"
+#include <cx/mempool.h>
+
+#include <cx/linked_list.h>
+#include <cx/array_list.h>
+#include <cx/compare.h>
+
+#include <Xm/XmAll.h>
+
+
+UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+
+ XmString label = NULL;
+ if(args->label) {
+ label = XmStringCreateLocalized((char*)args->label);
+ XtSetArg(xargs[n], XmNlabelString, label); n++;
+ }
+
+ char *name = args->name ? (char*)args->name : "button";
+ Widget button = XmCreatePushButton(parent, name, xargs, n);
+ XtManageChild(button);
+ ui_container_add(ctn, button);
+
+ ui_set_widget_groups(obj->ctx, button, args->groups);
+
+ if(args->onclick) {
+ UiEventData *eventdata = malloc(sizeof(UiEventData));
+ eventdata->callback = args->onclick;
+ eventdata->userdata = args->onclickdata;
+ eventdata->obj = obj;
+ eventdata->value = 0;
+ XtAddCallback(
+ button,
+ XmNactivateCallback,
+ (XtCallbackProc)ui_push_button_callback,
+ eventdata);
+ XtAddCallback(
+ button,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_destroy_eventdata,
+ eventdata);
+ }
+
+
+ XmStringFree(label);
+ return button;
+}
+
+void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) {
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = event->obj->window;
+ e.document = event->obj->ctx->document;
+ e.intval = event->value;
+ e.set = 0;
+ event->callback(&e, event->userdata);
+}
+
+UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ XtSetArg(xargs[n], XmNfillOnSelect, True); n++;
+ XtSetArg(xargs[n], XmNindicatorOn, False); n++;
+
+ XmString label = NULL;
+ if(args->label) {
+ label = XmStringCreateLocalized((char*)args->label);
+ XtSetArg(xargs[n], XmNlabelString, label); n++;
+ }
+
+ char *name = args->name ? (char*)args->name : "togglebutton";
+ Widget button = XmCreateToggleButton(parent, name, xargs, n);
+ XtManageChild(button);
+ ui_container_add(ctn, button);
+
+ ui_set_widget_groups(obj->ctx, button, args->groups);
+
+ ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_group);
+
+ XmStringFree(label);
+ return button;
+}
+
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+
+ XmString label = NULL;
+ if(args->label) {
+ label = XmStringCreateLocalized((char*)args->label);
+ XtSetArg(xargs[n], XmNlabelString, label); n++;
+ }
+
+ char *name = args->name ? (char*)args->name : "button";
+ Widget button = XmCreateToggleButton(parent, name, xargs, n);
+ XtManageChild(button);
+ ui_container_add(ctn, button);
+
+ ui_set_widget_groups(obj->ctx, button, args->groups);
+
+ ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_group);
+
+ XmStringFree(label);
+ return button;
+}
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
+ return ui_checkbox_create(obj, args);
+}
+
+static void togglebutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) {
+ if(event->value > 0) {
+ // button in configured to enable/disable states
+ if(tb->set) {
+ ui_set_group(event->obj->ctx, event->value);
+ } else {
+ ui_unset_group(event->obj->ctx, event->value);
+ }
+ }
+
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = e.obj->window;
+ e.document = e.obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = XmToggleButtonGetState(w);
+ e.set = ui_get_setop();
+
+ if(event->callback) {
+ event->callback(&e, event->userdata);
+ }
+
+ if(event->var && event->var->value) {
+ UiInteger *v = event->var->value;
+ v->value = e.intval;
+ ui_notify_evt(v->observers, &e);
+ }
+}
+
+void ui_bind_togglebutton(
+ UiObject *obj,
+ Widget widget,
+ const char *varname,
+ UiInteger *value,
+ ui_callback onchange,
+ void *onchangedata,
+ int enable_state)
+{
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
+ if(var) {
+ value = (UiInteger*)var->value;
+ value->obj = widget;
+ value->get = ui_togglebutton_get;
+ value->set = ui_togglebutton_set;
+
+ if(value->value) {
+ XmToggleButtonSetState(widget, True, False);
+ }
+ }
+
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ event->obj = obj;
+ event->callback = onchange;
+ event->userdata = onchangedata;
+ event->var = var;
+ event->observers = NULL;
+ event->value = enable_state;
+ XtAddCallback(
+ widget,
+ XmNvalueChangedCallback,
+ (XtCallbackProc)togglebutton_changed,
+ event);
+ XtAddCallback(
+ widget,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_destroy_eventdata,
+ event);
+}
+
+int64_t ui_togglebutton_get(UiInteger *i) {
+ Widget togglebutton = i->obj;
+ Boolean state = XmToggleButtonGetState(togglebutton);
+ i->value = state;
+ return state;
+}
+
+void ui_togglebutton_set(UiInteger *i, int64_t value) {
+ Widget togglebutton = i->obj;
+ i->value = value;
+ XmToggleButtonSetState(togglebutton, (Boolean)value, False);
+}
+
+static void destroy_list(Widget w, CxList *list, XtPointer d) {
+ cxListFree(list);
+}
+
+static void radiobutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) {
+ if(event->value > 0) {
+ // button in configured to enable/disable states
+ if(tb->set) {
+ ui_set_group(event->obj->ctx, event->value);
+ } else {
+ ui_unset_group(event->obj->ctx, event->value);
+ }
+ }
+
+ if(!tb->set) {
+ return; // only handle set-events
+ }
+
+ UiInteger *value = NULL;
+ int64_t v = 0;
+ if(event->var) {
+ value = event->var->value;
+ // find widget index and update all radiobuttons
+ // the UiInteger value must always be up-to-date
+ CxList *list = value->obj;
+ CxIterator i = cxListIterator(list);
+ cx_foreach(Widget, button, i) {
+ Boolean state = False;
+ if(button == w) {
+ value->value = i.index+1; // update value
+ state = True;
+ }
+ XmToggleButtonSetState(button, state, False);
+ }
+ v = value->value;
+ }
+
+ UiEvent e;
+ e.obj = event->obj;
+ e.window = e.obj->window;
+ e.document = e.obj->ctx->document;
+ e.eventdata = value;
+ e.eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ e.intval = v;
+ e.set = ui_get_setop();
+
+ if(event->callback) {
+ event->callback(&e, event->userdata);
+ }
+
+ if(value) {
+ ui_notify_evt(value->observers, &e);
+ }
+}
+
+void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const char *varname, ui_callback onchange, void *onchangedata, int enable_group) {
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *value = var->value;
+ CxList *rb = value->obj;
+ if(!rb) {
+ // first button in the radiobutton group
+ // create a list for all buttons and use the list as value obj
+ rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+ value->obj = rb;
+ value->get = ui_radiobutton_get;
+ value->set = ui_radiobutton_set;
+
+ // the first radio button is also responsible for cleanup
+ XtAddCallback(
+ rbutton,
+ XmNdestroyCallback,
+ (XtCallbackProc)destroy_list,
+ rb);
+ }
+ cxListAdd(rb, rbutton);
+
+ // set the radiobutton state, if the value is already set
+ if(cxListSize(rb) == value->value) {
+ XmToggleButtonSetState(rbutton, True, False);
+ }
+ }
+
+ // the radio button needs to handle change events to update all
+ // other buttons in the radio button group
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ event->obj = obj;
+ event->callback = onchange;
+ event->userdata = onchangedata;
+ event->observers = NULL;
+ event->var = var;
+ event->value = enable_group;
+ XtAddCallback(
+ rbutton,
+ XmNvalueChangedCallback,
+ (XtCallbackProc)radiobutton_changed,
+ event);
+ XtAddCallback(
+ rbutton,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_destroy_eventdata,
+ event);
+}
+
+UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++;
+ XmString label = NULL;
+ if(args->label) {
+ label = XmStringCreateLocalized((char*)args->label);
+ XtSetArg(xargs[n], XmNlabelString, label); n++;
+ }
+
+ char *name = args->name ? (char*)args->name : "button";
+ Widget button = XmCreateToggleButton(parent, name, xargs, n);
+ XtManageChild(button);
+ ui_container_add(ctn, button);
+
+ ui_set_widget_groups(obj->ctx, button, args->groups);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *value = var->value;
+ CxList *rb = value->obj;
+ if(!rb) {
+ // first button in the radiobutton group
+ // create a list for all buttons and use the list as value obj
+ rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+ value->obj = rb;
+ value->get = ui_radiobutton_get;
+ value->set = ui_radiobutton_set;
+
+ // the first radio button is also responsible for cleanup
+ XtAddCallback(
+ button,
+ XmNdestroyCallback,
+ (XtCallbackProc)destroy_list,
+ rb);
+ }
+ cxListAdd(rb, button);
+
+ // set the radiobutton state, if the value is already set
+ if(cxListSize(rb) == value->value) {
+ XmToggleButtonSetState(button, True, False);
+ }
+ }
+
+ // the radio button needs to handle change events to update all
+ // other buttons in the radio button group
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ event->obj = obj;
+ event->callback = args->onchange;
+ event->userdata = args->onchangedata;
+ event->observers = NULL;
+ event->var = var;
+ event->value = args->enable_group;
+ XtAddCallback(
+ button,
+ XmNvalueChangedCallback,
+ (XtCallbackProc)radiobutton_changed,
+ event);
+ XtAddCallback(
+ button,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_destroy_eventdata,
+ event);
+
+ XmStringFree(label);
+ return button;
+
+
+}
+
+int64_t ui_radiobutton_get(UiInteger *i) {
+ // the UiInteger should be updated automatically by change events
+ return i->value;
+}
+
+void ui_radiobutton_set(UiInteger *i, int64_t value) {
+ CxList *list = i->obj;
+ if(i->value > 0) {
+ Widget current = cxListAt(list, i->value-1);
+ if(current) {
+ XmToggleButtonSetState(current, False, False);
+ }
+ }
+ if(value > 0 && value <= cxListSize(list)) {
+ Widget button = cxListAt(list, value-1);
+ if(button) {
+ XmToggleButtonSetState(button, True, False);
+ i->value = value;
+ }
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 BUTTON_H
+#define BUTTON_H
+
+#include "../ui/button.h"
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d);
+
+void ui_bind_togglebutton(
+ UiObject *obj,
+ Widget widget,
+ const char *varname,
+ UiInteger *value,
+ ui_callback onchange,
+ void *onchangedata,
+ int enable_state);
+
+int64_t ui_togglebutton_get(UiInteger *i);
+void ui_togglebutton_set(UiInteger *i, int64_t value);
+
+void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const char *varname, ui_callback onchange, void *onchangedata, int enable_group);
+
+int64_t ui_radiobutton_get(UiInteger *i);
+void ui_radiobutton_set(UiInteger *i, int64_t value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BUTTON_H */
+
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "container.h"
+#include "../common/context.h"
+#include "../common/object.h"
+#include "../common/container.h"
+
+#include <cx/array_list.h>
+
+#include "Grid.h"
+
+
+
+Widget ui_container_prepare(UiContainerPrivate *container, UiLayout *layout, Arg *args, int *n) {
+ if(layout->margin != 0) {
+ layout->margin_left = layout->margin;
+ layout->margin_right = layout->margin;
+ layout->margin_top = layout->margin;
+ layout->margin_bottom = layout->margin;
+ }
+ return container->prepare(container, layout, args, n);
+}
+
+void ui_container_add(UiContainerPrivate *container, Widget widget) {
+ container->add(container, widget);
+}
+
+
+/* ---------------------------- Box Container ---------------------------- */
+
+static UIWIDGET box_create(UiObject *obj, UiContainerArgs *args, UiBoxOrientation orientation) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Arg xargs[16];
+ int n = 0;
+
+ if(orientation == UI_BOX_VERTICAL) {
+ //XtSetArg(xargs[n], gridRowSpacing, args->spacing); n++;
+ } else {
+ //XtSetArg(xargs[n], gridColumnSpacing, args->spacing); n++;
+ }
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ Widget grid = XtCreateManagedWidget(args->name ? args->name : "boxcontainer", gridClass, parent, xargs, n);
+ ctn->add(ctn, grid);
+
+ UiContainerX *container = ui_box_container(obj, grid, orientation);
+ uic_object_push_container(obj, container);
+
+ return grid;
+}
+
+// public
+UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
+ return box_create(obj, args, UI_BOX_VERTICAL);
+}
+
+// public
+UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
+ return box_create(obj, args, UI_BOX_HORIZONTAL);
+}
+
+UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation) {
+ UiBoxContainer *ctn = ui_malloc(obj->ctx, sizeof(UiBoxContainer));
+ memset(ctn, 0, sizeof(UiBoxContainer));
+ ctn->container.prepare = orientation == UI_BOX_VERTICAL ? ui_vbox_prepare : ui_hbox_prepare;
+ ctn->container.add = ui_box_container_add;
+ ctn->container.widget = grid;
+ ctn->n = 0;
+ return (UiContainerX*)ctn;
+}
+
+static Widget ui_box_container_prepare(UiBoxContainer *box, UiLayout *layout, Arg *args, int *n) {
+ int a = *n;
+ box->n++;
+ return box->container.widget;
+}
+
+Widget ui_vbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
+ UiBoxContainer *box = (UiBoxContainer*)ctn;
+ int a = *n;
+ XtSetArg(args[a], gridRow, box->n); a++;
+ if(layout->fill) {
+ XtSetArg(args[a], gridVExpand, TRUE); a++;
+ XtSetArg(args[a], gridVFill, TRUE); a++;
+ }
+ XtSetArg(args[a], gridHExpand, TRUE); a++;
+ XtSetArg(args[a], gridHFill, TRUE); a++;
+ *n = a;
+ return ui_box_container_prepare(box, layout, args, n);
+}
+
+Widget ui_hbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
+ UiBoxContainer *box = (UiBoxContainer*)ctn;
+ int a = *n;
+ XtSetArg(args[a], gridColumn, box->n); a++;
+ if(layout->fill) {
+ XtSetArg(args[a], gridHExpand, TRUE); a++;
+ XtSetArg(args[a], gridHFill, TRUE); a++;
+ }
+ XtSetArg(args[a], gridVExpand, TRUE); a++;
+ XtSetArg(args[a], gridVFill, TRUE); a++;
+ *n = a;
+ return ui_box_container_prepare(box, layout, args, n);
+}
+
+void ui_box_container_add(UiContainerPrivate *ctn, Widget widget) {
+
+}
+
+
+/* ---------------------------- Grid Container ---------------------------- */
+
+// public
+UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ XtSetArg(xargs[n], gridMargin, args->margin); n++;
+ XtSetArg(xargs[n], gridColumnSpacing, args->columnspacing); n++;
+ XtSetArg(xargs[n], gridRowSpacing, args->rowspacing); n++;
+ Widget grid = XtCreateManagedWidget(args->name ? args->name : "gridcontainer", gridClass, parent, xargs, n);
+ ui_container_add(ctn, grid);
+
+ UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill);
+ uic_object_push_container(obj, container);
+
+ return grid;
+}
+
+UiContainerX* ui_grid_container(
+ UiObject *obj,
+ Widget grid,
+ UiBool def_hexpand,
+ UiBool def_vexpand,
+ UiBool def_hfill,
+ UiBool def_vfill)
+{
+ UiGridContainer *ctn = ui_malloc(obj->ctx, sizeof(UiGridContainer));
+ memset(ctn, 0, sizeof(UiBoxContainer));
+ ctn->container.prepare = ui_grid_container_prepare;
+ ctn->container.add = ui_grid_container_add;
+ ctn->container.widget = grid;
+ ctn->x = 0;
+ ctn->y = 0;
+ ctn->def_hexpand = def_hexpand;
+ ctn->def_vexpand = def_vexpand;
+ ctn->def_hfill = def_hfill;
+ ctn->def_vfill = def_vfill;
+ return (UiContainerX*)ctn;
+}
+
+Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
+ UiGridContainer *grid = (UiGridContainer*)ctn;
+ if(ctn->container.newline) {
+ grid->y++;
+ grid->x = 0;
+ }
+
+ int a = *n;
+ XtSetArg(args[a], gridColumn, grid->x); a++;
+ XtSetArg(args[a], gridRow, grid->y); a++;
+ if(layout->colspan > 0) {
+ XtSetArg(args[a], gridColspan, layout->colspan); a++;
+ }
+ if(layout->rowspan > 0) {
+ XtSetArg(args[a], gridRowspan, layout->rowspan); a++;
+ }
+
+ uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill);
+
+ if(layout->hfill) {
+ XtSetArg(args[a], gridHFill, TRUE); a++;
+ }
+ if(layout->vfill) {
+ XtSetArg(args[a], gridVFill, TRUE); a++;
+ }
+ if(layout->hexpand) {
+ XtSetArg(args[a], gridHExpand, TRUE); a++;
+ }
+ if(layout->vexpand) {
+ XtSetArg(args[a], gridVExpand, TRUE); a++;
+ }
+
+ *n = a;
+ return ctn->widget;
+}
+
+void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget) {
+ UiGridContainer *grid = (UiGridContainer*)ctn;
+ grid->x++;
+ grid->container.container.newline = FALSE;
+}
+
+
+/* -------------------------- TabView Container -------------------------- */
+
+static void ui_tabbar_resize(Widget widget, XtPointer udata, XtPointer cdata) {
+ UiMotifTabView *tabview = udata;
+
+ if(tabview->tabview == UI_TABVIEW_INVISIBLE) {
+ return;
+ }
+
+ int width = 0;
+ int height = 0;
+ XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL);
+ int numbuttons = cxListSize(tabview->tabs);
+ if(numbuttons == 0) {
+ return;
+ }
+ int button_width = width / numbuttons;
+ int x = 0;
+
+ CxIterator i = cxListIterator(tabview->tabs);
+ cx_foreach(UiTab *, tab, i) {
+ if(i.index + 1 == numbuttons) {
+ button_width = width - x;
+ }
+ XtVaSetValues(
+ tab->tab_button,
+ XmNx, x,
+ XmNy, 0,
+ XmNwidth,
+ button_width,
+
+ NULL);
+ x += button_width;
+ }
+
+ if(height <= tabview->height) {
+ XtVaSetValues(widget, XmNheight, tabview->height + 4, NULL);
+ }
+}
+
+static void ui_tabbar_expose(Widget widget, XtPointer udata, XtPointer cdata) {
+ UiMotifTabView *tabview = udata;
+ XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)cdata;
+ XEvent *event = cbs->event;
+ Display *dpy = XtDisplay(widget);
+
+ if(!tabview->gc_initialized) {
+ XGCValues gcvals;
+ gcvals.foreground = tabview->fg1;
+ tabview->gc = XCreateGC(XtDisplay(tabview->tabbar), XtWindow(tabview->tabbar), (GCForeground), &gcvals);
+ }
+
+ if(tabview->current_tab) {
+ Widget tab = tabview->current_tab->tab_button;
+ XFillRectangle(dpy, XtWindow(widget), tabview->gc, tab->core.x, tab->core.height, tab->core.width, 4);
+ }
+}
+
+UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ // create widgets
+ // form
+ // - tabbar (Drawing Area)
+ // - content (Frame)
+ UiMotifTabView *tabview = malloc(sizeof(UiMotifTabView));
+ memset(tabview, 0, sizeof(UiMotifTabView));
+ char *name = args->name ? (char*)args->name : "tabview";
+ XtSetArg(xargs[n], XmNuserData, tabview); n++;
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ Widget form = XmCreateForm(parent, name, xargs, n);
+ XtManageChild(form);
+ ui_container_add(ctn, parent);
+
+ n = 0;
+ XtSetArg(xargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(xargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(xargs[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(xargs[n], XmNorientation, XmHORIZONTAL); n++;
+ XtSetArg(xargs[n], XmNpacking, XmPACK_TIGHT); n++;
+ XtSetArg(xargs[n], XmNspacing, 1); n++;
+ XtSetArg(xargs[n], XmNmarginWidth, 0); n++;
+ XtSetArg(xargs[n], XmNmarginHeight, 0); n++;
+ Widget tabbar = XmCreateDrawingArea(form, "ui_test", xargs, n);
+ XtManageChild(tabbar);
+ XtAddCallback(tabbar, XmNresizeCallback , ui_tabbar_resize, tabview);
+ XtAddCallback(tabbar, XmNexposeCallback, ui_tabbar_expose, tabview);
+
+ n = 0;
+ XtSetArg(xargs[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(xargs[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(xargs[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(xargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(xargs[n], XmNtopWidget, tabbar); n++;
+ Widget content = XmCreateFrame(form, "tabviewcontent", xargs, n);
+
+ // setup tabview object, that holds all relevant objects
+ tabview->obj = obj;
+ tabview->form = form;
+ tabview->tabbar = tabbar;
+ tabview->content = content;
+ tabview->tabview = args->tabview;
+ tabview->subcontainer = args->subcontainer;
+ tabview->select = ui_motif_tabview_select;
+ tabview->add = ui_motif_tabview_add_tab;
+ tabview->remove = ui_motif_tabview_remove;
+ tabview->tabs = cxArrayListCreate(obj->ctx->allocator, cx_cmp_ptr, sizeof(UiTab), 8);
+ tabview->current_index = -1;
+
+ UiTabViewContainer *ct = ui_malloc(obj->ctx, sizeof(UiTabViewContainer));
+ ct->container.widget = form;
+ ct->container.type = UI_CONTAINER_TABVIEW;
+ ct->container.prepare = ui_tabview_container_prepare;
+ ct->container.add = ui_tabview_container_add;
+ ct->tabview = tabview;
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *i = var->value;
+ i->obj = tabview;
+ i->get = ui_tabview_get;
+ i->set = ui_tabview_set;
+ }
+
+ uic_object_push_container(obj, (UiContainerX*)ct);
+
+ return form;
+}
+
+int64_t ui_tabview_get(UiInteger *i) {
+ UiMotifTabView *tabview = i->obj;
+ i->value = tabview->current_index;
+ return i->value;
+}
+
+void ui_tabview_set(UiInteger *i, int64_t value) {
+ UiMotifTabView *tabview = i->obj;
+ if(value < cxListSize(tabview->tabs)) {
+ ui_motif_tabview_select(tabview, value);
+ i->value = value;
+ }
+}
+
+void ui_tab_create(UiObject *obj, const char* title) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ if(ctn->type != UI_CONTAINER_TABVIEW) {
+ fprintf(stderr, "UI Error: container is not a tabview\n");
+ return;
+ }
+
+ UiMotifTabView *tabview = NULL;
+ XtVaGetValues(ctn->widget, XmNuserData, &tabview, NULL);
+ if(!tabview) {
+ fprintf(stderr, "UI Error: no tabview\n");
+ return;
+ }
+
+
+ Widget child = ui_vbox_create(obj, &(UiContainerArgs) { 0 });
+ if(tabview->current) {
+ XtUnmanageChild(child);
+ } else {
+ tabview->current = child;
+ }
+
+ tabview->add(tabview, -1, title, child);
+}
+
+void ui_tabview_select(UIWIDGET tabview, int tab) {
+ UiMotifTabView *tabviewdata = NULL;
+ XtVaGetValues(tabview, XmNuserData, &tabviewdata, NULL);
+ if(tabviewdata) {
+ ui_motif_tabview_select(tabviewdata, tab);
+ } else {
+ fprintf(stderr, "ui_tabview_select: widget is not a tabview\n");
+ }
+}
+
+void ui_tabview_remove(UIWIDGET tabview, int tab) {
+ UiMotifTabView *tabviewdata = NULL;
+ XtVaGetValues(tabview, XmNuserData, &tabviewdata, NULL);
+ if(tabviewdata) {
+ ui_motif_tabview_remove(tabviewdata, tab);
+ } else {
+ fprintf(stderr, "ui_tabview_select: widget is not a tabview\n");
+ }
+}
+
+UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
+ UiMotifTabView *tabviewdata = NULL;
+ XtVaGetValues(tabview, XmNuserData, &tabviewdata, NULL);
+ if(tabviewdata) {
+ Arg args[16];
+ int n = 0;
+
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNtopWidget, tabviewdata->tabbar); n++;
+
+ Widget grid = XtCreateManagedWidget("vbox", gridClass, tabviewdata->content, args, n);
+
+ UiObject *newobj = ui_calloc(tabviewdata->obj->ctx, 1, sizeof(UiObject));
+ newobj->ctx = tabviewdata->obj->ctx;
+ newobj->widget = grid;
+ UiContainerX *container = ui_box_container(newobj, grid, UI_BOX_VERTICAL);
+ newobj->container_begin = container;
+ newobj->container_end = container;
+ return newobj;
+ } else {
+ fprintf(stderr, "ui_tabview_select: widget is not a tabview\n");
+ return NULL;
+ }
+}
+
+void ui_motif_tabview_select(UiMotifTabView *tabview, int tab) {
+ UiTab *t = cxListAt(tabview->tabs, tab);
+ if(t) {
+ tabview->current_index = tab;
+ ui_motif_tabview_change_tab(tabview, t);
+ }
+}
+
+static void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d) {
+ UiMotifTabView *tabview = NULL;
+ XtVaGetValues(widget, XmNuserData, &tabview, NULL);
+ ui_motif_tabview_change_tab(tabview, tab);
+}
+
+void ui_motif_tabview_add_tab(UiMotifTabView *tabview, int index, const char *name, Widget child) {
+ UiTab tab;
+
+ Arg args[16];
+ int n = 0;
+
+ XmString label = XmStringCreateLocalized((char*)name);
+ XtSetArg(args[n], XmNlabelString, label); n++;
+ XtSetArg(args[n], XmNshadowThickness, 0); n++;
+ XtSetArg(args[n], XmNhighlightThickness, 0); n++;
+ XtSetArg(args[n], XmNuserData, tabview); n++;
+
+ Widget button = XmCreatePushButton(tabview->tabbar, "tab_button", args, n);
+ if(tabview->tabview != UI_TABVIEW_INVISIBLE) {
+ XtManageChild(button);
+ }
+
+ if(tabview->height == 0) {
+ Dimension h;
+ XtVaGetValues(
+ button,
+ XmNarmColor,
+ &tabview->bg1,
+ XmNbackground,
+ &tabview->bg2,
+ XmNhighlightColor,
+ &tabview->fg1,
+ XmNheight,
+ &h,
+ NULL);
+ tabview->height = h + 2; // border
+
+ XtVaSetValues(tabview->tabbar, XmNbackground, tabview->bg1, NULL);
+ }
+
+ tab.tab_button = button;
+ tab.child = child;
+ size_t newtab_index = cxListSize(tabview->tabs);
+ cxListAdd(tabview->tabs, &tab);
+ UiTab *newtab = cxListAt(tabview->tabs, newtab_index);
+
+ XtAddCallback(
+ button,
+ XmNactivateCallback,
+ (XtCallbackProc)ui_tab_button_callback,
+ newtab);
+
+ if(newtab_index == 0) {
+ ui_motif_tabview_change_tab(tabview, newtab);
+ } else {
+ XtVaSetValues(button, XmNbackground, tabview->bg1, NULL);
+ }
+}
+
+void ui_motif_tabview_remove(UiMotifTabView *tabview, int index) {
+ UiTab *tab = cxListAt(tabview->tabs, index);
+ if(tab) {
+ if(tab == tabview->current_tab) {
+ if(index > 0) {
+ ui_motif_tabview_select(tabview, index-1);
+ } else {
+ if(index < cxListSize(tabview->tabs)) {
+ ui_motif_tabview_select(tabview, index+1);
+ } else {
+ tabview->current_tab = NULL;
+ tabview->current_index = -1;
+ }
+ }
+ }
+ XtDestroyWidget(tab->tab_button);
+ XtDestroyWidget(tab->child);
+ cxListRemove(tabview->tabs, index);
+ }
+}
+
+void ui_motif_tabview_change_tab(UiMotifTabView *tabview, UiTab *tab) {
+ if(tabview->current_tab) {
+ XtVaSetValues(tabview->current_tab->tab_button, XmNshadowThickness, 0, XmNbackground, tabview->bg1, NULL);
+ XtUnmanageChild(tabview->current_tab->child);
+ }
+ XtVaSetValues(tab->tab_button, XmNshadowThickness, 1, XmNbackground, tabview->bg2, NULL);
+ tabview->current_tab = tab;
+ tabview->current_index = (int)cxListFind(tabview->tabs, tab);;
+ XtManageChild(tab->child);
+}
+
+Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
+ UiTabViewContainer *ct = (UiTabViewContainer*)ctn;
+ UiMotifTabView *tabview = ct->tabview;
+ int a = *n;
+ XtSetArg(args[a], XmNleftAttachment, XmATTACH_FORM); a++;
+ XtSetArg(args[a], XmNrightAttachment, XmATTACH_FORM); a++;
+ XtSetArg(args[a], XmNbottomAttachment, XmATTACH_FORM); a++;
+ XtSetArg(args[a], XmNtopAttachment, XmATTACH_WIDGET); a++;
+ XtSetArg(args[a], XmNtopWidget, tabview->tabbar); a++;
+ *n = a;
+ return tabview->form;
+}
+
+void ui_tabview_container_add(UiContainerPrivate *ctn, Widget widget) {
+
+}
+
+/* -------------------- ScrolledWindow -------------------- */
+
+Widget ui_scrolledwindow_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) {
+ return ctn->widget;
+}
+
+void ui_scrolledwindow_add(UiContainerPrivate *ctn, Widget widget) {
+
+}
+
+static UiContainerX* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow) {
+ UiContainerPrivate *ctn = ui_malloc(obj->ctx, sizeof(UiContainerPrivate));
+ memset(ctn, 0, sizeof(UiContainerPrivate));
+ ctn->prepare = ui_scrolledwindow_prepare;
+ ctn->add = ui_scrolledwindow_add;
+ ctn->widget = scrolledwindow;
+ return (UiContainerX*)ctn;
+}
+
+UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Arg xargs[16];
+ int n = 0;
+
+ XtSetArg(xargs[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ Widget scrolledwindow = XmCreateScrolledWindow(parent, "scrolledwindow", xargs, n);
+ ui_container_add(ctn, scrolledwindow);
+
+ UiContainerX *container = ui_scrolledwindow_container(obj, scrolledwindow);
+ uic_object_push_container(obj, container);
+
+ return scrolledwindow;
+}
+
+/* -------------------- Container Helper Functions -------------------- */
+
+void ui_container_begin_close(UiObject *obj) {
+ UiContainerPrivate *ct = ui_obj_container(obj);
+ ct->container.close = 1;
+}
+
+int ui_container_finish(UiObject *obj) {
+ UiContainerPrivate *ct = ui_obj_container(obj);
+ if(ct->container.close) {
+ ui_end_new(obj);
+ return 0;
+ }
+ return 1;
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 CONTAINER_H
+#define CONTAINER_H
+
+#include "../ui/toolkit.h"
+#include "../ui/container.h"
+#include <cx/list.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_APPLY_LAYOUT(layout, args) \
+ layout.fill = args->fill; \
+ layout.hexpand = args->hexpand; \
+ layout.vexpand = args->vexpand; \
+ layout.hfill = args->hfill; \
+ layout.vfill = args->vfill; \
+ layout.override_defaults = args->override_defaults; \
+ layout.colspan = args->colspan; \
+ layout.rowspan = args->rowspan
+
+typedef enum UiBoxOrientation UiBoxOrientation;
+
+enum UiContainerType {
+ UI_CONTAINER_GENERIC = 0,
+ UI_CONTAINER_TABVIEW
+};
+typedef enum UiContainerType UiContainerType;
+
+#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout))
+#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE)
+#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE)
+
+#define ui_obj_container(obj) (UiContainerPrivate*)obj->container_end
+
+
+enum UiBoxOrientation {
+ UI_BOX_VERTICAL = 0,
+ UI_BOX_HORIZONTAL
+};
+
+typedef struct UiContainerPrivate UiContainerPrivate;
+
+
+struct UiContainerPrivate {
+ UiContainerX container;
+ Widget (*prepare)(UiContainerPrivate*, UiLayout *layout, Arg *, int*);
+ void (*add)(UiContainerPrivate*, Widget);
+ Widget widget;
+ UiContainerType type;
+};
+
+typedef struct UiBoxContainer {
+ UiContainerPrivate container;
+ Dimension n;
+} UiBoxContainer;
+
+typedef struct UiGridContainer {
+ UiContainerPrivate container;
+ Dimension x;
+ Dimension y;
+ UiBool def_hexpand;
+ UiBool def_vexpand;
+ UiBool def_hfill;
+ UiBool def_vfill;
+} UiGridContainer;
+
+typedef struct UiTab {
+ Widget tab_button;
+ Widget child;
+} UiTab;
+
+typedef struct UiMotifTabView UiMotifTabView;
+struct UiMotifTabView {
+ UiObject *obj;
+ Widget form;
+ Widget tabbar;
+ Widget content;
+ Widget current;
+ UiTab *current_tab;
+ int current_index;
+ int height;
+ Pixel bg1;
+ Pixel bg2;
+ Pixel fg1;
+ GC gc;
+ int gc_initialized;
+ UiTabViewType tabview;
+ UiSubContainerType subcontainer;
+ CxList *tabs;
+ void (*select)(UiMotifTabView *tabview, int tab);
+ void (*add)(UiMotifTabView *tabview, int index, const char *name, Widget child);
+ void (*remove)(UiMotifTabView *tabview, int index);
+};
+
+typedef struct UiTabViewContainer {
+ UiContainerPrivate container;
+ UiMotifTabView *tabview;
+} UiTabViewContainer;
+
+Widget ui_container_prepare(UiContainerPrivate *container, UiLayout *layout, Arg *args, int *n);
+void ui_container_add(UiContainerPrivate *container, Widget widget);
+
+void ui_motif_tabview_select(UiMotifTabView *tabview, int tab);
+void ui_motif_tabview_add_tab(UiMotifTabView *tabview, int index, const char *name, Widget child);
+void ui_motif_tabview_remove(UiMotifTabView *tabview, int index);
+void ui_motif_tabview_change_tab(UiMotifTabView *tabview, UiTab *tab);
+int64_t ui_tabview_get(UiInteger *i);
+void ui_tabview_set(UiInteger *i, int64_t value);
+
+Widget ui_tabview_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
+void ui_tabview_container_add(UiContainerPrivate *ctn, Widget widget);
+
+UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation);
+Widget ui_vbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
+Widget ui_hbox_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
+void ui_box_container_add(UiContainerPrivate *ctn, Widget widget);
+
+
+UiContainerX* ui_grid_container(
+ UiObject *obj,
+ Widget grid,
+ UiBool def_hexpand,
+ UiBool def_vexpand,
+ UiBool def_hfill,
+ UiBool def_vfill);
+Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n);
+void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONTAINER_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2018 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 "dnd.h"
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2018 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 DND_H
+#define DND_H
+
+#include "../ui/dnd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DND_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Intrinsic.h>
+#include <X11/IntrinsicP.h>
+
+#include "graphics.h"
+
+#include "container.h"
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 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 GRAPHICS_H
+#define GRAPHICS_H
+
+#include "../ui/graphics.h"
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRAPHICS_H */
+
--- /dev/null
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+#include "image.h"
+
+
+
+void ui_image_ref(UIIMAGE img) {
+ // TODO
+}
+
+void ui_image_unref(UIIMAGE img) {
+ // TODO
+}
--- /dev/null
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/*
+ * File: image.h
+ * Author: olaf
+ *
+ * Created on 1. Juli 2018, 19:01
+ */
+
+#ifndef IMAGE_H
+#define IMAGE_H
+
+#include "../ui/image.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IMAGE_H */
+
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "label.h"
+#include "container.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+#include "Grid.h"
+
+static UIWIDGET label_create(UiObject *obj, UiLabelArgs *args, int align) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+
+ XtSetArg(xargs[n], XmNalignment, align); n++;
+ XmString label = NULL;
+ if(args->label) {
+ label = XmStringCreateLocalized((char*)args->label);
+ XtSetArg(xargs[n], XmNlabelString, label); n++;
+ }
+
+ char *name = args->name ? (char*)args->name : "label";
+ Widget w = XmCreateLabel(parent, name, xargs, n);
+ XtManageChild(w);
+ ui_container_add(ctn, w);
+
+ XmStringFree(label);
+ return w;
+}
+
+UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs *args) {
+ return label_create(obj, args, XmALIGNMENT_CENTER);
+}
+
+UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs *args) {
+ return label_create(obj, args, XmALIGNMENT_BEGINNING);
+}
+
+UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs *args) {
+ return label_create(obj, args, XmALIGNMENT_END);
+}
+
+
+/* -------------------------- progressbar/spiner -------------------------- */
+
+static void ui_destroy_progressbar(Widget w, UiProgressBar *pb, XtPointer d) {
+ // TODO: free other stuff
+ free(pb);
+}
+
+static void ui_progressbar_expose(Widget widget, UiProgressBar *pb, XtPointer c) {
+ Display *dp = XtDisplay(widget);
+ Window w = XtWindow(widget);
+ if(w == 0) {
+ return;
+ }
+ if(!pb->gc) {
+ XGCValues gcvals;
+ gcvals.foreground = pb->color;
+ pb->gc = XCreateGC(dp, w, (GCForeground), &gcvals);
+ }
+
+ Dimension width = widget->core.width;
+ Dimension height = widget->core.height;
+
+ double value = (pb->value - pb->min) / (pb->max - pb->min);
+ Dimension valueW = (double)width * value;
+
+ XClearArea(dp, w, 0, 0, width, height, False);
+ XFillRectangle(dp, w, pb->gc, 0, 0, valueW, widget->core.height);
+}
+
+UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+
+ char *name = args->name ? (char*)args->name : "progressbar";
+ Widget frame = XmCreateFrame(parent, name, xargs, n);
+ ui_container_add(ctn, frame);
+
+ // create a button and get some informations about the height, shadow, highlight, ....
+ // we want the frame to have the same dimensions as a normal button
+ Widget test = XmCreatePushButton(frame, "button", NULL, 0);
+ XtManageChild(test);
+ Dimension h, highlightThickness, shadowThickness;
+ Pixel highlightColor;
+ XtVaGetValues(test, XmNheight, &h, XmNhighlightThickness, &highlightThickness,
+ XmNshadowThickness, &shadowThickness, XmNhighlightColor, &highlightColor, NULL);
+ XtDestroyWidget(test);
+
+ // adjust frame
+ XtVaSetValues(frame, XmNshadowThickness, shadowThickness, gridMarginLeft, highlightThickness,
+ gridMarginRight, highlightThickness, gridMarginTop, highlightThickness,
+ gridMarginBottom, highlightThickness, NULL);
+
+ // create drawing area
+ Dimension da_height = h - 2*highlightThickness - 2*shadowThickness;
+ n = 0;
+ XtSetArg(xargs[n], XmNheight, da_height); n++;
+ Widget drawingArea = XmCreateDrawingArea(frame, "progressbar_drawingarea", xargs, n);
+ XtManageChild(drawingArea);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_DOUBLE);
+
+ UiProgressBar *progressbarData = malloc(sizeof(UiProgressBar));
+ progressbarData->widget = drawingArea;
+ progressbarData->min = args->min;
+ progressbarData->max = args->max == 0 ? 100 : args->max;
+ progressbarData->value = 50;
+ progressbarData->var = var;
+ progressbarData->color = highlightColor;
+ progressbarData->gc = NULL; // initialize on first expose
+
+ if(var) {
+ UiDouble *d = var->value;
+ progressbarData->value = d->value;
+ d->obj = progressbarData;
+ d->get = ui_progressbar_get;
+ d->set = ui_progressbar_set;
+ }
+
+ XtAddCallback(
+ drawingArea,
+ XmNexposeCallback,
+ (XtCallbackProc)ui_progressbar_expose,
+ progressbarData);
+ XtAddCallback(
+ drawingArea,
+ XmNresizeCallback,
+ (XtCallbackProc)ui_progressbar_expose,
+ progressbarData);
+
+
+ XtManageChild(frame);
+ return frame;
+}
+
+double ui_progressbar_get(UiDouble *d) {
+ UiProgressBar *pb = d->obj;
+ d->value = pb->value;
+ return d->value;
+}
+
+void ui_progressbar_set(UiDouble *d, double value) {
+ UiProgressBar *pb = d->obj;
+ d->value = value;
+ pb->value = value;
+ ui_progressbar_expose(pb->widget, pb, NULL);
+}
+
+
+UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+
+ XmString label = XmStringCreateSimple("");
+ XtSetArg(xargs[n], XmNlabelString, label); n++;
+ XtSetArg(xargs[n], XmNalignment, XmALIGNMENT_END); n++;
+ XtSetArg(xargs[n], gridMinWidth, 40); n++;
+
+ char *name = args->name ? (char*)args->name : "progresss_spinner";
+ Widget w = XmCreateLabel(parent, name, xargs, n);
+ XtManageChild(w);
+ ui_container_add(ctn, w);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *value = var->value;
+ value->obj = w;
+ value->get = ui_progressspinner_get;
+ value->set = ui_progressspinner_set;
+
+ if(value->value) {
+ ui_progressspinner_set(value, 1);
+ }
+ }
+
+
+ XmStringFree(label);
+ return w;
+}
+
+int64_t ui_progressspinner_get(UiInteger *i) {
+ return i->value;
+}
+
+void ui_progressspinner_set(UiInteger *i, int64_t value) {
+ Widget w = i->obj;
+ XmString label;
+ if(value) {
+ char str[4];
+ snprintf(str, 4, "%c", 150);
+ label = XmStringCreateSimple(str);
+ } else {
+ label = XmStringCreateSimple("");
+ }
+ XtVaSetValues(w, XmNlabelString, label, NULL);
+ XmStringFree(label);
+ i->value = value;
+}
--- /dev/null
+/*
+ * 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 LABEL_H
+#define LABEL_H
+
+#include "../ui/display.h"
+#include "../common/context.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiProgressBar {
+ Widget widget;
+ GC gc;
+ UiVar *var;
+ double min;
+ double max;
+ double value;
+ Pixel color;
+} UiProgressBar;
+
+
+double ui_progressbar_get(UiDouble *d);
+void ui_progressbar_set(UiDouble *d, double value);
+
+int64_t ui_progressspinner_get(UiInteger *i);
+void ui_progressspinner_set(UiInteger *i, int64_t value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LABEL_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "container.h"
+
+#include "list.h"
+#include "../common/object.h"
+
+static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
+ return getvalue(elm, col);
+}
+
+/*
+static void* model_getvalue(UiModel *model, UiList *list, void *elm, int row, int col, UiBool *freeResult) {
+ if(model->getvalue2) {
+ return model->getvalue2(list, elm, row, col, model->getvalue2data, freeResult);
+ } else if(model->getvalue) {
+ return model->getvalue(elm, col);
+ }
+ return NULL;
+}
+*/
+
+UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ if(args->multiselection) {
+ XtSetArg(xargs[n], XmNselectionPolicy, XmEXTENDED_SELECT); n++;
+ } else {
+ XtSetArg(xargs[n], XmNselectionPolicy, XmSINGLE_SELECT); n++;
+ }
+
+ char *name = args->name ? (char*)args->name : "listview";
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ Widget widget = XmCreateScrolledList(parent, name, xargs, n);
+ XtManageChild(widget);
+ ui_container_add(ctn, widget);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ UiListView *listview = malloc(sizeof(UiListView));
+ memset(listview, 0, sizeof(UiListView));
+ listview->obj = obj;
+ listview->widget = widget;
+ if(args->getvalue2) {
+ listview->getvalue = args->getvalue2;
+ listview->getvaluedata = args->getvalue2data;
+ } else if(args->getvalue) {
+ listview->getvalue = getvalue_wrapper;
+ listview->getvaluedata = args->getvalue;
+ } else {
+ listview->getvalue = getvalue_wrapper;
+ listview->getvaluedata = ui_strmodel_getvalue;
+ }
+ listview->var = var;
+ listview->onactivate = args->onactivate;
+ listview->onactivatedata = args->onactivatedata;
+ listview->onselection = args->onselection;
+ listview->onselectiondata = args->onselectiondata;
+
+ if(var) {
+ UiList *list = var->value;
+ list->obj = listview;
+ list->update = ui_listview_update;
+ list->getselection = ui_listview_getselection;
+ list->setselection = ui_listview_setselection;
+ ui_listview_update(list, 0);
+ }
+
+ XtAddCallback(
+ widget,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_listview_destroy,
+ listview);
+
+ XtAddCallback(
+ widget,
+ XmNdefaultActionCallback,
+ (XtCallbackProc)ui_listview_activate,
+ listview);
+ XtAddCallback(
+ widget,
+ XmNextendedSelectionCallback,
+ (XtCallbackProc)ui_listview_selection,
+ listview);
+ XtAddCallback(
+ widget,
+ XmNsingleSelectionCallback,
+ (XtCallbackProc)ui_listview_selection,
+ listview);
+
+ return widget;
+}
+
+void ui_listview_destroy(Widget w, UiListView *listview, XtPointer d) {
+ // TODO
+}
+
+static void list_callback(UiObject *obj, UiListSelection sel, ui_callback callback, void *userdata) {
+ UiEvent event;
+ event.obj = obj;
+ event.window = obj->window;
+ event.document = obj->ctx->document;
+ event.eventdata = &sel;
+ event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+ event.intval = sel.count > 0 ? sel.rows[0] : -1;
+ callback(&event, userdata);
+}
+
+static void listview_save_selection(UiListView *listview, XmListCallbackStruct *cb) {
+ UiListSelection sel = { cb->selected_item_count, NULL };
+ if(sel.count > 0) {
+ sel.rows = calloc(sel.count, sizeof(int));
+ for(int i=0;i<sel.count;i++) {
+ sel.rows[i] = cb->selected_item_positions[i]-1;
+ }
+ }
+ free(listview->current_selection.rows);
+ listview->current_selection = sel;
+}
+
+void ui_listview_activate(Widget w, UiListView *listview, XmListCallbackStruct *cb) {
+ listview_save_selection(listview, cb);
+ if(listview->onactivate) {
+ list_callback(listview->obj, listview->current_selection, listview->onactivate, listview->onactivatedata);
+ }
+}
+
+void ui_listview_selection(Widget w, UiListView *listview, XmListCallbackStruct *cb) {
+ listview_save_selection(listview, cb);
+ if(listview->onselection) {
+ list_callback(listview->obj, listview->current_selection, listview->onselection, listview->onselectiondata);
+ }
+}
+
+static XmStringTable create_stringlist(UiList *list, ui_getvaluefunc2 getvalue, void *getvaluedata, int *count) {
+ int num = list->count(list);
+ XmStringTable items = (XmStringTable)XtMalloc(num * sizeof(XmString));
+ void *data = list->first(list);
+ for(int i=0;i<num;i++) {
+ UiBool freevalue = FALSE;
+ char *s = getvalue(list, data, i, 0, getvaluedata, &freevalue);
+ items[i] = XmStringCreateLocalized(s ? s : "");
+ if(freevalue) {
+ free(s);
+ }
+ data = list->next(list);
+ }
+
+ *count = num;
+ return items;
+}
+
+void ui_listview_update(UiList *list, int i) {
+ UiListView *listview = list->obj;
+
+ int count;
+ XmStringTable items = create_stringlist(
+ list,
+ listview->getvalue,
+ listview->getvaluedata,
+ &count);
+
+ XtVaSetValues(
+ listview->widget,
+ XmNitems, count == 0 ? NULL : items,
+ XmNitemCount,
+ count,
+ NULL);
+
+ for (int i=0;i<count;i++) {
+ XmStringFree(items[i]);
+ }
+ XtFree((char *)items);
+}
+
+UiListSelection ui_listview_getselection(UiList *list) {
+ UiListView *listview = list->obj;
+ UiListSelection sel = { listview->current_selection.count, NULL };
+ if(sel.count > 0) {
+ sel.rows = calloc(sel.count, sizeof(int));
+ memcpy(sel.rows, listview->current_selection.rows, sel.count*sizeof(int));
+ }
+ return sel;
+}
+
+void ui_listview_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
+ UiListView *listview = list->obj;
+ XmListDeselectAllItems(listview->widget);
+ for(int i=0;i<selection.count;i++) {
+ XmListSelectPos(listview->widget, selection.rows[i]+1, False);
+ }
+ ui_setop_enable(FALSE);
+}
+
+void* ui_strmodel_getvalue(void *elm, int column) {
+ return column == 0 ? elm : NULL;
+}
+
+/* ------------------------------- Drop Down ------------------------------- */
+
+static void ui_dropdown_selection(
+ Widget w,
+ UiListView *listview,
+ XmComboBoxCallbackStruct *cb)
+{
+ int index = cb->item_position;
+ void *elm = NULL;
+ if(listview->var) {
+ UiList *list = listview->var->value;
+ elm = ui_list_get(list, index);
+ }
+
+ UiEvent event;
+ event.obj = listview->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = elm;
+ event.eventdatatype = UI_EVENT_DATA_LIST_ELM;
+ event.intval = index;
+ if(listview->onactivate) {
+ listview->onactivate(&event, listview->onactivatedata);
+ }
+ if(listview->onselection) {
+ listview->onselection(&event, listview->onselectiondata);
+ }
+}
+
+UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ char *name = args->name ? (char*)args->name : "dropdown";
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ Widget widget = XmCreateDropDownList(parent, name, xargs, n);
+ XtManageChild(widget);
+ ui_container_add(ctn, widget);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ UiListView *listview = malloc(sizeof(UiListView));
+ memset(listview, 0, sizeof(UiListView));
+ listview->obj = obj;
+ listview->widget = widget;
+ if(args->getvalue2) {
+ listview->getvalue = args->getvalue2;
+ listview->getvaluedata = args->getvalue2data;
+ } else if(args->getvalue) {
+ listview->getvalue = getvalue_wrapper;
+ listview->getvaluedata = args->getvalue;
+ } else {
+ listview->getvalue = getvalue_wrapper;
+ listview->getvaluedata = ui_strmodel_getvalue;
+ }
+ listview->var = var;
+ listview->onactivate = args->onactivate;
+ listview->onactivatedata = args->onactivatedata;
+ listview->onselection = args->onselection;
+ listview->onselectiondata = args->onselectiondata;
+
+ if(var) {
+ UiList *list = var->value;
+ list->obj = listview;
+ list->update = ui_listview_update;
+ list->getselection = ui_listview_getselection;
+ list->setselection = ui_listview_setselection;
+ ui_listview_update(list, 0);
+ }
+
+ XtAddCallback(
+ widget,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_listview_destroy,
+ listview);
+ XtAddCallback(
+ widget,
+ XmNselectionCallback,
+ (XtCallbackProc)ui_dropdown_selection,
+ listview);
+
+ return widget;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 LIST_H
+#define LIST_H
+
+#include "toolkit.h"
+#include "../ui/tree.h"
+#include "../common/context.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiListView {
+ UiObject *obj;
+ Widget widget;
+ UiVar *var;
+ UiModel* model;
+ ui_getvaluefunc2 getvalue;
+ void *getvaluedata;
+
+ UiListSelection current_selection;
+
+ ui_callback onactivate;
+ void* onactivatedata;
+ ui_callback onselection;
+ void* onselectiondata;
+ ui_callback ondragstart;
+ void* ondragstartdata;
+ ui_callback ondragcomplete;
+ void* ondragcompletedata;
+ ui_callback ondrop;
+ void* ondropsdata;
+ UiBool multiselection;
+} UiListView;
+
+void ui_listview_destroy(Widget w, UiListView *listview, XtPointer d);
+
+void ui_listview_activate(Widget w, UiListView *listview, XmListCallbackStruct *cb);
+void ui_listview_selection(Widget w, UiListView *listview, XmListCallbackStruct *cb);
+
+void ui_listview_update(UiList *list, int i);
+UiListSelection ui_listview_getselection(UiList *list);
+void ui_listview_setselection(UiList *list, UiListSelection selection);
+
+void* ui_strmodel_getvalue(void *elm, int column);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIST_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "menu.h"
+#include "button.h"
+#include "toolkit.h"
+#include "stock.h"
+#include "container.h"
+#include "../common/context.h"
+#include "../common/menu.h"
+#include "../common/types.h"
+#include "../ui/window.h"
+
+#include <cx/linked_list.h>
+#include <cx/array_list.h>
+
+
+static ui_menu_add_f createMenuItem[] = {
+ /* UI_MENU */ add_menu_widget,
+ /* UI_MENU_ITEM */ add_menuitem_widget,
+ /* UI_MENU_CHECK_ITEM */ add_checkitem_widget,
+ /* UI_MENU_RADIO_ITEM */ add_radioitem_widget,
+ /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_CHECKITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_RADIOITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_SEPARATOR */ add_menuseparator_widget
+};
+
+void ui_create_menubar(UiObject *obj, Widget window) {
+ UiMenu *menus_begin = uic_get_menu_list();
+ if(!menus_begin) {
+ return;
+ }
+
+ Widget menubar = XmCreateMenuBar(window, "menubar", NULL, 0);
+ XtManageChild(menubar);
+
+ UiMenu *ls = menus_begin;
+ while(ls) {
+ UiMenu *menu = ls;
+ add_menu_widget(menubar, 0, &menu->item, obj);
+ ls = (UiMenu*)ls->item.next;
+ }
+}
+
+void ui_add_menu_items(Widget 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(Widget parent, int i, UiMenuItemI *item, UiObject *obj) {
+ UiMenu *menu = (UiMenu*)item;
+ Arg args[4];
+ int n = 0;
+
+ XmString s = NULL;
+ if(menu->label) {
+ s = XmStringCreateLocalized((char*)menu->label);
+ XtSetArg(args[n], XmNlabelString, s); n++;
+ }
+
+ Widget submenu = XmVaCreateSimplePulldownMenu(parent, "menu_pulldown", i, NULL, NULL);
+ XtSetArg(args[n], XmNsubMenuId, submenu); n++;
+ Widget menuItem = XtCreateManagedWidget(
+ "menuitem",
+ xmCascadeButtonWidgetClass,
+ parent,
+ args,
+ n);
+
+
+ if(s) {
+ XmStringFree(s);
+ }
+
+ ui_add_menu_items(submenu, i, menu, obj);
+}
+
+void add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) {
+ UiMenuItem *it = (UiMenuItem*)item;
+
+ XmString s = NULL;
+ Arg args[4];
+ int n = 0;
+ if(it->label) {
+ s = XmStringCreateLocalized((char*)it->label);
+ XtSetArg(args[n], XmNlabelString, s); n++;
+ }
+
+ Widget mitem = XtCreateManagedWidget(
+ "menubutton",
+ xmPushButtonWidgetClass,
+ parent,
+ args,
+ n);
+ if(s) {
+ XmStringFree(s);
+ }
+
+ if(it->callback) {
+ UiEventData *eventdata = malloc(sizeof(UiEventData));
+ eventdata->callback = it->callback;
+ eventdata->userdata = it->userdata;
+ eventdata->obj = obj;
+ eventdata->value = 0;
+ XtAddCallback(
+ mitem,
+ XmNactivateCallback,
+ (XtCallbackProc)ui_push_button_callback,
+ eventdata);
+ XtAddCallback(
+ mitem,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_destroy_eventdata,
+ eventdata);
+ }
+
+ ui_set_widget_groups(obj->ctx, mitem, it->groups);
+}
+
+void add_menuseparator_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) {
+ Widget s = XmCreateSeparatorGadget (p, "menuseparator", NULL, 0);
+ XtManageChild(s);
+}
+
+void add_checkitem_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) {
+ UiMenuCheckItem *it = (UiMenuCheckItem*)item;
+
+ Arg args[4];
+ int n = 0;
+ XmString s = NULL;
+ if(it->label) {
+ s = XmStringCreateLocalized(it->label);
+ XtSetArg(args[n], XmNlabelString, s); n++;
+ }
+
+ //XtSetArg(args[n], XmNvisibleWhenOff, 0); n++;
+ Widget checkbox = XtCreateManagedWidget(
+ "menutogglebutton",
+ xmToggleButtonWidgetClass,
+ p,
+ args,
+ n);
+ if(s) {
+ XmStringFree(s);
+ }
+
+ ui_bind_togglebutton(obj, checkbox, it->varname, NULL, it->callback, it->userdata, 0);
+
+ ui_set_widget_groups(obj->ctx, checkbox, it->groups);
+}
+
+void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuRadioItem *it = (UiMenuRadioItem*)item;
+
+ Arg args[4];
+ int n = 0;
+ XmString s = NULL;
+ if(it->label) {
+ s = XmStringCreateLocalized(it->label);
+ XtSetArg(args[n], XmNlabelString, s); n++;
+ }
+ XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++;
+
+ Widget button = XmCreateToggleButton(p, "menuradiobutton", args, n);
+ XtManageChild(button);
+
+ ui_bind_radiobutton(obj, button, NULL, it->varname, it->callback, it->userdata, 0);
+}
+
+static void menuitem_list_remove_binding(void *obj) {
+ UiActiveMenuItemList *ls = obj;
+ UiList *list = ls->var->value;
+ CxList *bindings = list->obj;
+ if(bindings) {
+ (void)cxListFindRemove(bindings, obj);
+ if(cxListSize(bindings) == 0) {
+ cxListFree(bindings);
+ list->obj = NULL;
+ list->update = NULL;
+ }
+ }
+}
+
+void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) {
+ UiMenuItemList *il = (UiMenuItemList*)item;
+ const CxAllocator *a = obj->ctx->allocator;
+
+ UiActiveMenuItemList *ls = cxMalloc(
+ a,
+ sizeof(UiActiveMenuItemList));
+ ls->object = obj;
+ ls->menu = p;
+ ls->index = i;
+ ls->oldcount = 0;
+ ls->getvalue = il->getvalue;
+ ls->callback = il->callback;
+ ls->userdata = il->userdata;
+ ls->addseparator = il->addseparator;
+
+ ls->var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+ if(ls->var) {
+ UiList *list = ls->var->value;
+ list->update = ui_menulist_update;
+ list->getselection = NULL;
+ list->setselection = NULL;
+
+ // It is possible, that the UiVar is from a global shared context,
+ // used by multiple windows. To support this usecase, the list->obj
+ // binding object is a list of all connected UiActiveMenuItemList.
+ CxList *bindings = list->obj;
+ if(!bindings) {
+ bindings = cxLinkedListCreate(ls->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ list->obj = bindings;
+ }
+ cxListAdd(bindings, ls);
+
+ // The destruction of the toplevel obj must remove the menulist binding
+ uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls);
+
+ ui_update_menuitem_list(ls);
+ }
+}
+
+void ui_menulist_update(UiList *list, int ignored) {
+ CxList *bindings = list->obj;
+ CxIterator i = cxListIterator(bindings);
+ cx_foreach(UiActiveMenuItemList *, ls, i) {
+ ui_update_menuitem_list(ls);
+ }
+}
+
+void ui_update_menuitem_list(UiActiveMenuItemList *list) {
+ XmString s = NULL;
+ Arg args[4];
+ int n;
+
+ UiList *ls;
+ if(list->var && list->var->value) {
+ ls = list->var->value;
+ } else {
+ return;
+ }
+
+ if(list->oldcount > 0) {
+ Widget *children;
+ int nc;
+
+ XtVaGetValues(
+ list->menu,
+ XmNchildren,
+ &children,
+ XmNnumChildren,
+ &nc,
+ NULL);
+
+ for(int i=0;i<list->oldcount;i++) {
+ XtDestroyWidget(children[list->index + i]);
+ }
+ }
+
+ void* elm = ui_list_first(ls);
+ int i = 0;
+ if(elm && list->addseparator) {
+ XtSetArg(args[0], XmNpositionIndex, list->index);
+ Widget s = XmCreateSeparatorGadget(list->menu, "menuseparator", args, 1);
+ XtManageChild(s);
+ i++;
+ }
+
+ ui_getvaluefunc getvalue = list->getvalue;
+ int pos = list->index;
+ while(elm) {
+ n = 0;
+ char *label = (char*) (getvalue ? getvalue(elm, 0) : elm);
+ if(label) {
+ s = XmStringCreateLocalized(label);
+ XtSetArg(args[n], XmNlabelString, s); n++;
+ }
+ XtSetArg(args[n], XmNpositionIndex, pos+i); n++;
+
+ Widget mitem = XtCreateManagedWidget(
+ "menubutton",
+ xmPushButtonWidgetClass,
+ list->menu,
+ args,
+ n);
+ if(s) {
+ XmStringFree(s);
+ }
+
+ if(list->callback) {
+ UiEventData *eventdata = malloc(sizeof(UiEventData));
+ eventdata->callback = list->callback;
+ eventdata->userdata = list->userdata;
+ eventdata->obj = list->object;
+ eventdata->value = 0;
+ XtAddCallback(
+ mitem,
+ XmNactivateCallback,
+ (XtCallbackProc)ui_push_button_callback,
+ eventdata);
+ XtAddCallback(
+ mitem,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_destroy_eventdata,
+ eventdata);
+ }
+
+ elm = ui_list_next(ls);
+ i++;
+ }
+
+ list->oldcount = i;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 MENU_H
+#define MENU_H
+
+#include "../ui/menu.h"
+#include "../common/menu.h"
+#include "../common/context.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiActiveMenuItemList UiActiveMenuItemList;
+struct UiActiveMenuItemList {
+ UiObject *object;
+ Widget menu;
+ int index;
+ int oldcount;
+ UiVar *var;
+ ui_getvaluefunc getvalue;
+ ui_callback callback;
+ void *userdata;
+ bool addseparator;
+};
+
+typedef void(*ui_menu_add_f)(Widget, int, UiMenuItemI*, UiObject*);
+
+void ui_create_menubar(UiObject *obj, Widget window);
+void ui_add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
+
+void add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuitem_st_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuseparator_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj);
+void add_checkitem_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj);
+void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj);
+void add_checkitemnv_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj);
+
+void ui_menulist_update(UiList *list, int ignored);
+void ui_update_menuitem_list(UiActiveMenuItemList *list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MENU_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+MOTIF_SRC_DIR = ui/motif/
+MOTIF_OBJPRE = $(OBJ_DIR)$(MOTIF_SRC_DIR)
+
+MOTIFOBJ = toolkit.o
+MOTIFOBJ += stock.o
+MOTIFOBJ += window.o
+MOTIFOBJ += widget.o
+MOTIFOBJ += container.o
+MOTIFOBJ += menu.o
+MOTIFOBJ += toolbar.o
+MOTIFOBJ += button.o
+MOTIFOBJ += label.o
+MOTIFOBJ += text.o
+MOTIFOBJ += list.o
+MOTIFOBJ += graphics.o
+MOTIFOBJ += range.o
+MOTIFOBJ += dnd.o
+MOTIFOBJ += image.o
+MOTIFOBJ += Grid.o
+
+TOOLKITOBJS += $(MOTIFOBJ:%=$(MOTIF_OBJPRE)%)
+TOOLKITSOURCE += $(MOTIFOBJ:%.o=motif/%.c)
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2016 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "range.h"
+#include "container.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2016 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 RANGE_H
+#define RANGE_H
+
+#include "toolkit.h"
+#include "../ui/range.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RANGE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "stock.h"
+#include "../ui/properties.h"
+#include <cx/hash_map.h>
+
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 STOCK_H
+#define STOCK_H
+
+#include "../ui/stock.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STOCK_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "text.h"
+#include "container.h"
+
+#include <cx/string.h>
+
+
+/* ------------------------------ Text Area ------------------------------ */
+
+UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ XtSetArg(xargs[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ char *name = args->name ? (char*)args->name : "textarea";
+ XtSetArg(xargs[n], XmNwidth, 100); n++;
+ Widget widget = XmCreateScrolledText(parent, name, xargs, n);
+ XtManageChild(widget);
+ ui_container_add(ctn, widget);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_TEXT);
+
+ UiTextArea *textarea = malloc(sizeof(UiTextArea));
+ memset(textarea, 0, sizeof(UiTextArea));
+ textarea->obj = obj;
+ textarea->var = var;
+
+ if(var) {
+ UiText *value = var->value;
+ if(value->value.ptr) {
+ XmTextSetString(widget, value->value.ptr);
+ value->value.free(value->value.ptr);
+ value->value.ptr = NULL;
+ }
+
+ value->save = ui_textarea_save;
+ value->restore = ui_textarea_restore;
+ value->destroy = ui_textarea_text_destroy;
+ value->set = ui_textarea_set;
+ value->get = ui_textarea_get;
+ value->getsubstr = ui_textarea_getsubstr;
+ value->insert = ui_textarea_insert;
+ value->setposition = ui_textarea_setposition;
+ value->position = ui_textarea_position;
+ value->selection = ui_textarea_selection;
+ value->length = ui_textarea_length;
+ value->value.ptr = NULL;
+ value->obj = widget;
+
+ if(!value->data2) {
+ value->data2 = ui_create_undomgr();
+ }
+
+ XtAddCallback(
+ widget,
+ XmNmodifyVerifyCallback,
+ (XtCallbackProc)ui_text_modify_callback,
+ var);
+ }
+
+ return widget;
+}
+
+char* ui_textarea_get(UiText *text) {
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+ char *str = XmTextGetString(text->obj);
+ text->value.ptr = str;
+ text->value.free = (ui_freefunc)XtFree;
+ return str;
+}
+
+void ui_textarea_save(UiText *text) {
+ (void)ui_textarea_get(text);
+}
+
+void ui_textarea_restore(UiText *text) {
+ if(text->value.ptr) {
+ ui_textarea_set(text, text->value.ptr);
+ }
+}
+
+void ui_textarea_text_destroy(UiText *text) {
+ if(text->value.free) {
+ text->value.free(text->value.ptr);
+ }
+ if(text->data2) {
+ ui_destroy_undomgr(text->data2);
+ }
+}
+
+void ui_textarea_set(UiText *text, const char *str) {
+ XmTextSetString(text->obj, (char*)str);
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+ text->value.ptr = NULL;
+}
+
+char* ui_textarea_getsubstr(UiText *text, int begin, int end) {
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+ int length = end - begin;
+ char *str = XtMalloc(length + 1);
+ XmTextGetSubstring(text->obj, begin, length, length + 1, str);
+ text->value.ptr = str;
+ text->value.free = (ui_freefunc)XtFree;
+ return str;
+}
+
+void ui_textarea_insert(UiText *text, int pos, char *str) {
+ text->value.ptr = NULL;
+ XmTextInsert(text->obj, pos, str);
+ if(text->value.ptr) {
+ text->value.free(text->value.ptr);
+ }
+}
+
+void ui_textarea_setposition(UiText *text, int pos) {
+ XmTextSetInsertionPosition(text->obj, pos);
+}
+
+int ui_textarea_position(UiText *text) {
+ long begin;
+ long end;
+ XmTextGetSelectionPosition(text->obj, &begin, &end);
+ text->pos = begin;
+ return text->pos;
+}
+
+void ui_textarea_selection(UiText *text, int *begin, int *end) {
+ XmTextGetSelectionPosition(text->obj, (long*)begin, (long*)end);
+}
+
+int ui_textarea_length(UiText *text) {
+ return (int)XmTextGetLastPosition(text->obj);
+}
+
+
+
+UiUndoMgr* ui_create_undomgr() {
+ UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr));
+ mgr->begin = NULL;
+ mgr->end = NULL;
+ mgr->cur = NULL;
+ mgr->length = 0;
+ mgr->event = 1;
+ return mgr;
+}
+
+void ui_destroy_undomgr(UiUndoMgr *mgr) {
+ UiTextBufOp *op = mgr->begin;
+ while(op) {
+ UiTextBufOp *nextOp = op->next;
+ if(op->text) {
+ free(op->text);
+ }
+ free(op);
+ op = nextOp;
+ }
+ free(mgr);
+}
+
+void ui_text_selection_callback(
+ Widget widget,
+ UiTextArea *textarea,
+ XtPointer data)
+{
+ long left = 0;
+ long right = 0;
+ XmTextGetSelectionPosition(widget, &left, &right);
+ int sel = left < right ? 1 : 0;
+ if(sel != textarea->last_selection_state) {
+ if(sel) {
+ ui_set_group(textarea->obj->ctx, UI_GROUP_SELECTION);
+ } else {
+ ui_unset_group(textarea->obj->ctx, UI_GROUP_SELECTION);
+ }
+ }
+ textarea->last_selection_state = sel;
+}
+
+void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) {
+ UiText *value = var->value;
+ if(!value->obj) {
+ // TODO: bug, fix
+ return;
+ }
+ if(!value->data2) {
+ value->data2 = ui_create_undomgr();
+ }
+
+ XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data;
+ int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE;
+ UiUndoMgr *mgr = value->data2;
+ if(!mgr->event) {
+ return;
+ }
+
+ char *text = txv->text->ptr;
+ int length = txv->text->length;
+
+ if(mgr->cur) {
+ UiTextBufOp *elm = mgr->cur->next;
+ if(elm) {
+ mgr->cur->next = NULL;
+ mgr->end = mgr->cur;
+ while(elm) {
+ elm->prev = NULL;
+ UiTextBufOp *next = elm->next;
+ ui_free_textbuf_op(elm);
+ elm = next;
+ }
+ }
+
+ UiTextBufOp *last_op = mgr->cur;
+ if(
+ last_op->type == UI_TEXTBUF_INSERT &&
+ ui_check_insertstr(last_op->text, last_op->len, text, length) == 0)
+ {
+ // append text to last op
+ int ln = last_op->len;
+ char *newtext = malloc(ln + length + 1);
+ memcpy(newtext, last_op->text, ln);
+ memcpy(newtext+ln, text, length);
+ newtext[ln+length] = '\0';
+
+ last_op->text = newtext;
+ last_op->len = ln + length;
+ last_op->end += length;
+
+ return;
+ }
+ }
+
+ char *str;
+ if(type == UI_TEXTBUF_INSERT) {
+ str = malloc(length + 1);
+ memcpy(str, text, length);
+ str[length] = 0;
+ } else {
+ length = txv->endPos - txv->startPos;
+ str = malloc(length + 1);
+ XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str);
+ }
+
+ UiTextBufOp *op = malloc(sizeof(UiTextBufOp));
+ op->prev = NULL;
+ op->next = NULL;
+ op->type = type;
+ op->start = txv->startPos;
+ op->end = txv->endPos + 1;
+ op->len = length;
+ op->text = str;
+
+ cx_linked_list_add(
+ (void**)&mgr->begin,
+ (void**)&mgr->end,
+ offsetof(UiTextBufOp, prev),
+ offsetof(UiTextBufOp, next),
+ op);
+
+ mgr->cur = op;
+}
+
+int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) {
+ // return 1 if oldstr + newstr are one word
+
+ int has_space = 0;
+ for(int i=0;i<oldlen;i++) {
+ if(oldstr[i] < 33) {
+ has_space = 1;
+ break;
+ }
+ }
+
+ for(int i=0;i<newlen;i++) {
+ if(has_space && newstr[i] > 32) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void ui_free_textbuf_op(UiTextBufOp *op) {
+ if(op->text) {
+ free(op->text);
+ }
+ free(op);
+}
+
+
+void ui_text_undo(UiText *value) {
+ UiUndoMgr *mgr = value->data2;
+
+ if(mgr->cur) {
+ UiTextBufOp *op = mgr->cur;
+ mgr->event = 0;
+ switch(op->type) {
+ case UI_TEXTBUF_INSERT: {
+ XmTextReplace(value->obj, op->start, op->end, "");
+ break;
+ }
+ case UI_TEXTBUF_DELETE: {
+ XmTextInsert(value->obj, op->start, op->text);
+ break;
+ }
+ }
+ mgr->event = 1;
+ mgr->cur = mgr->cur->prev;
+ }
+}
+
+void ui_text_redo(UiText *value) {
+ UiUndoMgr *mgr = value->data2;
+
+ UiTextBufOp *elm = NULL;
+ if(mgr->cur) {
+ if(mgr->cur->next) {
+ elm = mgr->cur->next;
+ }
+ } else if(mgr->begin) {
+ elm = mgr->begin;
+ }
+
+ if(elm) {
+ UiTextBufOp *op = elm;
+ mgr->event = 0;
+ switch(op->type) {
+ case UI_TEXTBUF_INSERT: {
+ XmTextInsert(value->obj, op->start, op->text);
+ break;
+ }
+ case UI_TEXTBUF_DELETE: {
+ XmTextReplace(value->obj, op->start, op->end, "");
+ break;
+ }
+ }
+ mgr->event = 1;
+ mgr->cur = elm;
+ }
+}
+
+
+
+/* ------------------------------ Text Field ------------------------------ */
+
+static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, int frameless, int password) {
+ Arg xargs[16];
+ int n = 0;
+
+ if(frameless) {
+ XtSetArg(xargs[n], XmNshadowThickness, 0);
+ n++;
+ }
+ if(password) {
+ // TODO
+ }
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ char *name = args->name ? (char*)args->name : "textfield";
+ Widget textfield = XmCreateTextField(parent, name, xargs, n);
+ XtManageChild(textfield);
+ ui_container_add(ctn, textfield);
+
+ ui_set_widget_groups(obj->ctx, textfield, args->groups);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiString *value = (UiString*)var->value;
+ value->obj = textfield;
+ value->get = ui_textfield_get;
+ value->set = ui_textfield_set;
+
+ if(value->value.ptr) {
+ ui_textfield_set(value, value->value.ptr);
+ }
+ }
+
+ return textfield;
+}
+
+UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) {
+ return create_textfield(obj, args, FALSE, FALSE);
+}
+
+UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs *args) {
+ return create_textfield(obj, args, TRUE, FALSE);
+}
+
+UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs *args) {
+ return create_textfield(obj, args, FALSE, FALSE);
+}
+
+char* ui_textfield_get(UiString *str) {
+ if(str->value.free) {
+ str->value.free(str->value.ptr);
+ }
+ char *value = XmTextFieldGetString(str->obj);
+ str->value.ptr = value;
+ str->value.free = (ui_freefunc)XtFree;
+ return value;
+}
+
+void ui_textfield_set(UiString *str, const char *value) {
+ XmTextFieldSetString(str->obj, (void*)value);
+ if(str->value.free) {
+ str->value.free(str->value.ptr);
+ }
+ str->value.ptr = NULL;
+}
+
+
+
+
+
+/* -------------------- path bar -------------------- */
+
+#define XNECreateText(parent,name,args,count) XmCreateTextField(parent,name,args,count)
+#define XNETextSetString(widget,value) XmTextFieldSetString(widget,value)
+#define XNETextGetString(widget) XmTextFieldGetString(widget)
+#define XNETextGetLastPosition(widget) XmTextFieldGetLastPosition(widget)
+#define XNETextSetInsertionPosition(widget, i) XmTextFieldSetInsertionPosition(widget, i)
+#define XNETextSetSelection(w, f, l, t) XmTextFieldSetSelection(w, f, l, t)
+
+typedef void(*updatedir_callback)(void*,char*,int);
+
+typedef struct PathBar {
+ Widget widget;
+ Widget textfield;
+
+ Widget focus_widget;
+
+ Widget left;
+ Widget right;
+ Dimension lw;
+ Dimension rw;
+
+ int shift;
+
+ UiPathElm *current_pathelms;
+ Widget *pathSegments;
+ size_t numSegments;
+ size_t segmentAlloc;
+
+ char *path;
+ int selection;
+ Boolean input;
+
+ int focus;
+
+ updatedir_callback updateDir;
+ void *updateDirData;
+
+ ui_pathelm_func getpathelm;
+ void *getpathelmdata;
+} PathBar;
+
+void PathBarSetPath(PathBar *bar, const char *path);
+
+void pathbar_resize(Widget w, PathBar *p, XtPointer d)
+{
+ Dimension width, height;
+ XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL);
+
+ Dimension *segW = (void*)XtCalloc(p->numSegments, sizeof(Dimension));
+
+ Dimension maxHeight = 0;
+
+ /* get width/height from all widgets */
+ Dimension pathWidth = 0;
+ for(int i=0;i<p->numSegments;i++) {
+ Dimension segWidth;
+ Dimension segHeight;
+ XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL);
+ segW[i] = segWidth;
+ pathWidth += segWidth;
+ if(segHeight > maxHeight) {
+ maxHeight = segHeight;
+ }
+ }
+ Dimension tfHeight;
+ XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL);
+ if(tfHeight > maxHeight) {
+ maxHeight = tfHeight;
+ }
+
+ Boolean arrows = False;
+ if(pathWidth + 10 > width) {
+ arrows = True;
+ pathWidth += p->lw + p->rw;
+ }
+
+ /* calc max visible widgets */
+ int start = 0;
+ if(arrows) {
+ Dimension vis = p->lw+p->rw;
+ for(int i=p->numSegments;i>0;i--) {
+ Dimension segWidth = segW[i-1];
+ if(vis + segWidth + 10 > width) {
+ start = i;
+ arrows = True;
+ break;
+ }
+ vis += segWidth;
+ }
+ } else {
+ p->shift = 0;
+ }
+
+ int leftShift = 0;
+ if(p->shift < 0) {
+ if(start + p->shift < 0) {
+ leftShift = start;
+ start = 0;
+ p->shift = -leftShift;
+ } else {
+ leftShift = -p->shift; /* negative shift */
+ start += p->shift;
+ }
+ }
+
+ int x = 0;
+ if(arrows) {
+ XtManageChild(p->left);
+ XtManageChild(p->right);
+ x = p->lw;
+ } else {
+ XtUnmanageChild(p->left);
+ XtUnmanageChild(p->right);
+ }
+
+ for(int i=0;i<p->numSegments;i++) {
+ if(i >= start && i < p->numSegments - leftShift && !p->input) {
+ XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL);
+ x += segW[i];
+ XtManageChild(p->pathSegments[i]);
+ } else {
+ XtUnmanageChild(p->pathSegments[i]);
+ }
+ }
+
+ if(arrows) {
+ XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL);
+ XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL);
+ }
+
+ free(segW);
+
+ Dimension rw, rh;
+ XtMakeResizeRequest(w, width, maxHeight, &rw, &rh);
+
+ XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL);
+}
+
+static void pathbarActivateTF(PathBar *p)
+{
+ XtUnmanageChild(p->left);
+ XtUnmanageChild(p->right);
+ XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0);
+ XtManageChild(p->textfield);
+ p->input = 1;
+
+ XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT);
+
+ pathbar_resize(p->widget, p, NULL);
+}
+
+void PathBarActivateTextfield(PathBar *p)
+{
+ p->focus = 1;
+ pathbarActivateTF(p);
+}
+
+void pathbar_input(Widget w, PathBar *p, XtPointer c)
+{
+ XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c;
+ XEvent *xevent = cbs->event;
+
+ if (cbs->reason == XmCR_INPUT) {
+ if (xevent->xany.type == ButtonPress) {
+ p->focus = 0;
+ pathbarActivateTF(p);
+ }
+ }
+}
+
+void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c)
+{
+ if(--p->focus < 0) {
+ p->input = False;
+ XtUnmanageChild(p->textfield);
+ }
+}
+
+static cxmutstr concat_path_s(cxstring base, cxstring path) {
+ if(!path.ptr) {
+ path = CX_STR("");
+ }
+
+ int add_separator = 0;
+ if(base.length != 0 && base.ptr[base.length-1] == '/') {
+ if(path.ptr[0] == '/') {
+ base.length--;
+ }
+ } else {
+ if(path.length == 0 || path.ptr[0] != '/') {
+ add_separator = 1;
+ }
+ }
+
+ cxmutstr url;
+ if(add_separator) {
+ url = cx_strcat(3, base, CX_STR("/"), path);
+ } else {
+ url = cx_strcat(2, base, path);
+ }
+
+ return url;
+}
+
+static char* ConcatPath(const char *path1, const char *path2) {
+ return concat_path_s(cx_str(path1), cx_str(path2)).ptr;
+}
+
+void pathbar_pathinput(Widget w, PathBar *p, XtPointer d)
+{
+ char *newpath = XNETextGetString(p->textfield);
+ if(newpath) {
+ if(newpath[0] == '~') {
+ char *p = newpath+1;
+ char *home = getenv("HOME");
+ char *cp = ConcatPath(home, p);
+ XtFree(newpath);
+ newpath = cp;
+ } else if(newpath[0] != '/') {
+ char curdir[2048];
+ curdir[0] = 0;
+ getcwd(curdir, 2048);
+ char *cp = ConcatPath(curdir, newpath);
+ XtFree(newpath);
+ newpath = cp;
+ }
+
+ /* update path */
+ PathBarSetPath(p, newpath);
+ if(p->updateDir) {
+ p->updateDir(p->updateDirData, newpath, -1);
+ }
+ XtFree(newpath);
+
+ /* hide textfield and show path as buttons */
+ XtUnmanageChild(p->textfield);
+ pathbar_resize(p->widget, p, NULL);
+
+ if(p->focus_widget) {
+ XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT);
+ }
+ }
+}
+
+void pathbar_shift_left(Widget w, PathBar *p, XtPointer d)
+{
+ p->shift--;
+ pathbar_resize(p->widget, p, NULL);
+}
+
+void pathbar_shift_right(Widget w, PathBar *p, XtPointer d)
+{
+ if(p->shift < 0) {
+ p->shift++;
+ }
+ pathbar_resize(p->widget, p, NULL);
+}
+
+static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
+ PathBar *pb = data;
+ if(event->type == KeyReleaseMask) {
+ if(event->xkey.keycode == 9) {
+ XtUnmanageChild(pb->textfield);
+ pathbar_resize(pb->widget, pb, NULL);
+ *dispatch = False;
+ } else if(event->xkey.keycode == 36) {
+ pathbar_pathinput(pb->textfield, pb, NULL);
+ *dispatch = False;
+ }
+ }
+}
+
+PathBar* CreatePathBar(Widget parent, ArgList args, int n)
+{
+ PathBar *bar = (PathBar*)XtMalloc(sizeof(PathBar));
+ bar->path = NULL;
+ bar->updateDir = NULL;
+ bar->updateDirData = NULL;
+
+ bar->focus_widget = NULL;
+
+ bar->getpathelm = NULL;
+ bar->getpathelmdata = NULL;
+ bar->current_pathelms = NULL;
+
+ bar->shift = 0;
+
+ XtSetArg(args[n], XmNmarginWidth, 0); n++;
+ XtSetArg(args[n], XmNmarginHeight, 0); n++;
+ bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n);
+ XtAddCallback(
+ bar->widget,
+ XmNresizeCallback,
+ (XtCallbackProc)pathbar_resize,
+ bar);
+ XtAddCallback(
+ bar->widget,
+ XmNinputCallback,
+ (XtCallbackProc)pathbar_input,
+ bar);
+
+ Arg a[4];
+ XtSetArg(a[0], XmNshadowThickness, 0);
+ XtSetArg(a[1], XmNx, 0);
+ XtSetArg(a[2], XmNy, 0);
+ bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3);
+ bar->input = 0;
+ XtAddCallback(
+ bar->textfield,
+ XmNlosingFocusCallback,
+ (XtCallbackProc)pathbar_losingfocus,
+ bar);
+ XtAddCallback(bar->textfield, XmNactivateCallback,
+ (XtCallbackProc)pathbar_pathinput, bar);
+ XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar);
+
+ XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT);
+ bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
+ XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT);
+ bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
+ XtAddCallback(
+ bar->left,
+ XmNactivateCallback,
+ (XtCallbackProc)pathbar_shift_left,
+ bar);
+ XtAddCallback(
+ bar->right,
+ XmNactivateCallback,
+ (XtCallbackProc)pathbar_shift_right,
+ bar);
+
+ Pixel bg;
+ XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL);
+ XtVaSetValues(bar->widget, XmNbackground, bg, NULL);
+
+ XtManageChild(bar->left);
+ XtManageChild(bar->right);
+
+ XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL);
+ XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL);
+
+ bar->segmentAlloc = 16;
+ bar->numSegments = 0;
+ bar->pathSegments = (Widget*)XtCalloc(16, sizeof(Widget));
+
+ bar->selection = 0;
+
+ return bar;
+}
+
+void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c)
+{
+ XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False);
+
+ int i;
+ for(i=0;i<bar->numSegments;i++) {
+ if(bar->pathSegments[i] == w) {
+ bar->selection = i;
+ XmToggleButtonSetState(w, True, False);
+ break;
+ }
+ }
+
+ UiPathElm elm = bar->current_pathelms[i];
+ cxmutstr path = cx_strdup(cx_strn(elm.path, elm.path_len));
+ if(bar->updateDir) {
+ XNETextSetString(bar->textfield, path.ptr);
+ bar->updateDir(bar->updateDirData, path.ptr, i);
+ }
+ free(path.ptr);
+}
+
+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);
+}
+
+void PathBarSetPath(PathBar *bar, const char *path)
+{
+ if(bar->path) {
+ free(bar->path);
+ }
+ bar->path = strdup(path);
+
+ for(int i=0;i<bar->numSegments;i++) {
+ XtDestroyWidget(bar->pathSegments[i]);
+ }
+ XtUnmanageChild(bar->textfield);
+ XtManageChild(bar->left);
+ XtManageChild(bar->right);
+ bar->input = False;
+
+ Arg args[4];
+ XmString str;
+
+ bar->numSegments = 0;
+
+ ui_pathelm_destroy(bar->current_pathelms, bar->numSegments);
+ size_t nelm = 0;
+ UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata);
+ if (!path_elm) {
+ return;
+ }
+ bar->current_pathelms = path_elm;
+ bar->numSegments = nelm;
+ bar->pathSegments = realloc(bar->pathSegments, nelm * sizeof(Widget*));
+
+ for(int i=0;i<nelm;i++) {
+ UiPathElm elm = path_elm[i];
+
+ cxmutstr name = cx_strdup(cx_strn(elm.name, elm.name_len));
+ str = XmStringCreateLocalized(elm.name);
+ free(name.ptr);
+
+ XtSetArg(args[0], XmNlabelString, str);
+ XtSetArg(args[1], XmNfillOnSelect, True);
+ XtSetArg(args[2], XmNindicatorOn, False);
+ Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3);
+ XtAddCallback(
+ button,
+ XmNvalueChangedCallback,
+ (XtCallbackProc)PathBarChangeDir,
+ bar);
+ XmStringFree(str);
+
+ bar->pathSegments[i] = button;
+ }
+
+ bar->selection = bar->numSegments-1;
+ XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False);
+
+ XNETextSetString(bar->textfield, (char*)path);
+ XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield));
+
+ pathbar_resize(bar->widget, bar, NULL);
+}
+
+void PathBarDestroy(PathBar *pathbar) {
+ if(pathbar->path) {
+ XtFree(pathbar->path);
+ }
+ XtFree((void*)pathbar->pathSegments);
+ XtFree((void*)pathbar);
+}
+
+
+/* ---------------------------- Path Text Field ---------------------------- */
+
+static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) {
+ PathBar *pathbar = (PathBar*)data;
+ // TODO: check if there is somonething missing
+ XtFree((void*)pathbar->pathSegments);
+ XtFree((void*)pathbar);
+}
+
+// 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 void pathbar_activate(void *data, char *path, int index) {
+ UiEventData *event = data;
+ UiEvent evt;
+ evt.obj = event->obj;
+ evt.window = evt.obj->window;
+ evt.document = evt.obj->ctx->document;
+ evt.eventdata = path;
+ evt.eventdatatype = UI_EVENT_DATA_STRING;
+ evt.intval = index;
+ event->callback(&evt, event->userdata);
+}
+
+UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
+ Arg xargs[16];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ // TODO: name
+
+
+ PathBar *pathbar = CreatePathBar(parent, xargs, n);
+ if(!args->getpathelm) {
+ pathbar->getpathelm= default_pathelm_func;
+ } else {
+ pathbar->getpathelm = args->getpathelm;
+ pathbar->getpathelmdata = args->getpathelmdata;
+ }
+
+
+ XtManageChild(pathbar->widget);
+ ui_container_add(ctn, pathbar->widget);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if (var) {
+ UiString* value = (UiString*)var->value;
+ value->obj = pathbar;
+ 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);
+ }
+ }
+
+ if(args->onactivate) {
+ UiEventData *eventdata = malloc(sizeof(UiEventData));
+ eventdata->callback = args->onactivate;
+ eventdata->userdata = args->onactivatedata;
+ eventdata->obj = obj;
+ eventdata->value = 0;
+
+ pathbar->updateDir = pathbar_activate;
+ pathbar->updateDirData = eventdata;
+
+ XtAddCallback(
+ pathbar->widget,
+ XmNdestroyCallback,
+ (XtCallbackProc)ui_destroy_eventdata,
+ eventdata);
+ }
+
+ XtAddCallback(
+ pathbar->widget,
+ XmNdestroyCallback,
+ (XtCallbackProc)destroy_pathbar,
+ pathbar);
+
+ return pathbar->widget;
+}
+
+char* ui_path_textfield_get(UiString *str) {
+ PathBar *pathbar = str->obj;
+ str->value.free(str->value.ptr);
+ char *value = XmTextFieldGetString(pathbar->textfield);
+ str->value.ptr = value;
+ str->value.free = (ui_freefunc)XtFree;
+ return value;
+}
+
+void ui_path_textfield_set(UiString *str, const char *value) {
+ PathBarSetPath(str->obj, value);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TEXT_H
+#define TEXT_H
+
+#include "../ui/text.h"
+#include "toolkit.h"
+#include <cx/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_TEXTBUF_INSERT 0
+#define UI_TEXTBUF_DELETE 1
+typedef struct UiTextBufOp UiTextBufOp;
+struct UiTextBufOp {
+ UiTextBufOp *prev;
+ UiTextBufOp *next;
+ int type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE
+ int start;
+ int end;
+ int len;
+ char *text;
+};
+
+typedef struct UiUndoMgr {
+ UiTextBufOp *begin;
+ UiTextBufOp *end;
+ UiTextBufOp *cur;
+ int length;
+ int event;
+} UiUndoMgr;
+
+typedef struct UiTextArea {
+ UiObject *obj;
+ UiVar *var;
+ int last_selection_state;
+} UiTextArea;
+
+void ui_textarea_save(UiText *text);
+void ui_textarea_restore(UiText *text);
+void ui_textarea_text_destroy(UiText *text);
+char* ui_textarea_get(UiText *text);
+void ui_textarea_set(UiText *text, const char *str);
+char* ui_textarea_getsubstr(UiText *text, int begin, int end);
+void ui_textarea_insert(UiText *text, int pos, char *str);
+void ui_textarea_setposition(UiText *text, int pos);
+int ui_textarea_position(UiText *text);
+void ui_textarea_selection(UiText *text, int *begin, int *end);
+int ui_textarea_length(UiText *text);
+
+UiUndoMgr* ui_create_undomgr();
+void ui_destroy_undomgr(UiUndoMgr *mgr);
+void ui_text_selection_callback(
+ Widget widget,
+ UiTextArea *textarea,
+ XtPointer data);
+void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data);
+int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen);
+void ui_free_textbuf_op(UiTextBufOp *op);
+
+char* ui_textfield_get(UiString *str);
+void ui_textfield_set(UiString *str, const char *value);
+
+char* ui_path_textfield_get(UiString *str);
+void ui_path_textfield_set(UiString *str, const char *value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TEXT_H */
+
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "toolbar.h"
+#include "button.h"
+#include "stock.h"
+#include "list.h"
+
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
+#include <cx/array_list.h>
+
+#include "../common/context.h"
+
--- /dev/null
+/*
+ * 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 TOOLBAR_H
+#define TOOLBAR_H
+
+#include "../ui/toolbar.h"
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TOOLBAR_H */
+
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include "toolkit.h"
+#include "toolbar.h"
+#include "container.h"
+#include "stock.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
+#include "../common/document.h"
+#include "../common/properties.h"
+#include <cx/buffer.h>
+
+#include <X11/Intrinsic.h>
+#include <Xm/CutPaste.h>
+
+static XtAppContext app;
+static Display *display;
+static Widget active_window;
+static const char *application_name;
+
+static ui_callback startup_func;
+static void *startup_data;
+static ui_callback open_func;
+static void *open_data;
+static ui_callback exit_func;
+static void *exit_data;
+
+static ui_callback appclose_fnc;
+static void *appclose_udata;
+
+static int is_toplevel_realized = 0;
+
+static int event_pipe[2];
+
+static UiBool exit_on_shutdown;
+
+static String fallback[] = {
+ //"*fontList: -dt-interface system-medium-r-normal-s*utf*:",
+ "*renderTable: rt",
+ "*rt*fontType: FONT_IS_XFT",
+ "*rt*fontName: Sans",
+ "*rt*fontSize: 11",
+
+ "*progresss_spinner*renderTable*fontType: FONT_IS_FONT",
+ "*progresss_spinner*renderTable*fontName: Cursor",
+
+ "*window_frame.shadowType: SHADOW_ETCHED_OUT",
+ "*window_frame.shadowThickness: 1",
+ "*togglebutton.shadowThickness: 1",
+ "*togglebutton.highlightThickness: 2",
+
+ "*ui_test.background: red",
+ NULL
+};
+
+void input_proc(XtPointer data, int *source, XtInputId *iid) {
+ void *ptr;
+ read(event_pipe[0], &ptr, sizeof(void*));
+}
+
+void ui_init(const char *appname, int argc, char **argv) {
+ application_name = appname;
+ uic_init_global_context();
+
+ XtToolkitInitialize();
+ XtSetLanguageProc(NULL, NULL, NULL);
+ app = XtCreateApplicationContext();
+ XtAppSetFallbackResources(app, fallback);
+
+ display = XtOpenDisplay(app, NULL, appname, appname, NULL, 0, &argc, argv);
+
+ uic_menu_init();
+ uic_toolbar_init();
+ uic_load_app_properties();
+
+ if(pipe(event_pipe)) {
+ fprintf(stderr, "UiError: Cannot create event pipe\n");
+ exit(-1);
+ }
+ XtAppAddInput(
+ app,
+ event_pipe[0],
+ (XtPointer)XtInputReadMask,
+ input_proc,
+ NULL);
+}
+
+const char* ui_appname() {
+ return application_name;
+}
+
+Display* ui_motif_get_display() {
+ return display;
+}
+
+void ui_onstartup(ui_callback f, void *userdata) {
+ startup_func = f;
+ startup_data = userdata;
+}
+
+void ui_onopen(ui_callback f, void *userdata) {
+ open_func = f;
+ open_data = userdata;
+}
+
+void ui_onexit(ui_callback f, void *userdata) {
+ exit_func = f;
+ exit_data = userdata;
+}
+
+void ui_app_exit_on_shutdown(UiBool exitapp) {
+ exit_on_shutdown = exitapp;
+}
+
+void ui_main() {
+ if(startup_func) {
+ startup_func(NULL, startup_data);
+ }
+ XtAppMainLoop(app);
+ if(exit_func) {
+ exit_func(NULL, exit_data);
+ }
+ uic_store_app_properties();
+ if(exit_on_shutdown) {
+ exit(0);
+ }
+}
+
+void ui_exit_mainloop() {
+ XtAppSetExitFlag(app);
+}
+
+void ui_secondary_event_loop(int *loop) {
+ while(*loop && !XtAppGetExitFlag(app)) {
+ XEvent event;
+ XtAppNextEvent(app, &event);
+ XtDispatchEvent(&event);
+ }
+}
+
+void ui_show(UiObject *obj) {
+ uic_check_group_widgets(obj->ctx);
+ if(!XtIsRealized(obj->widget)) {
+ XtRealizeWidget(obj->widget);
+ obj->ref++;
+ }
+}
+
+void ui_close(UiObject *obj) {
+
+}
+
+
+void ui_set_enabled(UIWIDGET widget, int enabled) {
+ XtSetSensitive(widget, enabled);
+}
+
+void ui_set_show_all(UIWIDGET widget, int value) {
+ if(!value) {
+ XtUnmanageChild(widget);
+ }
+}
+
+void ui_set_visible(UIWIDGET widget, int visible) {
+ if(visible) {
+ XtManageChild(widget);
+ } else {
+ XtUnmanageChild(widget);
+ }
+}
+
+static Boolean ui_job_finished(void *data) {
+ UiJob *job = data;
+ if(job->finish_callback) {
+ UiEvent event;
+ event.obj = job->obj;
+ event.window = job->obj->window;
+ event.document = job->obj->ctx->document;
+ event.intval = 0;
+ event.eventdata = NULL;
+ event.eventdatatype = 0;
+ job->finish_callback(&event, job->finish_data);
+ }
+ free(job);
+ return TRUE;
+}
+
+static void* ui_jobthread(void *data) {
+ UiJob *job = data;
+ int result = job->job_func(job->job_data);
+ if(!result) {
+ write(event_pipe[1], &job, sizeof(void*)); // hack
+ XtAppAddWorkProc(app, ui_job_finished, job);
+
+ }
+ return NULL;
+}
+
+void ui_job(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;
+ pthread_t pid;
+ pthread_create(&pid, NULL, ui_jobthread, job);
+}
+
+void ui_clipboard_set(char *str) {
+ int length = strlen(str) + 1;
+
+ Display *dp = XtDisplayOfObject(active_window);
+ Window window = XtWindowOfObject(active_window);
+
+ XmString label = XmStringCreateLocalized("toolkit_clipboard");
+ long id = 0;
+
+ while(XmClipboardStartCopy(
+ dp,
+ window,
+ label,
+ CurrentTime,
+ NULL,
+ NULL,
+ &id) == ClipboardLocked);
+ XmStringFree(label);
+
+ while(XmClipboardCopy(
+ dp,
+ window,
+ id,
+ "STRING",
+ str,
+ length,
+ 1,
+ NULL) == ClipboardLocked);
+
+ while(XmClipboardEndCopy(dp, window, id) == ClipboardLocked);
+}
+
+char* ui_clipboard_get() {
+ Display *dp = XtDisplayOfObject(active_window);
+ Window window = XtWindowOfObject(active_window);
+
+ long id;
+ size_t size = 128;
+ char *buf = malloc(size);
+
+ int r;
+ for(;;) {
+ r = XmClipboardRetrieve(dp, window, "STRING", buf, size, NULL, &id);
+ if(r == ClipboardSuccess) {
+ break;
+ } else if(r == ClipboardTruncate) {
+ size *= 2;
+ buf = realloc(buf, size);
+ } else if(r == ClipboardNoData) {
+ free(buf);
+ buf = NULL;
+ break;
+ }
+ }
+
+ return buf;
+}
+
+void ui_set_active_window(Widget w) {
+ active_window = w;
+}
+
+Widget ui_get_active_window() {
+ return active_window;
+}
+
+/*
+ * doesn't work with gnome anymore
+ */
+void ui_window_dark_theme(Display *dp, Window window) {
+ Atom atom = XInternAtom(dp, "_GTK_THEME_VARIANT", False);
+ Atom type = XInternAtom(dp, "UTF8_STRING", False);
+ XChangeProperty(
+ dp,
+ window,
+ atom,
+ type,
+ 8,
+ PropModeReplace,
+ (const unsigned char*)"dark",
+ 4);
+}
+
+void ui_destroy_eventdata(Widget w, XtPointer data, XtPointer d) {
+ free(data);
+}
+
+void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) {
+ if(!groups) {
+ return;
+ }
+ size_t ngroups = uic_group_array_size(groups);
+ ui_set_widget_ngroups(ctx, widget, groups, ngroups);
+}
+
+void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups) {
+ if(ngroups > 0) {
+ uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
+ ui_set_enabled(widget, FALSE);
+ }
+}
+
--- /dev/null
+/*
+ * 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 TOOLKIT_H
+#define TOOLKIT_H
+
+#include <inttypes.h>
+#include "../ui/toolkit.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiEventData {
+ UiObject *obj;
+ ui_callback callback;
+ void *userdata;
+ int value;
+ void *customdata;
+} UiEventData;
+
+typedef struct UiEventDataExt {
+ UiObject *obj;
+ ui_callback callback;
+ void *userdata;
+ ui_callback callback2;
+ void *userdata2;
+ int value0;
+ int value1;
+ int value2;
+ int value3;
+ void *customdata0;
+ void *customdata1;
+ void *customdata2;
+ void *customdata3;
+} UiEventDataExt;
+
+typedef struct UiVarEventData {
+ UiObject *obj;
+ UiVar *var;
+ UiObserver **observers;
+ ui_callback callback;
+ void *userdata;
+ int value;
+} UiVarEventData;
+
+typedef struct UiJob {
+ UiObject *obj;
+ ui_threadfunc job_func;
+ void *job_data;
+ ui_callback finish_callback;
+ void *finish_data;
+} UiJob;
+
+typedef enum UiOrientation UiOrientation;
+enum UiOrientation { UI_HORIZONTAL = 0, UI_VERTICAL };
+
+void ui_exit_mainloop();
+
+Display* ui_motif_get_display(void);
+
+void ui_set_active_window(Widget w);
+Widget ui_get_active_window();
+
+void ui_secondary_event_loop(int *loop);
+void ui_window_dark_theme(Display *dp, Window window);
+
+void ui_destroy_eventdata(Widget w, XtPointer data, XtPointer d);
+
+void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) ;
+void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TOOLKIT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "../ui/widget.h"
+
+#include "container.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+
+UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
+ Arg xargs[64];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ Widget widget = create_widget(obj, args, userdata, parent, xargs, n);
+ XtManageChild(widget);
+ ui_container_add(ctn, widget);
+
+ return widget;
+}
+
+
+UIEXPORT UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
+ Arg xargs[64];
+ int n = 0;
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ char *name = args->name ? (char*)args->name : "separator";
+ Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+ Widget widget = XmCreateSeparator(parent, name, xargs, n);
+ XtManageChild(widget);
+ ui_container_add(ctn, widget);
+
+ return widget;
+}
+
+void ui_widget_set_size(UIWIDGET w, int width, int height) {
+
+}
+
+void ui_widget_redraw(UIWIDGET w) {
+
+}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "toolkit.h"
+#include "menu.h"
+#include "toolbar.h"
+#include "container.h"
+#include "../ui/window.h"
+#include "../common/context.h"
+
+#include "Grid.h"
+
+#include <cx/mempool.h>
+
+static int nwindows = 0;
+
+static int window_default_width = 600;
+static int window_default_height = 500;
+
+void ui_window_widget_destroy(UiObject *obj) {
+ XtDestroyWidget(obj->widget);
+ uic_object_destroy(obj);
+ nwindows--;
+ if(nwindows == 0) {
+ ui_exit_mainloop();
+ }
+}
+
+static void window_close_handler(Widget window, void *udata, void *cdata) {
+ UiObject *obj = udata;
+
+ uic_context_prepare_close(obj->ctx);
+ obj->ref--;
+ if(obj->ref > 0) {
+ XtUnmapWidget(obj->widget);
+ } else {
+ ui_window_widget_destroy(obj);
+ }
+}
+
+
+static UiObject* create_window(const char *title, void *window_data, Boolean simple) {
+ CxMempool *mp = cxMempoolCreateSimple(256);
+ const CxAllocator *a = mp->allocator;
+ UiObject *obj = uic_object_new_toplevel();
+ obj->window = window_data;
+ obj->destroy = ui_window_widget_destroy;
+
+ Arg args[16];
+ int n = 0;
+ XtSetArg(args[n], XmNtitle, title); n++;
+ XtSetArg(args[n], XmNminWidth, 100); n++;
+ XtSetArg(args[n], XmNminHeight, 50); n++;
+ XtSetArg(args[n], XmNwidth, window_default_width); n++;
+ XtSetArg(args[n], XmNheight, window_default_height); n++;
+
+ Widget toplevel = XtAppCreateShell(
+ ui_appname(),
+ "mainwindow",
+ //applicationShellWidgetClass,
+ vendorShellWidgetClass,
+ ui_motif_get_display(),
+ args,
+ n);
+
+ Atom wm_delete_window;
+ wm_delete_window = XmInternAtom(
+ XtDisplay(toplevel),
+ "WM_DELETE_WINDOW",
+ 0);
+ XmAddWMProtocolCallback(
+ toplevel,
+ wm_delete_window,
+ window_close_handler,
+ obj);
+
+ Widget window = XtVaCreateManagedWidget(
+ title,
+ xmMainWindowWidgetClass,
+ toplevel,
+ NULL);
+
+ // menu
+ if(!simple) {
+ ui_create_menubar(obj, window);
+ }
+
+ // content frame
+ n = 0;
+ Widget frame = XmCreateFrame(window, "window_frame", args, n);
+ XtManageChild(frame);
+
+ Widget form = XmCreateForm(frame, "window_form", args, 0);
+ XtManageChild(form);
+
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ Widget vbox = XtCreateManagedWidget("window_vbox", gridClass, form, args, n);
+ UiContainerX *container = ui_box_container(obj, vbox, UI_BOX_VERTICAL);
+ uic_object_push_container(obj, container);
+
+ obj->widget = toplevel;
+ nwindows++;
+ return obj;
+}
+
+UiObject* ui_window(const char *title, void *window_data) {
+ return create_window(title, window_data, FALSE);
+}
+
+UiObject* ui_simple_window(const char *title, void *window_data) {
+ return create_window(title, window_data, TRUE);
+}
--- /dev/null
+/*
+ * 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 WINDOW_H
+#define WINDOW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WINDOW_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+QT_MAKEFILE = ../build/ui/qt/Makefile.mk
+
+UI_QT_LIB = ../build/ui/qt/
+
+$(QT_MAKEFILE): qt/$(QT_PRO_FILE)
+ $(QMAKE) -o - $< > $(QT_MAKEFILE)
+
+$(UI_LIB): $(QT_MAKEFILE) $(OBJ) $(UI_LIB) FORCE
+ $(MAKE) -f $(QT_MAKEFILE)
+ $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ)
+
+$(UI_SHLIB): $(QT_MAKEFILE) $(OBJ) $(UI_LIB_SH) $(UI_LIB) FORCE
+ $(CXX) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) ../build/ui/qt/*.o -L../build/lib -lucx
+
+FORCE:
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "button.h"
+#include "container.h"
+#include "toolkit.h"
+
+UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QString str = QString::fromUtf8(args->label);
+ QPushButton *button = new QPushButton(str);
+
+ if(args->onclick) {
+ UiEventWrapper *event = new UiEventWrapper(obj, args->onclick, args->onclickdata);
+ button->connect(button, SIGNAL(clicked()), event, SLOT(slot()));
+ button->connect(button, SIGNAL(destroyed()), event, SLOT(destroy()));
+ }
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(button, layout);
+
+ return button;
+}
+
+void ui_button_set_label(UIWIDGET button, const char *label) {
+ QString str = QString::fromUtf8(label);
+ QAbstractButton *b = (QAbstractButton*)button;
+ b->setText(str);
+}
+
+void ui_button_set_icon(UIWIDGET button, const char *icon) {
+ // TODO
+}
+
+static void togglebutton_event(UiEvent *event, UiEventWrapper *wrapper) {
+ QPushButton *button = (QPushButton*)wrapper->customdata1;
+ event->intval = button->isChecked();
+ if(wrapper->var) {
+ event->eventdata = wrapper->var->value;
+ event->eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ }
+}
+
+UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QString str = QString::fromUtf8(args->label);
+ QPushButton *button = new QPushButton(str);
+ button->setCheckable(true);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+
+ if(args->onchange) {
+ UiEventWrapper *event = new UiEventWrapper(obj, args->onchange, args->onchangedata);
+ event->var = var;
+ event->customdata1 = button;
+ event->prepare_event = togglebutton_event;
+ button->connect(button, SIGNAL(clicked()), event, SLOT(slot()));
+ button->connect(button, SIGNAL(destroyed()), event, SLOT(destroy()));
+ }
+
+ if(var) {
+ UiInteger *i = (UiInteger*)var->value;
+
+ if(i->value) {
+ button->setChecked(true);
+ }
+
+ i->obj = button;
+ i->get = ui_togglebutton_get;
+ i->set = ui_togglebutton_set;
+ }
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(button, layout);
+
+ return button;
+}
+
+int64_t ui_togglebutton_get(UiInteger *value) {
+ QPushButton *button = (QPushButton*)value->obj;
+ value->value = button->isChecked();
+ return value->value;
+}
+
+void ui_togglebutton_set(UiInteger *value, int64_t i) {
+ QPushButton *button = (QPushButton*)value->obj;
+ value->value = i;
+ if(i != 0) {
+ button->setChecked(true);
+ }
+}
+
+static void checkbox_event(UiEvent *event, UiEventWrapper *wrapper) {
+ QPushButton *button = (QPushButton*)wrapper->customdata1;
+ event->intval = button->isChecked();
+ if(wrapper->var) {
+ event->eventdata = wrapper->var->value;
+ event->eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ }
+}
+
+
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QString str = QString::fromUtf8(args->label);
+ QCheckBox *checkbox = new QCheckBox(str);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+
+ if(args->onchange) {
+ UiEventWrapper *event = new UiEventWrapper(obj, args->onchange, args->onchangedata);
+ event->var = var;
+ event->customdata1 = checkbox;
+ event->prepare_event = checkbox_event;
+ checkbox->connect(checkbox, SIGNAL(clicked()), event, SLOT(slot()));
+ checkbox->connect(checkbox, SIGNAL(destroyed()), event, SLOT(destroy()));
+ }
+
+ if(var) {
+ UiInteger *i = (UiInteger*)var->value;
+
+ if(i->value) {
+ checkbox->setChecked(true);
+ }
+
+ i->obj = checkbox;
+ i->get = ui_checkbox_get;
+ i->set = ui_checkbox_set;
+ }
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(checkbox, layout);
+
+ return checkbox;
+}
+
+int64_t ui_checkbox_get(UiInteger *value) {
+ QPushButton *button = (QPushButton*)value->obj;
+ value->value = button->isChecked();
+ return value->value;
+}
+
+void ui_checkbox_set(UiInteger *value, int64_t i) {
+ QPushButton *button = (QPushButton*)value->obj;
+ value->value = i;
+ if(i != 0) {
+ button->setChecked(true);
+ }
+}
+
+
+static void radiobutton_event(UiEvent *event, UiEventWrapper *wrapper) {
+ if(wrapper->var) {
+ UiInteger *value = (UiInteger*)wrapper->var->value;
+ event->eventdata = value;
+ event->eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ event->intval = value->get(value);
+ }
+}
+
+UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QString str = QString::fromUtf8(args->label);
+ QRadioButton *button = new QRadioButton(str);
+ button->setAutoExclusive(false);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *value = (UiInteger*)var->value;
+ QButtonGroup *buttonGroup = (QButtonGroup*)value->obj;
+ if(!buttonGroup) {
+ buttonGroup = new QButtonGroup();
+ value->obj = buttonGroup;
+ }
+ int id = buttonGroup->buttons().size()+1;
+ buttonGroup->addButton(button, id);
+ if(value->value == id) {
+ button->setChecked(true);
+ }
+ value->get = ui_radiobutton_get;
+ value->set = ui_radiobutton_set;
+ }
+
+ UiEventWrapper *event = new UiEventWrapper(obj, args->onchange, args->onchangedata);
+ event->var = var;
+ event->customdata1 = button;
+ event->prepare_event = togglebutton_event;
+ button->connect(button, SIGNAL(clicked()), event, SLOT(slot()));
+ button->connect(button, SIGNAL(destroyed()), event, SLOT(destroy()));
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(button, layout);
+
+ return button;
+}
+
+int64_t ui_radiobutton_get(UiInteger *value) {
+ QButtonGroup *buttonGroup = (QButtonGroup*)value->obj;
+ value->value = buttonGroup->checkedId();
+ return value->value;
+}
+
+void ui_radiobutton_set(UiInteger *value, int64_t i) {
+ QButtonGroup *buttonGroup = (QButtonGroup*)value->obj;
+ QAbstractButton *button = buttonGroup->button(i);
+ if(button) {
+ button->setChecked(true);
+ value->value = i;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include "toolkit.h"
+#include "../ui/button.h"
+#include <QPushButton>
+#include <QRadioButton>
+#include <QButtonGroup>
+#include <QCheckBox>
+
+extern "C" {
+
+
+int64_t ui_togglebutton_get(UiInteger *value);
+void ui_togglebutton_set(UiInteger *value, int64_t i);
+
+int64_t ui_checkbox_get(UiInteger *value);
+void ui_checkbox_set(UiInteger *value, int64_t i);
+
+int64_t ui_radiobutton_get(UiInteger *value);
+void ui_radiobutton_set(UiInteger *value, int64_t i);
+
+}
+
+#endif /* BUTTON_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include "container.h"
+#include "../common/object.h"
+#include "../common/container.h"
+
+#include <cx/mempool.h>
+
+#include <QSpacerItem>
+#include <QStackedWidget>
+#include <QLabel>
+#include <QDockWidget>
+
+static void delete_container(UiContainerPrivate *ct) {
+ delete ct;
+}
+
+void ui_obj_add_container(UiObject *obj, UiContainerPrivate *ct) {
+ UiContainerX *container = (UiContainerX*)ui_malloc(obj->ctx, sizeof(UiContainerX));
+ container->close = 0;
+ container->container = ct;
+ container->prev = NULL;
+ container->next = NULL;
+ ct->container = container;
+ cxMempoolRegister(obj->ctx->mp, ct, (cx_destructor_func)delete_container);
+ uic_object_push_container(obj, container);
+}
+
+/* ------------------------ margin helper ------------------------ */
+
+QWidget* ui_widget_set_margin(QWidget *w, int left, int right, int top, int bottom) {
+ if((left | right | top | bottom) == 0) {
+ return w;
+ }
+ QWidget* wrapper = new QWidget;
+ QVBoxLayout* inner = new QVBoxLayout(wrapper);
+ inner->setContentsMargins(left, top, right, bottom);
+ inner->addWidget(w);
+
+ // TODO: handle widget visibility changes
+
+ return wrapper;
+}
+
+/* -------------------- UiBoxContainer -------------------- */
+
+UiBoxContainer::UiBoxContainer(QBoxLayout* box) {
+ this->box = box;
+ this->direction = box->direction();
+ box->setContentsMargins(QMargins(0,0,0,0));
+ box->setSpacing(0);
+}
+
+void UiBoxContainer::add(QWidget* widget, UiLayout& layout) {
+ bool fill = layout.fill;
+ if(hasStretchedWidget && fill) {
+ fill = false;
+ fprintf(stderr, "UiError: container has 2 filled widgets");
+ }
+
+ uic_layout_setup_margin(&layout);
+ widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom);
+ box->addWidget(widget);
+ if(direction == Qt::LeftToRight) {
+ box->setAlignment(widget, Qt::AlignTop);
+ }
+
+ if(!hasStretchedWidget) {
+ QSpacerItem *newspace = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ box->removeItem(space);
+ box->addSpacerItem(newspace);
+ space = newspace;
+ }
+
+ if(fill) {
+ hasStretchedWidget = true;
+ }
+ current = widget;
+}
+
+UIWIDGET ui_box(UiObject *obj, UiContainerArgs *args, QBoxLayout::Direction dir) {
+ UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ QWidget *widget = new QWidget();
+ QBoxLayout *box = new QBoxLayout(dir);
+ widget->setLayout(box);
+ ctn->add(widget, layout);
+
+ ui_obj_add_container(obj, new UiBoxContainer(box));
+
+ return widget;
+}
+
+UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) {
+ return ui_box(obj, args, QBoxLayout::TopToBottom);
+}
+
+UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) {
+ return ui_box(obj, args, QBoxLayout::LeftToRight);
+}
+
+
+/* -------------------- UiGridContainer -------------------- */
+
+UiGridContainer::UiGridContainer(
+ QGridLayout *grid,
+ int margin,
+ int columnspacing,
+ int rowspacing,
+ bool def_hexpand,
+ bool def_vexpand,
+ bool def_hfill,
+ bool def_vfill)
+{
+ this->current = NULL;
+ this->grid = grid;
+ this->def_hexpand = def_hexpand;
+ this->def_vexpand = def_vexpand;
+ this->def_hfill = def_hfill;
+ this->def_vfill = def_vfill;
+ grid->setContentsMargins(QMargins(margin, margin, margin, margin));
+ grid->setHorizontalSpacing(columnspacing);
+ grid->setVerticalSpacing(rowspacing);
+}
+
+void UiGridContainer::add(QWidget* widget, UiLayout& layout) {
+ if(container->newline) {
+ x = 0;
+ y++;
+ container->newline = false;
+ }
+
+ uic_layout_setup_expand_fill(&layout, def_hexpand, def_vexpand, def_hfill, def_vfill);
+ uic_layout_setup_margin(&layout);
+
+ if(layout.hexpand) {
+ col_expanding = true;
+ }
+ if(layout.vexpand) {
+ row_expanding = true;
+ }
+
+ if(layout.hexpand) {
+ grid->setColumnStretch(x, 1);
+ }
+ if(layout.vexpand) {
+ grid->setRowStretch(y, 1);
+ }
+
+ Qt::Alignment alignment = 0;
+ if(!layout.hfill) {
+ alignment = Qt::AlignLeft;
+ }
+ if(!layout.vfill) {
+ alignment = Qt::AlignTop;
+ }
+
+ int colspan = layout.colspan > 0 ? layout.colspan : 1;
+ int rowspan = layout.rowspan > 0 ? layout.rowspan : 1;
+
+ widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom);
+ grid->addWidget(widget, y, x, rowspan, colspan, alignment);
+
+ if(x > max_x) {
+ max_x = x;
+ }
+ if(y > max_y) {
+ max_y = y;
+ }
+
+ x += colspan;
+
+ current = widget;
+}
+
+void UiGridContainer::end() {
+ if(!col_expanding) {
+ QSpacerItem *filler = new QSpacerItem(0, 0);
+ x = max_x + 1;
+ grid->setColumnStretch(x, 1);
+ grid->addItem(filler, 0, x, 1, 1, 0);
+ }
+ if(!row_expanding) {
+ QSpacerItem *filler = new QSpacerItem(0, 0);
+ y++;
+ grid->setRowStretch(y, 1);
+ grid->addItem(filler, y, 0, 1, 1, 0);
+ }
+}
+
+UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
+ UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ QWidget *widget = new QWidget();
+ QGridLayout *grid = new QGridLayout();
+ widget->setLayout(grid);
+ ctn->add(widget, layout);
+
+ ui_obj_add_container(obj, new UiGridContainer(
+ grid,
+ args->margin,
+ args->columnspacing,
+ args->rowspacing,
+ args->def_hexpand,
+ args->def_vexpand,
+ args->def_hfill,
+ args->def_vfill));
+
+ return widget;
+}
+
+
+/* ---------------------------- UiSidebar ---------------------------- */
+
+UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
+ QVariant v = obj->widget->property("ui_sidebar");
+ QDockWidget *dock = (QDockWidget*)v.value<void*>();
+ if(!dock) {
+ fprintf(stderr, "Error: window is not configured for sidebar\n");
+ return nullptr;
+ }
+
+ QWidget *widget = new QWidget();
+ QBoxLayout *box = new QBoxLayout(QBoxLayout::TopToBottom);
+ widget->setLayout(box);
+ dock->setWidget(widget);
+
+ ui_obj_add_container(obj, new UiBoxContainer(box));
+
+ return dock;
+}
+
+/* -------------------- Container Helper Functions -------------------- */
+
+void ui_container_begin_close(UiObject *obj) {
+ obj->container_end->close = true;
+}
+
+int ui_container_finish(UiObject *obj) {
+ if(obj->container_end->close) {
+ UiContainerPrivate *ctn = (UiContainerPrivate*)obj->container_end->container;
+ ctn->end();
+ ui_end_new(obj);
+ return 0;
+ }
+ return 1;
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 CONTAINER_H
+#define CONTAINER_H
+
+#include "toolkit.h"
+#include "../ui/container.h"
+#include "window.h"
+
+#include <string.h>
+#include <QBoxLayout>
+#include <QGridLayout>
+#include <QTabWidget>
+#include <QStackedWidget>
+#include <QSplitter>
+
+#define ui_obj_container(obj) (UiContainerPrivate*)((UiContainerX*)obj->container_end)->container
+
+struct UiContainerPrivate {
+ UIWIDGET current;
+ UiContainerX *container;
+
+ virtual void add(QWidget *widget, UiLayout& layout) = 0;
+ virtual void end() {}
+};
+
+class UiBoxContainer : public UiContainerPrivate {
+public:
+ QBoxLayout *box;
+ bool hasStretchedWidget = false;
+ QSpacerItem *space;
+ QBoxLayout::Direction direction;
+
+ UiBoxContainer(QBoxLayout *box);
+
+ virtual void add(QWidget *widget, UiLayout& layout);
+};
+
+class UiGridContainer : public UiContainerPrivate {
+public:
+ QGridLayout *grid;
+ int x = 0;
+ int y = 0;
+ bool def_hexpand;
+ bool def_vexpand;
+ bool def_hfill;
+ bool def_vfill;
+ bool col_expanding = false;
+ bool row_expanding = false;
+ int max_x;
+ int max_y;
+
+ UiGridContainer(
+ QGridLayout *grid,
+ int margin,
+ int columnspacing,
+ int rowspacing,
+ bool def_hexpand,
+ bool def_vexpand,
+ bool def_hfill,
+ bool def_vfill);
+
+ virtual void add(QWidget *widget, UiLayout& layout);
+ virtual void end();
+};
+
+void ui_obj_add_container(UiObject *obj, UiContainerPrivate *ct);
+
+QWidget* ui_widget_set_margin(QWidget *w, int left, int right, int top, int bottom);
+
+
+#endif /* CONTAINER_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "entry.h"
+
+#include "container.h"
+#include "../common/context.h"
+
+#include <QDoubleSpinBox>
+#include <QSpinBox>
+
+
+
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ double min = args->min;
+ double max = args->max != 0 ? args->max : 100000;
+
+ bool use_double = false;
+ UiVar *var = NULL;
+ UiVarType vartype = UI_VAR_SPECIAL;
+ if(args->varname) {
+ var = uic_get_var(obj->ctx, args->varname);
+ vartype = var->type;
+ if(var->type == UI_VAR_DOUBLE) {
+ use_double = true;
+ } else if(var->type == UI_VAR_RANGE) {
+ use_double = true;
+ } else if(var->type != UI_VAR_INTEGER) {
+ var = NULL;
+ fprintf(stderr, "UI Error: var '%s' has wrong type (must be int/double/range)\n", args->varname);
+ }
+ }
+
+ if(!var) {
+ if(args->intvalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+ vartype = UI_VAR_INTEGER;
+ } else if(args->doublevalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+ vartype = UI_VAR_DOUBLE;
+ use_double = true;
+ } else if(args->rangevalue) {
+ var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+ vartype = UI_VAR_RANGE;
+ use_double = true;
+ } else {
+ if(args->digits > 0) {
+ use_double = true;
+ }
+ }
+ }
+
+ if(vartype == UI_VAR_RANGE) {
+ UiRange *r = (UiRange*)var->value;
+ min = r->min;
+ max = r->max;
+ }
+
+ QAbstractSpinBox *widget = nullptr;
+ if(use_double) {
+ QDoubleSpinBox *spinbox = new QDoubleSpinBox();
+ spinbox->setDecimals(args->digits);
+ if(args->step != 0) {
+ spinbox->setSingleStep(args->step);
+ }
+ spinbox->setMinimum(min);
+ spinbox->setMaximum(max);
+ widget = spinbox;
+ } else {
+ QSpinBox *spinbox = new QSpinBox();
+ if(args->step != 0) {
+ spinbox->setSingleStep(args->step);
+ }
+ spinbox->setMinimum((int)min);
+ spinbox->setMaximum((int)max);
+ widget = spinbox;
+ }
+
+ if(var) {
+ if(vartype == UI_VAR_INTEGER) {
+ UiInteger *value = (UiInteger*)var->value;
+ value->obj = widget;
+ if(value->value != 0) {
+ QSpinBox *spinbox = (QSpinBox*)widget;
+ spinbox->setValue(value->value);
+ }
+ value->get = ui_spinbox_int_get;
+ value->set = ui_spinbox_int_set;
+ } else if(vartype == UI_VAR_DOUBLE) {
+ UiDouble *value = (UiDouble*)var->value;
+ value->obj = widget;
+ if(value->value != 0) {
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)widget;
+ spinbox->setValue(value->value);
+ }
+ value->get = ui_spinbox_double_get;
+ value->set = ui_spinbox_double_set;
+ } else if(vartype == UI_VAR_RANGE) {
+ UiRange *value = (UiRange*)var->value;
+ value->obj = widget;
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)widget;
+ if(value->value != 0) {
+ spinbox->setValue(value->value);
+ }
+ if(value->min != value->max) {
+ spinbox->setRange(value->min, value->max);
+ }
+ if(value->extent != 0) {
+ spinbox->setSingleStep(value->extent);
+ }
+ value->get = ui_spinbox_range_get;
+ value->set = ui_spinbox_range_set;
+ value->setrange = ui_spinbox_range_setrange;
+ value->setextent = ui_spinbox_range_setextent;
+ }
+ }
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(widget, layout);
+ return widget;
+}
+
+int64_t ui_spinbox_int_get(UiInteger *i) {
+ QSpinBox *spinbox = (QSpinBox*)i->obj;
+ i->value = spinbox->value();
+ return i->value;
+}
+
+void ui_spinbox_int_set(UiInteger *i, int64_t value) {
+ QSpinBox *spinbox = (QSpinBox*)i->obj;
+ spinbox->setValue(value);
+ i->value = spinbox->value();
+}
+
+double ui_spinbox_double_get(UiDouble *d) {
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)d->obj;
+ d->value = spinbox->value();
+ return d->value;
+}
+
+void ui_spinbox_double_set(UiDouble *d, double value) {
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)d->obj;
+ spinbox->setValue(value);
+ d->value = spinbox->value();
+}
+
+double ui_spinbox_range_get(UiRange *range) {
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)range->obj;
+ range->value = spinbox->value();
+ return range->value;
+}
+
+void ui_spinbox_range_set(UiRange *range, double value) {
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)range->obj;
+ spinbox->setValue(value);
+ range->value = spinbox->value();
+}
+
+void ui_spinbox_range_setrange(UiRange *range, double min, double max) {
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)range->obj;
+ spinbox->setRange(min, max);
+ range->min = min;
+ range->max = max;
+}
+
+void ui_spinbox_range_setextent(UiRange *range, double extent) {
+ QDoubleSpinBox *spinbox = (QDoubleSpinBox*)range->obj;
+ spinbox->setSingleStep(extent);
+ range->extent = extent;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ENTRY_H
+#define ENTRY_H
+
+#include "../ui/entry.h"
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int64_t ui_spinbox_int_get(UiInteger *i);
+void ui_spinbox_int_set(UiInteger *i, int64_t value);
+
+double ui_spinbox_double_get(UiDouble *d);
+void ui_spinbox_double_set(UiDouble *d, double value);
+
+double ui_spinbox_range_get(UiRange *range);
+void ui_spinbox_range_set(UiRange *range, double value);
+void ui_spinbox_range_setrange(UiRange *range, double min, double max);
+void ui_spinbox_range_setextent(UiRange *range, double extent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ENTRY_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 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 "graphics.h"
+#include "container.h"
+
+
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 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 GRAPHICS_H
+#define GRAPHICS_H
+
+#include "toolkit.h"
+#include "../ui/graphics.h"
+
+#include <QWidget>
+#include <QPainter>
+#include <QColor>
+#include <QStaticText>
+
+
+#endif /* GRAPHICS_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "image.h"
+
+
+
+void ui_image_ref(UIIMAGE img) {
+ // TODO
+}
+
+void ui_image_unref(UIIMAGE img) {
+ // TODO
+}
\ No newline at end of file
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IMAGE_H
+#define IMAGE_H
+
+#include "toolkit.h"
+#include "../ui/image.h"
+
+#endif /* IMAGE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "label.h"
+#include "container.h"
+#include "toolkit.h"
+#include "ui/display.h"
+
+
+UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QString str = QString::fromUtf8(args->label);
+ QLabel *widget = new QLabel(str);
+
+ Qt::AlignmentFlag align = Qt::AlignCenter;
+ if(args->align == UI_ALIGN_LEFT) {
+ align = Qt::AlignLeft;
+ } else if(args->align == UI_ALIGN_RIGHT) {
+ align = Qt::AlignRight;
+ }
+ widget->setAlignment(align);
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(widget, layout);
+
+ 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);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 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 LABEL_H
+#define LABEL_H
+
+#include "toolkit.h"
+#include <QLabel>
+
+#endif /* LABEL_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 "list.h"
+#include "container.h"
+
+#include <QTreeView>
+#include <QTreeWidgetItem>
+#include <QListView>
+
+extern "C" void* ui_strmodel_getvalue(void *elm, int column) {
+ return column == 0 ? elm : NULL;
+}
+
+static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
+ return getvalue(elm, col);
+}
+
+static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+ return NULL;
+}
+
+UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QListView *view = new QListView();
+ ui_getvaluefunc2 getvalue = nullptr;
+ void *getvaluedata = nullptr;
+ if(args->getvalue2) {
+ getvalue = args->getvalue2;
+ getvaluedata = args->getvalue2data;
+ } else if(args->getvalue) {
+ getvalue = getvalue_wrapper;
+ getvaluedata = (void*)args->getvalue;
+ } else {
+ getvalue = getvalue_wrapper;
+ getvaluedata = (void*)ui_strmodel_getvalue;
+ }
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ ListModel *model = new ListModel(obj, view, var, getvalue, getvaluedata);
+ view->setModel(model);
+
+ if(var) {
+ UiList *list = (UiList*)var->value;
+ list->update = ui_listmodel_update;
+ list->getselection = ui_listmodel_getselection;
+ list->setselection = ui_listmodel_setselection;
+ list->obj = model;
+ }
+
+ model->setActivationCallback(args->onactivate, args->onactivatedata);
+ model->setSelectionCallback(args->onselection, args->onselectiondata);
+
+ QItemSelectionModel *s = view->selectionModel();
+ QObject::connect(
+ s,
+ SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ model,
+ SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(view, layout);
+
+ return view;
+}
+
+UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QTreeView *view = new QTreeView();
+ view->setItemsExpandable(false);
+ view->setRootIsDecorated(false);
+ if(args->multiselection) {
+ view->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ }
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ ui_getvaluefunc2 getvalue = args->getvalue2;
+ void *getvaluedata = args->getvalue2data;
+ if(!getvalue) {
+ if(args->getvalue) {
+ getvalue = getvalue_wrapper;
+ getvaluedata = (void*)args->getvalue;
+ } else {
+ getvalue = null_getvalue;
+ }
+ }
+ TableModel *model = new TableModel(obj, view, var, args->model, getvalue, getvaluedata);
+ view->setModel(model);
+
+ if(var) {
+ UiList *list = (UiList*)var->value;
+ list->update = ui_listmodel_update;
+ list->getselection = ui_listmodel_getselection;
+ list->setselection = ui_listmodel_setselection;
+ list->obj = model;
+ }
+
+ model->setActivationCallback(args->onactivate, args->onactivatedata);
+ model->setSelectionCallback(args->onselection, args->onselectiondata);
+
+ QItemSelectionModel *s = view->selectionModel();
+ QObject::connect(
+ s,
+ SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ model,
+ SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
+
+
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(view, layout);
+
+ return view;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TREE_H
+#define TREE_H
+
+#include "../ui/tree.h"
+#include "model.h"
+
+#include <QTableView>
+
+
+
+#endif /* TREE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <QMenuBar>
+#include <QAction>
+
+#include "menu.h"
+#include "toolkit.h"
+#include "../common/context.h"
+#include "../common/menu.h"
+#include "../ui/properties.h"
+#include "../ui/window.h"
+#include "stock.h"
+#include "container.h"
+
+
+static ui_menu_add_f createMenuItem[] = {
+ /* UI_MENU */ add_menu_widget,
+ /* UI_MENU_ITEM */ add_menuitem_widget,
+ /* UI_MENU_CHECK_ITEM */ add_checkitem_widget,
+ /* UI_MENU_RADIO_ITEM */ add_radioitem_widget,
+ /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_CHECKITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_RADIOITEM_LIST */ add_menuitem_list_widget,
+ /* UI_MENU_SEPARATOR */ add_menuseparator_widget
+};
+
+/*
+ * create all menu child items
+ */
+static void add_menu_items(QMenu *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(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
+ UiMenu *m = (UiMenu*)item;
+ QMenu *menu = parent->addMenu(m->label);
+ add_menu_items(menu, i, m, obj);
+}
+
+static UiAction* create_action(
+ UiObject *obj,
+ const char *icon,
+ const char *label,
+ ui_callback callback,
+ void *userdata,
+ int *states)
+{
+ QString str = QString::fromUtf8(label);
+ UiAction *action = new UiAction(obj, str, callback, userdata);
+ if(icon) {
+ action->setIcon(QIcon::fromTheme(icon));
+ action->setIconVisibleInMenu(true);
+ }
+
+ if(states) {
+ size_t nstates = uic_group_array_size(states);
+ uic_add_group_widget_i(obj->ctx, action, (ui_enablefunc)ui_action_enable, states, nstates);
+ action->setEnabled(false);
+ }
+
+ return action;
+}
+
+void add_menuitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
+ UiMenuItem *it = (UiMenuItem*)item;
+ UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+ parent->addAction(action);
+ QObject::connect(action, SIGNAL(triggered()), action, SLOT(trigger()));
+}
+
+void add_menuseparator_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
+ parent->addSeparator();
+}
+
+void add_checkitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
+ UiMenuCheckItem *it = (UiMenuCheckItem*)item;
+
+ UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+ parent->addAction(action);
+ action->setCheckable(true);
+ action->prepare_event = ui_checkableaction_prepare_event;
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, it->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *value = (UiInteger*)var->value;
+ value->obj = action;
+ value->get = ui_checkableaction_get;
+ value->set = ui_checkableaction_set;
+
+ action->setChecked((bool)value->value);
+ }
+ action->var = var;
+}
+
+void add_radioitem_widget(QMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
+ UiMenuRadioItem *it = (UiMenuRadioItem*)item;
+
+ UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+ parent->addAction(action);
+ action->setCheckable(true);
+ action->prepare_event = ui_actiongroup_prepare_event;
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, it->varname, UI_VAR_INTEGER);
+ if(var) {
+ UiInteger *value = (UiInteger*)var->value;
+ QActionGroup *group = (QActionGroup*)value->obj;
+ if(!group) {
+ group = new QActionGroup(parent);
+ value->obj = group;
+ }
+ group->addAction(action);
+ value->get = ui_actiongroup_get;
+ value->set = ui_actiongroup_set;
+ if(value->value != 0) {
+ ui_actiongroup_set(value, value->value);
+ }
+ }
+ action->var;
+}
+
+void ui_actiongroup_prepare_event(UiEvent *event, UiAction *action) {
+ if(action->var) {
+ UiInteger *value = (UiInteger*)action->var->value;
+ event->eventdata = value;
+ event->eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ event->intval = value->get(value);
+ }
+}
+
+int64_t ui_actiongroup_get(UiInteger *value) {
+ QActionGroup *group = (QActionGroup*)value->obj;
+ auto actions = group->actions();
+ int i = 1;
+ foreach(const QAction *action, actions) {
+ if(action->isChecked()) {
+ value->value = i;
+ break;
+ }
+ i++;
+ }
+ return value->value;
+}
+
+void ui_actiongroup_set(UiInteger *value, int64_t i) {
+ QActionGroup *group = (QActionGroup*)value->obj;
+ auto actions = group->actions();
+ if(i > 0) {
+ if(i-1 < actions.size()) {
+ actions[i]->setEnabled(true);
+ }
+ value->value = i;
+ } else {
+ foreach(QAction *action, actions) {
+ action->setEnabled(false);
+ }
+ value->value = 0;
+ }
+}
+
+void add_menuitem_list_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
+
+}
+
+
+void ui_add_menus(UiObject *obj, QMainWindow *window) {
+ UiMenu *menus_begin = uic_get_menu_list();
+ if(menus_begin == NULL) {
+ return;
+ }
+
+ QMenuBar *mb = window->menuBar();
+
+ UiMenu *ls = menus_begin;
+ while(ls) {
+ if(ls->item.type == UI_MENU) {
+ QMenu *menu = mb->addMenu(ls->label);
+ add_menu_items(menu, 0, ls, obj);
+ }
+ ls = (UiMenu*)ls->item.next;
+ }
+}
+
+void ui_checkableaction_prepare_event(UiEvent *event, UiAction *action) {
+ if(action->var) {
+ event->eventdata = action->var->value;
+ event->eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ }
+ event->intval = action->isChecked();
+}
+
+int64_t ui_checkableaction_get(UiInteger *value) {
+ UiAction *action= (UiAction*)value->obj;
+ value->value = action->isChecked();
+ return value->value;
+}
+
+void ui_checkableaction_set(UiInteger *value, int64_t i) {
+ UiAction *action = (UiAction*)value->obj;
+ value->value = i;
+ if(i != 0) {
+ action->setChecked((bool)i);
+ }
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 MENU_H
+#define MENU_H
+
+#include "../ui/menu.h"
+#include "../common/menu.h"
+
+#include "toolkit.h"
+
+#include <QMainWindow>
+#include <QMenu>
+#include <QMenuBar>
+#include <QContextMenuEvent>
+
+void ui_add_menus(UiObject *obj, QMainWindow *window);
+
+typedef void(*ui_menu_add_f)(QMenu*, int, UiMenuItemI*, UiObject*);
+
+void add_menu_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuseparator_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj);
+void add_checkitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj);
+void add_radioitem_widget(QMenu *parent, int index, UiMenuItemI *item, UiObject *obj);
+void add_checkitemnv_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj);
+void add_menuitem_list_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj);
+
+void ui_checkableaction_prepare_event(UiEvent *event, UiAction *action);
+int64_t ui_checkableaction_get(UiInteger *value);
+void ui_checkableaction_set(UiInteger *value, int64_t i);
+
+void ui_actiongroup_prepare_event(UiEvent *event, UiAction *action);
+int64_t ui_actiongroup_get(UiInteger *value);
+void ui_actiongroup_set(UiInteger *value, int64_t i);
+
+#endif /* MENU_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 "model.h"
+
+
+ListModel::ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata){
+ this->obj = obj;
+ this->view = view;
+ this->var = var;
+ this->getvalue = getvalue;
+ this->getvaluedata = getvaluedata;
+ this->onactivate = nullptr;
+ this->onactivatedata = nullptr;
+ this->onselection = nullptr;
+ this->onselectiondata = nullptr;
+}
+
+void ListModel::setActivationCallback(ui_callback f, void *userdata) {
+ onactivate = f;
+ onactivatedata = userdata;
+}
+
+void ListModel::setSelectionCallback(ui_callback f, void *userdata) {
+ onselection = f;
+ onselectiondata = userdata;
+}
+
+void ListModel::update(int row) {
+ if(row >= 0) {
+ this->update(row);
+ } else {
+ this->beginResetModel();
+ this->endResetModel();
+ }
+}
+
+int ListModel::rowCount(const QModelIndex& parent) const {
+ UiList *list = (UiList*)var->value;
+ return ui_list_count(list);
+}
+
+QVariant ListModel::data(const QModelIndex &index, int role) const {
+ if(role == Qt::DisplayRole) {
+ UiList *ls = (UiList*)var->value;
+ void *rowData = ls->get(ls, index.row());
+ if(rowData && getvalue) {
+ UiBool freeResult = false;
+ void *value = getvalue(ls, rowData, index.row(), 0, getvaluedata, &freeResult);
+ if(value) {
+ auto qs = QString::fromUtf8((char*)value);
+ if(freeResult) {
+ free(value);
+ }
+ return qs;
+ }
+ }
+ }
+ return QVariant();
+}
+
+void ListModel::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
+ UiListSelection sel = ui_selection_model_to_selection(view->selectionModel());
+
+ UiEvent event;
+ event.obj = obj;
+ event.window = obj->window;
+ event.document = obj->ctx->document;
+ event.eventdata = &sel;
+ event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+ event.intval = sel.count;
+ event.set = ui_get_setop();
+
+ if(onactivate) {
+ onactivate(&event, onactivatedata);
+ }
+ if(onselection) {
+ onselection(&event, onselectiondata);
+ }
+
+ free(sel.rows);
+}
+
+
+
+TableModel::TableModel(UiObject *obj, QTreeView *view, UiVar *var, UiModel *model, ui_getvaluefunc2 getvalue, void *getvaluedata){
+ this->obj = obj;
+ this->view = view;
+ this->var = var;
+ this->model = model;
+ this->getvalue = getvalue;
+ this->getvaluedata = getvaluedata;
+ this->onactivate = nullptr;
+ this->onactivatedata = nullptr;
+ this->onselection = nullptr;
+ this->onselectiondata = nullptr;
+}
+
+void TableModel::setActivationCallback(ui_callback f, void *userdata) {
+ onactivate = f;
+ onactivatedata = userdata;
+}
+
+void TableModel::setSelectionCallback(ui_callback f, void *userdata) {
+ onselection = f;
+ onselectiondata = userdata;
+}
+
+void TableModel::update(int row) {
+ if(row >= 0) {
+ this->update(row);
+ } else {
+ this->beginResetModel();
+ this->endResetModel();
+ }
+}
+
+int TableModel::rowCount(const QModelIndex& parent) const {
+ UiList *list = (UiList*)var->value;
+ return ui_list_count(list);
+}
+
+int TableModel::columnCount(const QModelIndex &parent) const {
+ return model->columns;
+}
+
+QVariant TableModel::data(const QModelIndex &index, int role) const {
+ if(role == Qt::DisplayRole) {
+ UiList *ls = (UiList*)var->value;
+ void *rowData = ls->get(ls, index.row());
+ if(rowData) {
+ int col = index.column();
+ UiBool freeResult = false;
+ void *value = getvalue(ls, rowData, index.row(), col, getvaluedata, &freeResult);
+ if(value) {
+ UiModelType type = model->types[col];
+ switch(type) {
+ case UI_STRING: {
+ auto qs = QString::fromUtf8((char*)value);
+ if(freeResult) {
+ free(value);
+ }
+ return qs;
+ }
+ case UI_STRING_FREE: {
+ QString s = QString::fromUtf8((char*)value);
+ free(value);
+ return s;
+ }
+ case UI_INTEGER: {
+ intptr_t i = (intptr_t)value;
+ return QString::number(i);
+ }
+ case UI_ICON: {
+ break; // TODO
+ }
+ case UI_ICON_TEXT: {
+ break; // TODO
+ }
+ case UI_ICON_TEXT_FREE: {
+ break; // TODO
+ }
+ }
+ }
+ }
+ }
+ return QVariant();
+}
+
+QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const {
+ if(role == Qt::DisplayRole) {
+ char *label = model->titles[section];
+ return QString::fromUtf8(label);
+ }
+ return QVariant();
+}
+
+void TableModel::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
+ UiListSelection sel = ui_selection_model_to_selection(view->selectionModel());
+
+ UiEvent event;
+ event.obj = obj;
+ event.window = obj->window;
+ event.document = obj->ctx->document;
+ event.eventdata = &sel;
+ event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+ event.intval = sel.count;
+ event.set = ui_get_setop();
+
+ if(onactivate) {
+ onactivate(&event, onactivatedata);
+ }
+ if(onselection) {
+ onselection(&event, onselectiondata);
+ }
+
+ free(sel.rows);
+}
+
+
+
+UiListSelection ui_selection_model_to_selection(QItemSelectionModel *model) {
+ UiListSelection sel;
+ sel.rows = NULL;
+ sel.count = 0;
+
+ if(model->hasSelection()) {
+ QModelIndexList indices = model->selectedIndexes();
+ sel.count = indices.count();
+ sel.rows = (int*)calloc(sel.count, sizeof(int));
+
+ int i = 0;
+ for (const QModelIndex &index : indices) {
+ sel.rows[i++] = index.row();
+ }
+ }
+
+ return sel;
+}
+
+/* ---------------------- UiList implementation -----------------------------*/
+
+void ui_listmodel_update(UiList *list, int row) {
+ ListModel *model = (ListModel*)list->obj;
+ model->update(row);
+}
+
+void ui_listmodel_setselection(UiList *list, UiListSelection sel) {
+ ListModel *model = (ListModel*)list->obj;
+ QItemSelection selection;
+ for (int i=0;i<sel.count;i++) {
+ QModelIndex index = model->index(sel.rows[i]);
+ selection.select(index, index);
+ }
+ model->view->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
+}
+
+UiListSelection ui_listmodel_getselection(UiList *list) {
+ ListModel *model = (ListModel*)list->obj;
+ return ui_selection_model_to_selection(model->view->selectionModel());
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MODEL_H
+#define MODEL_H
+
+#include "toolkit.h"
+#include "../ui/tree.h"
+#include "../common/context.h"
+#include <QListView>
+#include <QTreeView>
+#include <QAbstractListModel>
+#include <QAbstractTableModel>
+#include <QAbstractItemModel>
+#include <QItemSelectionModel>
+
+
+
+class ListModel : public QAbstractListModel {
+ Q_OBJECT
+
+ ui_getvaluefunc2 getvalue;
+ void *getvaluedata;
+ ui_callback onactivate;
+ void *onactivatedata;
+ ui_callback onselection;
+ void *onselectiondata;
+
+public:
+ UiObject *obj;
+ UiVar *var;
+ QListView *view;
+
+ ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata);
+
+ void setActivationCallback(ui_callback f, void *userdata);
+ void setSelectionCallback(ui_callback f, void *userdata);
+
+ void update(int row);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+public slots:
+ void selectionChanged(
+ const QItemSelection & selected,
+ const QItemSelection & deselected);
+};
+
+class TableModel : public QAbstractListModel {
+ Q_OBJECT
+
+ UiModel *model;
+ ui_getvaluefunc2 getvalue;
+ void *getvaluedata;
+ ui_callback onactivate;
+ void *onactivatedata;
+ ui_callback onselection;
+ void *onselectiondata;
+
+public:
+ UiObject *obj;
+ UiVar *var;
+ QTreeView *view;
+
+ TableModel(UiObject *obj, QTreeView *view, UiVar *var, UiModel *model, ui_getvaluefunc2 getvalue, void *getvaluedata);
+
+ void setActivationCallback(ui_callback f, void *userdata);
+ void setSelectionCallback(ui_callback f, void *userdata);
+
+ void update(int row);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+public slots:
+ void selectionChanged(
+ const QItemSelection & selected,
+ const QItemSelection & deselected);
+};
+
+
+UiListSelection ui_selection_model_to_selection(QItemSelectionModel *model);
+
+extern "C" {
+
+ void ui_listmodel_update(UiList *list, int row);
+ void ui_listmodel_setselection(UiList *list, UiListSelection sel);
+ UiListSelection ui_listmodel_getselection(UiList *list);
+
+}
+
+#endif /* MODEL_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2012 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.
+#
+
+QT_SRC_DIR = ui/qt/
+QT_OBJPRE = $(OBJ_DIR)/$(QT_SRC_DIR)
+
+#QTOBJ =
+
+TOOLKITOBJS += $(QTOBJ:%=$(QT_OBJPRE)%)
+TOOLKITSOURCE += $(QTOBJ:%.o=qt/%.cpp)
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2025 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+TARGET = uitk
+TEMPLATE = lib
+CONFIG += staticlib warn_off debug
+DESTDIR = ../build/lib
+MOC_DIR = ../build/ui/qt
+OBJECTS_DIR = ../build/ui/qt
+
+QMAKE_CXXFLAGS += -I../ucx
+
+QT += core gui widgets
+
+DEFINES += UI_QT5
+
+SOURCES += toolkit.cpp
+SOURCES += window.cpp
+SOURCES += menu.cpp
+SOURCES += toolbar.cpp
+SOURCES += stock.cpp
+SOURCES += container.cpp
+SOURCES += text.cpp
+SOURCES += model.cpp
+SOURCES += list.cpp
+SOURCES += button.cpp
+SOURCES += label.cpp
+SOURCES += graphics.cpp
+SOURCES += widget.cpp
+SOURCES += entry.cpp
+SOURCES += image.cpp
+
+HEADERS += toolkit.h
+HEADERS += window.h
+HEADERS += menu.h
+HEADERS += toolbar.h
+HEADERS += stock.h
+HEADERS += container.h
+HEADERS += text.h
+HEADERS += model.h
+HEADERS += list.h
+HEADERS += button.h
+HEADERS += label.h
+HEADERS += graphics.h
+HEADERS += widget.h
+HEADERS += entry.h
+HEADERS += image.h
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 "stock.h"
+#include "../ui/properties.h"
+
+
+
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 STOCK_H
+#define STOCK_H
+
+#include "../ui/stock.h"
+
+
+
+#endif /* STOCK_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "text.h"
+#include "container.h"
+
+#include "../common/context.h"
+#include "../common/document.h"
+
+/*
+ * Gets or creates a QTextDocument for the UiText value and initializes it
+ * with the UiText string value
+ */
+static QTextDocument* get_or_create_doc(UiText *value) {
+ QTextDocument *document = nullptr;
+ if(value->data1) {
+ document = (QTextDocument*)value->data1;
+ } else {
+ document = new QTextDocument();
+ if(value->value.ptr) {
+ QString str = QString::fromUtf8(value->value.ptr);
+ document->setPlainText(str);
+ }
+ }
+
+ if(value->value.free) {
+ value->value.free(value->value.ptr);
+ }
+ value->value.ptr = NULL;
+ value->value.free = NULL;
+
+ return document;
+}
+
+UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QTextEdit *textarea = new QTextEdit();
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(textarea, layout);
+
+ QTextDocument *document = nullptr;
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiText *value = (UiText*)var->value;
+
+ document = get_or_create_doc(value);
+
+ value->save = ui_textarea_save;
+ value->restore = ui_textarea_restore;
+ value->destroy = ui_textarea_text_destroy;
+ value->get = ui_textarea_get;
+ value->set = ui_textarea_set;
+ value->getsubstr = ui_textarea_getsubstr;
+ value->insert = ui_textarea_insert;
+ value->setposition = ui_textarea_setposition;
+ value->position = ui_textarea_position;
+ value->setselection = ui_textarea_setselection;
+ value->selection = ui_textarea_selection;
+ value->length = ui_textarea_length;
+ value->remove = ui_textarea_remove;
+ value->obj = textarea;
+ value->data1 = document;
+ } else {
+ document = new QTextDocument();
+ }
+
+ textarea->setDocument(document);
+
+ return textarea;
+}
+
+void ui_textarea_save(UiText *text) {
+ // NOOP
+}
+
+void ui_textarea_restore(UiText *text) {
+ QTextEdit *textarea = (QTextEdit*)text->obj;
+ QTextDocument *document = get_or_create_doc(text);
+ textarea->setDocument(document);
+}
+
+void ui_textarea_text_destroy(UiText *text) {
+ QTextDocument *document = (QTextDocument*)text->data1;
+ if(document) {
+ delete document;
+ }
+}
+
+char* ui_textarea_get(UiText *text) {
+ // clean previous value
+ if(text->value.free) {
+ text->value.free(text->value.ptr);
+ }
+
+ // get string
+ QTextDocument *doc = (QTextDocument*)text->data1;
+ QString str = doc->toPlainText();
+ QByteArray array = str.toUtf8();
+ const char *cstr = array.constData();
+
+ // store a copy of the string in the UiText value
+ text->value.ptr = strdup(cstr);
+ text->value.free = free;
+ return text->value.ptr;
+}
+
+void ui_textarea_set(UiText *text, const char *str) {
+ if(text->value.free) {
+ text->value.free(text->value.ptr);
+ }
+ text->value.ptr = NULL;
+ text->value.free = NULL;
+
+ QTextDocument *doc = (QTextDocument*)text->data1;
+ QString qstr = QString::fromUtf8(str);
+ doc->setPlainText(qstr);
+}
+
+char* ui_textarea_getsubstr(UiText *text, int begin, int end) {
+ QTextDocument *doc = (QTextDocument*)text->data1;
+ QTextCursor cursor(doc);
+ cursor.setPosition(begin, QTextCursor::MoveAnchor);
+ cursor.setPosition(end, QTextCursor::KeepAnchor);
+ QString str = cursor.selectedText();
+ QByteArray bytes = str.toUtf8();
+ const char *cstr = bytes.constData();
+ return cstr ? strdup(cstr) : NULL;
+}
+
+void ui_textarea_insert(UiText *text, int pos, char *str) {
+ QTextDocument *doc = (QTextDocument*)text->data1;
+ QTextCursor cursor(doc);
+ cursor.setPosition(pos);
+ cursor.insertText(str);
+}
+
+void ui_textarea_setposition(UiText *text, int pos) {
+ QTextEdit *textview = (QTextEdit*)text->obj;
+ QTextCursor cursor = textview->textCursor();
+ cursor.setPosition(pos);
+ textview->setTextCursor(cursor);
+}
+
+int ui_textarea_position(UiText *text) {
+ QTextEdit *textview = (QTextEdit*)text->obj;
+ QTextCursor cursor = textview->textCursor();
+ return cursor.position();
+}
+
+void ui_textarea_selection(UiText *text, int *begin, int *end) {
+ QTextEdit *textview = (QTextEdit*)text->obj;
+ QTextCursor cursor = textview->textCursor();
+ if(cursor.hasSelection()) {
+ if(begin) {
+ *begin = cursor.selectionStart();
+ }
+ if(end) {
+ *end = cursor.selectionEnd();
+ }
+ }
+}
+
+void ui_textarea_setselection(UiText *text, int begin, int end) {
+ QTextEdit *textview = (QTextEdit*)text->obj;
+ QTextCursor cursor = textview->textCursor();
+ cursor.setPosition(begin, QTextCursor::MoveAnchor);
+ cursor.setPosition(end, QTextCursor::KeepAnchor);
+ textview->setTextCursor(cursor);
+}
+
+int ui_textarea_length(UiText *text) {
+ QTextDocument *doc = (QTextDocument*)text->data1;
+ return doc->characterCount();
+}
+
+void ui_textarea_remove(UiText *text, int begin, int end) {
+ QTextDocument *doc = (QTextDocument*)text->data1;
+ QTextCursor cursor(doc);
+ cursor.setPosition(begin);
+ cursor.setPosition(end, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+}
+
+/* ------------------------------ TextField ------------------------------ */
+
+static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, bool password, bool frameless) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QLineEdit *textfield = new QLineEdit();
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(textfield, layout);
+
+ if(password) {
+ textfield->setEchoMode(QLineEdit::Password);
+ }
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
+ if(var) {
+ UiString *value = (UiString*)var->value;
+ if(value->value.ptr) {
+ QString str = QString::fromUtf8(value->value.ptr);
+ textfield->setText(str);
+ if(value->value.free) {
+ value->value.free(value->value.ptr);
+ }
+ value->value.ptr = NULL;
+ }
+
+ value->set = ui_textfield_set;
+ value->get = ui_textfield_get;
+ value->obj = textfield;
+ }
+
+ return textfield;
+}
+
+UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) {
+ return create_textfield(obj, args, false, false);
+}
+
+UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs *args) {
+ return create_textfield(obj, args, false, true);
+}
+
+UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs *args) {
+ return create_textfield(obj, args, true, false);
+}
+
+char* ui_textfield_get(UiString *str) {
+ QLineEdit *textfield = (QLineEdit*)str->obj;
+ QString qstr = textfield->text();
+
+ if(str->value.free) {
+ str->value.free(str->value.ptr);
+ }
+ QByteArray array = qstr.toUtf8();
+ const char *cstr = array.constData();
+ str->value.ptr = strdup(cstr);
+ str->value.free = free;
+
+ return str->value.ptr;
+}
+
+void ui_textfield_set(UiString *str, const char *value) {
+ QLineEdit *textfield = (QLineEdit*)str->obj;
+ QString qstr = QString::fromUtf8(value);
+ textfield->setText(qstr);
+
+ if(str->value.free) {
+ str->value.free(str->value.ptr);
+ }
+ str->value.ptr = NULL;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TEXT_H
+#define TEXT_H
+
+#include "toolkit.h"
+#include "../ui/text.h"
+#include <QTextEdit>
+#include <QLineEdit>
+
+// value implementations
+extern "C" {
+
+void ui_textarea_save(UiText *text);
+void ui_textarea_restore(UiText *text);
+void ui_textarea_text_destroy(UiText *text);
+char* ui_textarea_get(UiText *text);
+void ui_textarea_set(UiText *text, const char *str);
+char* ui_textarea_getsubstr(UiText *text, int begin, int end);
+void ui_textarea_insert(UiText *text, int pos, char *str);
+void ui_textarea_setposition(UiText *text, int pos);
+int ui_textarea_position(UiText *text);
+void ui_textarea_setselection(UiText *text, int begin, int end);
+void ui_textarea_selection(UiText *text, int *begin, int *end);
+int ui_textarea_length(UiText *text);
+void ui_textarea_remove(UiText *text, int begin, int end);
+
+char* ui_textfield_get(UiString *str) ;
+void ui_textfield_set(UiString *str, const char *value);
+
+}
+
+
+
+#endif /* TEXT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 <inttypes.h>
+
+#include "toolbar.h"
+#include "menu.h"
+#include "stock.h"
+
+static void add_items(UiObject *obj, QToolBar *toolbar, CxList *defaults, CxMap *items);
+static void create_item(UiObject *obj, QToolBar *toolbar, UiToolbarItemI *i);
+
+QToolBar* ui_create_toolbar(UiObject *obj) {
+ 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);
+
+ if(!items || cxMapSize(items) == 0) {
+ return nullptr;
+ }
+
+ QToolBar *toolbar = new QToolBar();
+ add_items(obj, toolbar, left_defaults, items);
+ add_items(obj, toolbar, center_defaults, items);
+ add_items(obj, toolbar, right_defaults, items);
+
+
+ return toolbar;
+}
+
+static void add_items(UiObject *obj, QToolBar *toolbar, CxList *defaults, CxMap *items) {
+ CxIterator i = cxListIterator(defaults);
+ cx_foreach(char *, name, i) {
+ UiToolbarItemI *item = (UiToolbarItemI*)cxMapGet(items, name);
+ if(item) {
+ create_item(obj, toolbar, item);
+ } else {
+ fprintf(stderr, "UI Error: unknown toolbar item '%s'\n", name);
+ }
+ }
+}
+
+static void create_item(UiObject *obj, QToolBar *toolbar, UiToolbarItemI *i) {
+ switch(i->type) {
+ case UI_TOOLBAR_ITEM: {
+ ui_toolbar_add_item(obj, toolbar, (UiToolbarItem*)i);
+ break;
+ }
+ case UI_TOOLBAR_TOGGLEITEM: {
+ ui_toolbar_add_toggleitem(obj, toolbar, (UiToolbarToggleItem*)i);
+ break;
+ }
+ case UI_TOOLBAR_MENU: {
+ ui_toolbar_add_menu(obj, toolbar, (UiToolbarMenuItem*)i);
+ break;
+ }
+ default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type);
+ }
+}
+
+void ui_toolbar_add_item(UiObject *obj, QToolBar *toolbar, UiToolbarItem *item) {
+ QAction *action = new QAction();
+ if(item->args.label) {
+ action->setText(item->args.label);
+ }
+ if(item->args.icon) {
+ action->setIcon(QIcon::fromTheme(item->args.icon));
+ }
+ toolbar->addAction(action);
+
+ UiEventWrapper *event = new UiEventWrapper(obj, item->args.onclick, item->args.onclickdata);
+ action->connect(action, SIGNAL(triggered()), event, SLOT(slot()));
+ action->connect(action, SIGNAL(destroyed()), event, SLOT(destroy()));
+}
+
+static void toolbar_togglebutton_event(UiEvent *event, UiEventWrapper *wrapper) {
+ QAction *action = (QAction*)wrapper->customdata1;
+ event->intval = action->isChecked();
+ if(wrapper->var) {
+ event->eventdata = wrapper->var->value;
+ event->eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;
+ }
+}
+
+void ui_toolbar_add_toggleitem(UiObject *obj, QToolBar *toolbar, UiToolbarToggleItem *item) {
+ QAction *action = new QAction();
+ action->setCheckable(true);
+ if(item->args.label) {
+ action->setText(item->args.label);
+ }
+ if(item->args.icon) {
+ action->setIcon(QIcon::fromTheme(item->args.icon));
+ }
+ toolbar->addAction(action);
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, nullptr, item->args.varname, UI_VAR_INTEGER);
+ UiEventWrapper *event = new UiEventWrapper(obj, item->args.onchange, item->args.onchangedata);
+ event->var = var;
+ event->customdata1 = action;
+ event->prepare_event = toolbar_togglebutton_event;
+ action->connect(action, SIGNAL(triggered()), event, SLOT(slot()));
+ action->connect(action, SIGNAL(destroyed()), event, SLOT(destroy()));
+}
+
+void ui_toolbar_add_menu(UiObject *obj, QToolBar *toolbar, UiToolbarMenuItem *item) {
+
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 TOOLBAR_H
+#define TOOLBAR_H
+
+#include "toolkit.h"
+#include "../ui/toolbar.h"
+#include "../common/toolbar.h"
+#include <QToolBar>
+
+QToolBar* ui_create_toolbar(UiObject *obj);
+
+void ui_toolbar_add_item(UiObject *obj, QToolBar *toolbar, UiToolbarItem *item);
+void ui_toolbar_add_toggleitem(UiObject *obj, QToolBar *toolbar, UiToolbarToggleItem *item);
+void ui_toolbar_add_menu(UiObject *obj, QToolBar *toolbar, UiToolbarMenuItem *item);
+
+#endif /* TOOLBAR_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "toolkit.h"
+#include "window.h"
+#include "stock.h"
+
+#include "../common/document.h"
+#include "../common/properties.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
+
+static const char *application_name;
+
+static ui_callback startup_func;
+static void *startup_data;
+static ui_callback open_func;
+static void *open_data;
+static ui_callback exit_func;
+static void *exit_data;
+
+static int is_toplevel_realized = 0;
+
+static int app_argc;
+static char **app_argv;
+static QApplication *application = NULL;
+
+static UiBool exit_on_shutdown;
+
+void ui_init(const char *appname, int argc, char **argv) {
+ application_name = appname;
+
+ app_argc = argc;
+ app_argv = argv;
+ application = new QApplication(app_argc, app_argv);
+
+ uic_menu_init();
+ uic_toolbar_init();
+
+ uic_load_app_properties();
+
+}
+
+const char* ui_appname() {
+ return application_name;
+}
+
+void ui_onstartup(ui_callback f, void *userdata) {
+ startup_func = f;
+ startup_data = userdata;
+}
+
+void ui_onopen(ui_callback f, void *userdata) {
+ open_func = f;
+ open_data = userdata;
+}
+
+void ui_onexit(ui_callback f, void *userdata) {
+ exit_func = f;
+ exit_data = userdata;
+}
+
+void ui_app_exit_on_shutdown(UiBool exitapp) {
+ exit_on_shutdown = exitapp;
+}
+
+void ui_main() {
+ if(startup_func) {
+ startup_func(NULL, startup_data);
+ }
+ application->exec();
+ if(exit_func) {
+ exit_func(NULL, exit_data);
+ }
+ uic_store_app_properties();
+
+ delete application;
+
+ if(exit_on_shutdown) {
+ exit(0);
+ }
+}
+
+void ui_show(UiObject *obj) {
+ obj->widget->show();
+}
+
+void ui_close(UiObject *obj) {
+ QMainWindow *window = (QMainWindow*)obj->widget;
+ window->close();
+}
+
+void ui_set_enabled(UIWIDGET widget, int enabled) {
+
+}
+
+void ui_set_visible(UIWIDGET widget, int visible) {
+
+}
+
+
+
+/* --------------------- Implemtation UiEventWrapper --------------------- */
+
+UiEventWrapper::UiEventWrapper(UiObject *obj, ui_callback f, void* userdata) {
+ this->obj = obj;
+ this->callback = f;
+ this->userdata = userdata;
+}
+
+void UiEventWrapper::slot() {
+ if(!callback) {
+ return;
+ }
+
+ UiEvent e;
+ e.obj = obj;
+ e.window = obj->window;
+ e.document = obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = 0;
+ e.set = ui_get_setop();
+ if(prepare_event) {
+ prepare_event(&e, this);
+ }
+ callback(&e, userdata);
+
+ // TODO: notify var observers
+}
+
+void UiEventWrapper::destroy() {
+ delete this;
+}
+
+
+/* --------------------- Implemtation UiAction --------------------- */
+
+UiAction::UiAction(UiObject *obj, QString &label, ui_callback f, void *userdata) : QAction(label, NULL) {
+ this->obj = obj;
+ this->callback = f;
+ this->userdata = userdata;
+}
+
+UiAction::~UiAction() {
+ // TODO: unbind var
+}
+
+void UiAction::trigger() {
+ if(!callback) {
+ return;
+ }
+
+ UiEvent e;
+ e.obj = obj;
+ e.window = obj->window;
+ e.document = obj->ctx->document;
+ e.eventdata = NULL;
+ e.eventdatatype = 0;
+ e.intval = 0;
+ e.set = ui_get_setop();
+ if(prepare_event) {
+ prepare_event(&e, this);
+ }
+ callback(&e, userdata);
+
+ // TODO: notify var observers
+}
+
+// ui_enablefunc for UiAction
+void ui_action_enable(UiAction *action, int enable) {
+ action->setEnabled((bool)enable);
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 TOOLKIT_H
+#define TOOLKIT_H
+
+#include "../ui/toolkit.h"
+#include "../common/context.h"
+#include "../common/object.h"
+
+#include <QApplication>
+
+class UiEventWrapper;
+
+typedef void (*ui_prepare_event_func)(UiEvent *event, UiEventWrapper *wrapper);
+
+class UiEventWrapper : public QObject {
+ Q_OBJECT
+
+ UiObject *obj;
+ ui_callback callback;
+ void *userdata;
+
+public:
+ UiVar *var;
+
+ void *customdata1 = nullptr;
+ void *customdata2 = nullptr;
+ int customvalue1 = 0;
+ int customvalue2 = 0;
+
+ ui_prepare_event_func prepare_event = nullptr;
+
+ UiEventWrapper(UiObject *obj, ui_callback f, void *userdata);
+
+public slots:
+ void slot();
+ void destroy();
+};
+
+class UiAction;
+
+typedef void (*ui_prepare_action_event_func)(UiEvent *event, UiAction *action);
+
+class UiAction : public QAction {
+ Q_OBJECT
+
+ UiObject *obj;
+ ui_callback callback;
+ void *userdata;
+
+public:
+ UiVar *var;
+
+ ui_prepare_action_event_func prepare_event = nullptr;
+ void *customdata1 = nullptr;
+ void *customdata2 = nullptr;
+ int customvalue1 = 0;
+ int customvalue2 = 0;
+
+ UiAction(UiObject *obj, QString &label, ui_callback f, void *userdata);
+ ~UiAction();
+
+private slots:
+ void trigger();
+};
+
+void ui_action_enable(UiAction *action, int enable);
+
+#endif /* TOOLKIT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "widget.h"
+
+#include "container.h"
+#include "../common/context.h"
+
+UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
+ UIWIDGET widget = create_widget(obj, args, userdata);
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(widget, layout);
+ return widget;
+}
+
+UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
+ QFrame *separator = new QFrame();
+ separator->setFrameShape(QFrame::HLine);
+ separator->setFrameShadow(QFrame::Sunken);
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(separator, layout);
+
+ return separator;
+}
+
+void ui_set_enabled(UIWIDGET widget, int enabled) {
+ widget->setEnabled(enabled);
+}
+
+void ui_set_visible(UIWIDGET widget, int visible) {
+ widget->setVisible(visible);
+}
+
+void ui_widget_set_size(UIWIDGET w, int width, int height) {
+ w->resize(width >= 0 ? width : w->width(), height >= 0 ? height : w->height());
+}
+
+void ui_widget_redraw(UIWIDGET w) {
+ w->repaint();
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include "toolkit.h"
+
+#include "../ui/widget.h"
+
+
+#endif /* WIDGET_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <cx/mempool.h>
+#include "../common/context.h"
+#include "../common/object.h"
+
+#include "window.h"
+#include "menu.h"
+#include "toolbar.h"
+#include "container.h"
+
+#include <QVBoxLayout>
+#include <QFileDialog>
+#include <QPushButton>
+#include <QDockWidget>
+#include <QMessageBox>
+
+static UiObject* create_window(const char *title, void *window_data, bool simple, bool sidebar = false) {
+ UiObject *obj = uic_object_new_toplevel();
+ obj->window = window_data;
+ obj->next = NULL;
+
+ QMainWindow *window = new QMainWindow();
+ window->setWindowTitle(title);
+ obj->widget = window;
+
+ if(!simple) {
+ ui_add_menus(obj, window);
+ QToolBar *toolbar = ui_create_toolbar(obj);
+ if(toolbar) {
+ window->addToolBar(Qt::TopToolBarArea, toolbar);
+ }
+ }
+
+ QBoxLayout *box = new QVBoxLayout();
+ QWidget *boxWidget = new QWidget();
+ boxWidget->setLayout(box);
+ window->setCentralWidget(boxWidget);
+ ui_obj_add_container(obj, new UiBoxContainer(box));
+ if(sidebar) {
+ QDockWidget *dock = new QDockWidget();
+ window->addDockWidget(Qt::LeftDockWidgetArea, dock);
+ window->setProperty("ui_sidebar", QVariant::fromValue((void*)dock));
+ }
+
+ obj->widget = window;
+ return obj;
+}
+
+UiObject* ui_window(const char *title, void *window_data) {
+ return create_window(title, window_data, false);
+}
+
+UiObject* ui_simplewindow(char *title, void *window_data) {
+ return create_window(title, window_data, true);
+}
+
+UiObject *ui_sidebar_window(const char *title, void *window_data) {
+ return create_window(title, window_data, false, true);
+}
+
+void ui_dialog_create(UiObject *parent, UiDialogArgs *args) {
+ if(args->input || args->password) {
+ // TODO: QInputDialog
+ } else {
+ QMessageBox msgBox;
+ if(args->title) {
+ msgBox.setWindowTitle(args->title);
+ }
+ if(args->content) {
+ msgBox.setText(args->content);
+ }
+ QPushButton *btn1;
+ QPushButton *btn2;
+ if(args->button1_label) {
+ btn1 = msgBox.addButton(args->button1_label, QMessageBox::ActionRole);
+ }
+ if(args->button2_label) {
+ btn2 = msgBox.addButton(args->button2_label, QMessageBox::ActionRole);
+ }
+ if(args->closebutton_label) {
+ msgBox.addButton(args->closebutton_label, QMessageBox::DestructiveRole);
+ }
+
+ msgBox.exec();
+
+ UiEvent evt;
+ evt.obj = parent;
+ evt.document = evt.obj->ctx->document;
+ evt.window = evt.obj->window;
+ evt.eventdata = NULL;
+ evt.eventdatatype = 0;
+ evt.intval = 0;
+ if(msgBox.clickedButton() == btn1) {
+ evt.intval = 1;
+ } else if(msgBox.clickedButton() == btn2) {
+ evt.intval = 2;
+ }
+ if(args->result) {
+ args->result(&evt, args->resultdata);
+ }
+ }
+}
+
+char* ui_openfiledialog(UiObject *obj) {
+ QString fileName = QFileDialog::getOpenFileName(obj->widget);
+ if(fileName.size() > 0) {
+ QByteArray array = fileName.toLocal8Bit();
+ const char *cstr = array.constData();
+ return strdup(cstr);
+ } else {
+ return NULL;
+ }
+}
+
+char* ui_savefiledialog(UiObject *obj) {
+ QString fileName = QFileDialog::getSaveFileName(obj->widget);
+ if(fileName.size() > 0) {
+ QByteArray array = fileName.toLocal8Bit();
+ const char *cstr = array.constData();
+ return strdup(cstr);
+ } else {
+ return NULL;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 WINDOW_H
+#define WINDOW_H
+
+#include "../ui/window.h"
+
+#include <QMainWindow>
+
+
+
+#endif /* WINDOW_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_BUTTON_H
+#define UI_BUTTON_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum UiLinkType {
+ UI_LINK_TEXT = 0,
+ UI_LINK_BUTTON
+};
+typedef enum UiLinkType UiLinkType;
+
+typedef struct UiButtonArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ const char *label;
+ const char *icon;
+ const char *tooltip;
+ UiLabelType labeltype;
+ ui_callback onclick;
+ void *onclickdata;
+
+ const int *groups;
+} UiButtonArgs;
+
+typedef struct UiToggleArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ const char *label;
+ const char *icon;
+ const char *tooltip;
+ UiLabelType labeltype;
+ UiInteger *value;
+ const char *varname;
+ ui_callback onchange;
+ void *onchangedata;
+ int enable_group;
+
+ const int *groups;
+} UiToggleArgs;
+
+typedef struct UiLinkButtonArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ const char *label;
+ const char *uri;
+ UiString *value;
+ const char *varname;
+ ui_callback onclick;
+ void *onclickdata;
+ UiBool nofollow;
+ UiLinkType type;
+
+ const int *groups;
+} UiLinkButtonArgs;
+
+#define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } )
+#define ui_togglebutton(obj, ...) ui_togglebutton_create(obj, &(UiToggleArgs){ __VA_ARGS__ } )
+#define ui_checkbox(obj, ...) ui_checkbox_create(obj, &(UiToggleArgs){ __VA_ARGS__ } )
+#define ui_switch(obj, ...) ui_switch_create(obj, &(UiToggleArgs){ __VA_ARGS__ } )
+#define ui_radiobutton(obj, ...) ui_radiobutton_create(obj, &(UiToggleArgs){ __VA_ARGS__ } )
+#define ui_linkbutton(obj, ...) ui_linkbutton_create(obj, &(UiLinkButtonArgs){ __VA_ARGS__ })
+
+UIEXPORT UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args);
+UIEXPORT UIWIDGET ui_togglebutton_create(UiObject *obj, UiToggleArgs *args);
+UIEXPORT UIWIDGET ui_checkbox_create(UiObject *obj, UiToggleArgs *args);
+UIEXPORT UIWIDGET ui_switch_create(UiObject *obj, UiToggleArgs *args);
+UIEXPORT UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args);
+UIEXPORT UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args);
+
+UIEXPORT void ui_button_set_label(UIWIDGET button, const char *label);
+UIEXPORT void ui_button_set_icon(UIWIDGET button, const char *icon);
+
+UIEXPORT void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri);
+UIEXPORT void ui_linkbutton_value_set_label(UiString *str, const char *label);
+UIEXPORT void ui_linkbutton_value_set_uri(UiString *str, const char *uri);
+UIEXPORT void ui_linkbutton_value_set_visited(UiString *str, UiBool visited);
+
+UIEXPORT void ui_linkbutton_set_label(UIWIDGET button, const char *label);
+UIEXPORT void ui_linkbutton_set_uri(UIWIDGET button, const char *label);
+UIEXPORT void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited);
+UIEXPORT char* ui_linkbutton_get_label(UIWIDGET button);
+UIEXPORT char* ui_linkbutton_get_uri(UIWIDGET button);
+UIEXPORT UiBool ui_linkbutton_get_visited(UIWIDGET button);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_BUTTON_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_CONTAINER_H
+#define UI_CONTAINER_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum UiSubContainerType {
+ UI_CONTAINER_VBOX = 0,
+ UI_CONTAINER_HBOX,
+ UI_CONTAINER_GRID,
+ UI_CONTAINER_NO_SUB
+} UiSubContainerType;
+
+typedef enum UiTabViewType {
+ UI_TABVIEW_DEFAULT = 0,
+ UI_TABVIEW_DOC,
+ UI_TABVIEW_NAVIGATION_SIDE,
+ UI_TABVIEW_NAVIGATION_TOP,
+ UI_TABVIEW_NAVIGATION_TOP2,
+ UI_TABVIEW_INVISIBLE
+} UiTabViewType;
+
+typedef enum UiHeaderbarAlternative {
+ UI_HEADERBAR_ALTERNATIVE_DEFAULT = 0,
+ UI_HEADERBAR_ALTERNATIVE_TOOLBAR,
+ UI_HEADERBAR_ALTERNATIVE_BOX
+} UiHeaderbarAlternative;
+
+
+typedef struct UiContainerArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ int spacing;
+ int columnspacing;
+ int rowspacing;
+ UiBool def_hfill;
+ UiBool def_vfill;
+ UiBool def_hexpand;
+ UiBool def_vexpand;
+} UiContainerArgs;
+
+typedef struct UiFrameArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ UiSubContainerType subcontainer;
+
+ int padding;
+ int spacing;
+ int columnspacing;
+ int rowspacing;
+
+ const char* label;
+ UiBool isexpanded;
+} UiFrameArgs;
+
+typedef struct UiTabViewArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ UiTabViewType tabview;
+ ui_callback onchange;
+ void *onchangedata;
+
+ UiSubContainerType subcontainer;
+
+ UiInteger *value;
+ const char* varname;
+
+ int padding;
+ int spacing;
+ int columnspacing;
+ int rowspacing;
+} UiTabViewArgs;
+
+typedef struct UiHeaderbarArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ UiBool showtitle;
+ UiBool showwindowbuttons;
+
+ UiHeaderbarAlternative alternative;
+ int alt_spacing;
+} UiHeaderbarArgs;
+
+typedef struct UiSidebarArgs {
+ const char *name;
+ const char *style_class;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int spacing;
+} UiSidebarArgs;
+
+typedef struct UiSplitPaneArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ int spacing;
+ int columnspacing;
+ int rowspacing;
+
+ int initial_position;
+ const char *position_property;
+ UiInteger *value;
+ const char* varname;
+ int max_panes;
+} UiSplitPaneArgs;
+
+typedef struct UiItemListContainerArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ int spacing;
+
+ int sub_margin;
+ int sub_spacing;
+ int sub_columnspacing;
+ int sub_rowspacing;
+
+ UiList *value;
+ const char *varname;
+ /*
+ * void create_ui(UiObject *obj, int index, void *elm, void *userdata)
+ *
+ * UI constructor for each list element
+ *
+ * This callback is executed for each list element. A new UiObject is
+ * created for every item.
+ */
+ void (*create_ui)(UiObject *, int, void *, void *);
+ void *userdata;
+
+ /*
+ * ItemList container type
+ * Only UI_CONTAINER_VBOX or UI_CONTAINER_HBOX are supported
+ */
+ UiSubContainerType container;
+
+ /*
+ * item root container
+ */
+ UiSubContainerType subcontainer;
+} UiItemListContainerArgs;
+
+
+typedef struct UiLayout UiLayout;
+struct UiLayout {
+ UiBool fill;
+ char *label;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+};
+
+struct UiContainerX {
+ void *container;
+ UiBool close;
+ UiBool newline;
+ UiContainerX *prev;
+ UiContainerX *next;
+};
+
+
+#define UI_CTN(obj, ctn) for(ctn;ui_container_finish(obj);ui_container_begin_close(obj))
+
+#define ui_vbox(obj, ...) for(ui_vbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_hbox(obj, ...) for(ui_hbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_grid(obj, ...) for(ui_grid_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_frame(obj, ...) for(ui_frame_create(obj, &(UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_expander(obj, ...) for(ui_expander_create(obj, &(UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_scrolledwindow(obj, ...) for(ui_scrolledwindow_create(obj, &(UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_tabview(obj, ...) for(ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_headerbar(obj, ...) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_sidebar(obj, ...) for(ui_sidebar_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_left_panel(obj, ...) for(ui_left_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_right_panel(ob, ...) for(ui_right_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+
+#define ui_vbox0(obj) for(ui_vbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_hbox0(obj) for(ui_hbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_grid0(obj) for(ui_grid_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_frame0(obj) for(ui_frame_create(obj, &(UiFrameArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_expander0(obj) for(ui_expande_create(obj, &(UiFrameArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_scrolledwindow0(obj) for(ui_scrolledwindow_create(obj, &(UiFrameArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_tabview0(obj) for(ui_tabview_create(obj, &(UiTabViewArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_headerbar0(obj) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_sidebar0(obj) for(ui_sidebar_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_left_panel0(obj) for(ui_left_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_right_panel0(obj) for(ui_right_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+
+
+#define ui_vbox_w(obj, w, ...) for(w = ui_vbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_hbox_w(obj, w, ...) for(w = ui_hbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_grid_w(obj, w, ...) for(w = ui_grid_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_tabview_w(obj, w, ...) for(w = ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_scrolledwindow_w(obj, w, ...) for(w = ui_scrolledwindow_create(obj, &(UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+
+#define ui_hsplitpane(obj, ...) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_vsplitpane(obj, ...) for(ui_vsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_hsplitpane0(obj) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_vsplitpane0(obj) for(ui_vsplitpane_create(obj, &(UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+
+#define ui_hsplitpane_w(obj, w, ...) for(w = ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_vsplitpane_w(obj, w, ...) for(w = ui_vsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+
+#define ui_tab(obj, label) for(ui_tab_create(obj, label);ui_container_finish(obj);ui_container_begin_close(obj))
+
+#define ui_headerbar_start(obj) for(ui_headerbar_start_create(obj);ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_headerbar_center(obj) for(ui_headerbar_center_create(obj);ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_headerbar_end(obj) for(ui_headerbar_end_create(obj);ui_container_finish(obj);ui_container_begin_close(obj))
+
+#define ui_itemlist(obj, ...) ui_itemlist_create(obj, &(UiItemListContainerArgs) { __VA_ARGS__} )
+
+UIEXPORT void ui_end(UiObject *obj); // deprecated
+UIEXPORT void ui_end_new(UiObject *obj); // TODO: rename to ui_end
+
+UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args);
+UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args);
+UIEXPORT UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args);
+UIEXPORT UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args);
+UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args);
+UIEXPORT UIWIDGET ui_scrolledwindow_create(UiObject *obj, UiFrameArgs *args);
+
+UIEXPORT UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args);
+UIEXPORT void ui_tab_create(UiObject *obj, const char* title);
+UIEXPORT void ui_tabview_select(UIWIDGET tabview, int tab);
+UIEXPORT void ui_tabview_remove(UIWIDGET tabview, int tab);
+UIEXPORT UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index);
+
+UIEXPORT UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args);
+UIEXPORT void ui_headerbar_start_create(UiObject *obj);
+UIEXPORT void ui_headerbar_center_create(UiObject *obj);
+UIEXPORT void ui_headerbar_end_create(UiObject *obj);
+
+UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args);
+UIEXPORT UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args);
+UIEXPORT UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args);
+
+UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args);
+
+UIEXPORT UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs *args);
+UIEXPORT UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args);
+
+UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible);
+
+UIEXPORT void ui_newline(UiObject *obj);
+
+// TODO
+UIEXPORT UiTabbedPane* ui_tabbed_document_view(UiObject *obj);
+UIEXPORT UiObject* ui_document_tab(UiTabbedPane *view);
+
+
+
+/* used for macro */
+UIEXPORT void ui_container_begin_close(UiObject *obj);
+UIEXPORT int ui_container_finish(UiObject *obj);
+
+#define UI_APPLY_LAYOUT1(obj, args) \
+ if(args.fill) ui_layout_fill(obj, 1); \
+ if(args.hexpand) ui_layout_hexpand(obj, 1); \
+ if(args.vexpand) ui_layout_vexpand(obj, 1); \
+ if(args.hfill) ui_layout_hfill(obj, 1); \
+ if(args.vfill) ui_layout_vfill(obj, 1); \
+ if(args.override_defaults) ui_layout_override_defaults(obj, 1); \
+ if(args.colspan > 0) ui_layout_colspan(obj, args.colspan); \
+ if(args.rowspan > 0) ui_layout_rowspan(obj, args.rowspan); \
+ /*force caller to add ';'*/(void)0
+
+#define UI_APPLY_LAYOUT2(obj, args) \
+ if(args->fill) ui_layout_fill(obj, 1); \
+ if(args->hexpand) ui_layout_hexpand(obj, 1); \
+ if(args->vexpand) ui_layout_vexpand(obj, 1); \
+ if(args->hfill) ui_layout_hfill(obj, 1); \
+ if(args->vfill) ui_layout_vfill(obj, 1); \
+ if(args->override_defaults) ui_layout_override_defaults(obj, 1); \
+ if(args->colspan > 0) ui_layout_colspan(obj, args->colspan); \
+ if(args->rowspan > 0) ui_layout_rowspan(obj, args->rowspan); \
+ /*force caller to add ';'*/(void)0
+
+#define UI_ARGS2LAYOUT(args) { \
+ .fill = args->fill, \
+ .hexpand = args->hexpand, \
+ .vexpand = args->vexpand, \
+ .hfill = args->hfill, \
+ .vfill = args->vfill, \
+ .override_defaults = args->override_defaults, \
+ .margin = args->margin, \
+ .margin_left = args->margin_left, \
+ .margin_right = args->margin_right, \
+ .margin_top = args->margin_top, \
+ .margin_bottom = args->margin_bottom, \
+ .colspan = args->colspan, \
+ .rowspan = args->rowspan }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_CONTAINER_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * display widgets without user input
+ */
+
+#ifndef UI_DISPLAY_H
+#define UI_DISPLAY_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+enum UiLabelStyle {
+ UI_LABEL_STYLE_DEFAULT = 0,
+ UI_LABEL_STYLE_TITLE,
+ UI_LABEL_STYLE_SUBTITLE,
+ UI_LABEL_STYLE_DIM
+};
+
+typedef enum UiLabelStyle UiLabelStyle;
+
+typedef struct UiLabelArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ const char* label;
+ UiAlignment align;
+ UiLabelStyle style;
+ UiString* value;
+ const char* varname;
+} UiLabelArgs;
+
+typedef struct UiProgressbarArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ int width;
+ const char *name;
+ const char *style_class;
+
+ double min;
+ double max;
+ UiDouble* value;
+ const char* varname;
+} UiProgressbarArgs;
+
+typedef struct UiProgressbarSpinnerArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ UiInteger* value;
+ const char* varname;
+} UiProgressbarSpinnerArgs;
+
+/* label widgets */
+
+#define ui_label(obj, ...) ui_label_create(obj, &(UiLabelArgs) { __VA_ARGS__ })
+#define ui_llabel(obj, ...) ui_llabel_create(obj, &(UiLabelArgs) { __VA_ARGS__ })
+#define ui_rlabel(obj, ...) ui_rlabel_create(obj, &(UiLabelArgs) { __VA_ARGS__ })
+
+
+UIEXPORT UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs *args);
+UIEXPORT UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs *args);
+UIEXPORT UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs *args);
+
+UIWIDGET ui_space_deprecated(UiObject *obj);
+
+/* progress bar/spinner */
+
+#define ui_progressbar(obj, ...) ui_progressbar_create(obj, &(UiProgressbarArgs) { __VA_ARGS__ } )
+#define ui_progressspinner(obj, ...) ui_progressspinner_create(obj, &(UiProgressbarSpinnerArgs) { __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args);
+UIEXPORT UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_DISPLAY_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_DND_H
+#define UI_DND_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_DND_FILE_TARGET "XdndDirectSave0"
+
+UIEXPORT void ui_selection_settext(UiDnD *sel, char *str, int len);
+UIEXPORT void ui_selection_seturis(UiDnD *sel, char **uris, int nelm);
+
+UIEXPORT char* ui_selection_gettext(UiDnD *sel);
+UIEXPORT UiFileList ui_selection_geturis(UiDnD *sel);
+
+UIEXPORT UiDnDAction ui_dnd_result(UiDnD *dnd);
+UIEXPORT UiBool ui_dnd_need_delete(UiDnD *dnd);
+
+UIEXPORT void ui_dnd_accept(UiDnD *dnd, UiBool accept);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_DND_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_ENTRY_H
+#define UI_ENTRY_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct UiSpinBoxArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ int width;
+ const char *name;
+ const char *style_class;
+
+ double step;
+ int digits;
+ double min;
+ double max;
+ UiInteger *intvalue;
+ UiDouble* doublevalue;
+ UiRange *rangevalue;
+ const char* varname;
+ ui_callback onchange;
+ void* onchangedata;
+
+ const int *groups;
+} UiSpinBoxArgs;
+
+
+
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args);
+
+#define ui_spinbox(obj, ...) ui_spinbox_create(obj, &(UiSpinBoxArgs){ __VA_ARGS__ } )
+
+void ui_spinner_setrange(UIWIDGET spinner, double min, double max);
+void ui_spinner_setdigits(UIWIDGET spinner, int digits);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_ENTRY_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_GRAPHICS_H
+#define UI_GRAPHICS_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiGraphics UiGraphics;
+typedef struct UiTextLayout UiTextLayout;
+
+typedef void(*ui_drawfunc)(UiEvent*, UiGraphics*, void*);
+
+struct UiGraphics {
+ int width;
+ int height;
+};
+
+typedef struct UiDrawingAreaArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ int width;
+ int height;
+ ui_drawfunc draw;
+ void *drawdata;
+ ui_callback onclick;
+ void *onclickdata;
+ ui_callback onmotion;
+ void *onmotiondata;
+} UiDrawingAreaArgs;
+
+#define ui_drawingarea(obj, ...) ui_drawingarea_create(obj, &(UiDrawingAreaArgs) { __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args);
+UIEXPORT void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height);
+UIEXPORT void ui_drawingarea_redraw(UIWIDGET drawingarea);
+
+// text layout
+UIEXPORT UiTextLayout* ui_text(UiGraphics *g);
+UIEXPORT void ui_text_free(UiTextLayout *text);
+UIEXPORT void ui_text_setstring(UiTextLayout *layout, char *str);
+UIEXPORT void ui_text_setstringl(UiTextLayout *layout, char *str, int len);
+UIEXPORT void ui_text_setfont(UiTextLayout *layout, const char *font, int size);
+UIEXPORT void ui_text_getsize(UiTextLayout *layout, int *width, int *height);
+UIEXPORT void ui_text_setwidth(UiTextLayout *layout, int width);
+
+// drawing functions
+UIEXPORT void ui_graphics_color(UiGraphics *g, int red, int green, int blue);
+UIEXPORT void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2);
+UIEXPORT void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, UiBool fill);
+UIEXPORT void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_GRAPHICS_H */
+
--- /dev/null
+/*
+ * 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 UI_ICONS_H
+#define UI_ICONS_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef UI_GTK
+
+#define UI_ICON_HOME "go-home"
+#define UI_ICON_NEW_WINDOW "list-add"
+#define UI_ICON_REFRESH "view-refresh"
+#define UI_ICON_NEW_FOLDER "folder-new"
+#define UI_ICON_ADD "document-new"
+#define UI_ICON_UPLOAD "document-open"
+#define UI_ICON_SAVE_LOCAL "document-save-as"
+#define UI_ICON_DELETE "edit-delete"
+#define UI_ICON_DOCK_LEFT ""
+#define UI_ICON_DOCK_RIGHT ""
+#define UI_ICON_GO_BACK "go-previous"
+#define UI_ICON_GO_FORWARD "go-next"
+#define UI_ICON_GO_UP "go-up"
+#define UI_ICON_GO_DOWN "go-down"
+
+#endif /* UI_GTK */
+
+
+
+#ifdef UI_WINUI
+
+#define UI_ICON_HOME "Home"
+#define UI_ICON_NEW_WINDOW "NewWindow"
+#define UI_ICON_REFRESH "Refresh"
+#define UI_ICON_NEW_FOLDER "NewFolder"
+#define UI_ICON_ADD "Add"
+#define UI_ICON_UPLOAD "Upload"
+#define UI_ICON_SAVE_LOCAL "SaveLocal"
+#define UI_ICON_DELETE "Delete"
+#define UI_ICON_DOCK_LEFT "DockLeft"
+#define UI_ICON_DOCK_RIGHT "DockRight"
+#define UI_ICON_GO_BACK "Back"
+#define UI_ICON_GO_FORWARD "Forward"
+#define UI_ICON_GO_UP "Up"
+#define UI_ICON_GO_DOWN "" // TODO: implement workaround for missing down symbol
+
+#endif /* UI_WINUI */
+
+
+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);
+
+UIEXPORT UiIcon* ui_foldericon(size_t size);
+UIEXPORT UiIcon* ui_fileicon(size_t size);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_ICONS_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_IMAGE_H
+#define UI_IMAGE_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_IMAGE_OBJECT_TYPE "image"
+
+#ifdef UI_GTK
+#define UIIMAGE GdkPixbuf*
+#else
+#define UIIMAGE void*
+#endif
+
+
+typedef struct UiImageViewerArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ UiBool scrollarea;
+ UiBool autoscale;
+ UiBool adjustwidgetsize;
+ UiBool useradjustable;
+ int image_padding;
+ int image_padding_left;
+ int image_padding_right;
+ int image_padding_top;
+ int image_padding_bottom;
+ UiGeneric *value;
+ const char *varname;
+ UiMenuBuilder *contextmenu;
+
+ ui_callback onbuttonpress;
+ void *onbuttonpressdata;
+ ui_callback onbuttonrelease;
+ void *onbuttonreleasedata;
+} UiImageViewerArgs;
+
+#define ui_imageviewer(obj, ...) ui_imageviewer_create(obj, &(UiImageViewerArgs){ __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args);
+
+UIEXPORT UIWIDGET ui_imageviewer_reset(UIWIDGET w);
+UIEXPORT UIWIDGET ui_imageviewer_set_autoscale(UIWIDGET w, UiBool set);
+UIEXPORT UIWIDGET ui_imageviewer_set_adjustwidgetsize(UIWIDGET w, UiBool set);
+UIEXPORT UIWIDGET ui_imageviewer_set_useradjustable(UIWIDGET w, UiBool set);
+
+UIEXPORT int ui_image_load_file(UiGeneric *obj, const char *path);
+UIEXPORT int ui_image_load_data(UiGeneric *obj, const void *imgdata, size_t size);
+
+UIEXPORT void ui_image_ref(UIIMAGE img);
+UIEXPORT void ui_image_unref(UIIMAGE img);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_IMAGE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_MENU_H
+#define UI_MENU_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct UiMenuItemArgs {
+ const char* label;
+ const char* icon;
+
+ ui_callback onclick;
+ void* onclickdata;
+
+ const int* groups;
+} UiMenuItemArgs;
+
+typedef struct UiMenuToggleItemArgs {
+ const char* label;
+ const char* icon;
+
+ const char* varname;
+ ui_callback onchange;
+ void* onchangedata;
+
+ const int* groups;
+} UiMenuToggleItemArgs;
+
+typedef struct UiMenuItemListArgs {
+ const char* varname;
+ ui_getvaluefunc getvalue;
+ ui_callback onselect;
+ void* onselectdata;
+ UiBool addseparator;
+} UiMenuItemListArgs;
+
+#define ui_menu(label) for(ui_menu_create(label);ui_menu_is_open();ui_menu_close())
+
+#define ui_menuitem(...) ui_menuitem_create(&(UiMenuItemArgs){ __VA_ARGS__ })
+#define ui_menu_toggleitem(...) ui_menu_toggleitem_create(&(UiMenuToggleItemArgs){ __VA_ARGS__ })
+#define ui_menu_radioitem(...) ui_menu_radioitem_create(&(UiMenuToggleItemArgs){ __VA_ARGS__ })
+#define ui_menu_itemlist(...) ui_menu_itemlist_create(&(UiMenuItemListArgs) { __VA_ARGS__ } )
+#define ui_menu_togglelist(...) ui_menu_itemlist_create(&(UiMenuItemListArgs) { __VA_ARGS} )
+#define ui_menu_radiolist(...) ui_menu_itemlist_create(&(UiMenuItemListArgs) { __VA_ARGS} )
+
+UIEXPORT void ui_menu_create(const char* label);
+UIEXPORT void ui_menuitem_create(UiMenuItemArgs *args);
+UIEXPORT void ui_menu_toggleitem_create(UiMenuToggleItemArgs *args);
+UIEXPORT void ui_menu_radioitem_create(UiMenuToggleItemArgs *args);
+
+UIEXPORT void ui_menuseparator();
+
+UIEXPORT void ui_menu_itemlist_create(UiMenuItemListArgs *args);
+UIEXPORT void ui_menu_toggleitemlist_create(UiMenuItemListArgs *args);
+UIEXPORT void ui_menu_radioitemlist_create(UiMenuItemListArgs *args);
+
+UIEXPORT void ui_menu_end(void); // TODO: private
+
+/*
+ * widget menu functions
+ */
+
+#define ui_contextmenu(builder) for(ui_contextmenu_builder(builder);ui_menu_is_open();ui_menu_close())
+
+UIEXPORT void ui_contextmenu_builder(UiMenuBuilder **out_builder);
+UIEXPORT void ui_menubuilder_ref(UiMenuBuilder *builder);
+UIEXPORT void ui_menubuilder_unref(UiMenuBuilder *builder);
+UIEXPORT UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget);
+UIEXPORT void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y);
+
+// used for macro
+UIEXPORT void ui_menu_close(void);
+UIEXPORT int ui_menu_is_open(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_MENU_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_PROPERTIES_H
+#define UI_PROPERTIES_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char* ui_get_property(const char *name);
+void ui_set_property(const char *name, const char *value);
+const char* ui_set_default_property(const char *name, const char *value);
+
+int ui_properties_store(void);
+
+void ui_locales_dir(const char *path);
+void ui_pixmaps_dir(const char *path);
+
+void ui_load_lang(const char *locale);
+void ui_load_lang_def(char *locale, char *default_locale);
+
+char* uistr(const char *name);
+char* uistr_n(const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_PROPERTIES_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_RANGE_H
+#define UI_RANGE_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+UIWIDGET ui_hscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata); // TODO
+UIWIDGET ui_vscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata); // TODO
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_RANGE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_STOCK_H
+#define UI_STOCK_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// motif stock ids
+#if UI_MOTIF || UI_COCOA || UI_QT4 || UI_QT5
+
+#define UI_STOCK_NEW "ui.stock.New"
+#define UI_STOCK_OPEN "ui.stock.Open"
+#define UI_STOCK_SAVE "ui.stock.Save"
+#define UI_STOCK_SAVE_AS "ui.stock.SaveAs"
+#define UI_STOCK_REVERT_TO_SAVED "ui.stock.RevertToSaved"
+#define UI_STOCK_GO_BACK "ui.stock.GoBack"
+#define UI_STOCK_GO_FORWARD "ui.stock.GoForward"
+#define UI_STOCK_ADD "ui.stock.Add"
+#define UI_STOCK_CLOSE "ui.stock.Close"
+
+#define UI_STOCK_UNDO "ui.stock.Undo"
+#define UI_STOCK_REDO "ui.stock.Redo"
+#define UI_STOCK_CUT "ui.stock.Cut"
+#define UI_STOCK_COPY "ui.stock.Copy"
+#define UI_STOCK_PASTE "ui.stock.Paste"
+#define UI_STOCK_DELETE "ui.stock.Delete"
+
+#endif
+
+#if UI_GTK2 || UI_GTK3
+
+#define UI_STOCK_NEW GTK_STOCK_NEW
+#define UI_STOCK_OPEN GTK_STOCK_OPEN
+#define UI_STOCK_SAVE GTK_STOCK_SAVE
+#define UI_STOCK_SAVE_AS GTK_STOCK_SAVE_AS
+#define UI_STOCK_REVERT_TO_SAVED GTK_STOCK_REVERT_TO_SAVED
+#define UI_STOCK_UNDO GTK_STOCK_UNDO
+#define UI_STOCK_REDO GTK_STOCK_REDO
+#define UI_STOCK_GO_BACK GTK_STOCK_GO_BACK
+#define UI_STOCK_GO_FORWARD GTK_STOCK_GO_FORWARD
+#define UI_STOCK_ADD GTK_STOCK_ADD
+#define UI_STOCK_CLOSE GTK_STOCK_CLOSE
+
+#define UI_STOCK_UNDO GTK_STOCK_UNDO
+#define UI_STOCK_REDO GTK_STOCK_REDO
+#define UI_STOCK_CUT GTK_STOCK_CUT
+#define UI_STOCK_COPY GTK_STOCK_COPY
+#define UI_STOCK_PASTE GTK_STOCK_PASTE
+#define UI_STOCK_DELETE GTK_STOCK_DELETE
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_STOCK_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_TEXT_H
+#define UI_TEXT_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiTextAreaArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ int width;
+ int height;
+ const char *name;
+ const char *style_class;
+
+ UiText *value;
+ const char *varname;
+ ui_callback onchange;
+ void *onchangedata;
+
+ const int *groups;
+} UiTextAreaArgs;
+
+typedef struct UiTextFieldArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ int width;
+ const char *name;
+ const char *style_class;
+
+ UiString* value;
+ const char *varname;
+ ui_callback onchange;
+ void *onchangedata;
+ ui_callback onactivate;
+ void *onactivatedata;
+
+ const int *groups;
+} UiTextFieldArgs;
+
+typedef struct UiPathElmRet {
+ char *name;
+ size_t name_len;
+ char *path;
+ size_t path_len;
+} UiPathElm;
+
+typedef UiPathElm*(*ui_pathelm_func)(const char *full_path, size_t len, size_t *ret_nelm, void *data);
+
+
+
+typedef struct UiPathTextFieldArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ UiString *value;
+ const char *varname;
+
+ 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 *ondropsdata;
+} UiPathTextFieldArgs;
+
+#define ui_textarea(obj, ...) ui_textarea_create(obj, &(UiTextAreaArgs) { __VA_ARGS__ })
+
+UIEXPORT UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args);
+
+UIEXPORT UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea);
+
+UIEXPORT void ui_text_undo(UiText *value);
+UIEXPORT void ui_text_redo(UiText *value);
+
+#define ui_textfield(obj, ...) ui_textfield_create(obj, &(UiTextFieldArgs) { __VA_ARGS__ })
+#define ui_frameless_textfield(obj, ...) ui_frameless_field_create(obj, &(UiTextFieldArgs) { __VA_ARGS__ })
+#define ui_passwordfield(obj, ...) ui_passwordfield_create(obj, &(UiTextFieldArgs) { __VA_ARGS__ })
+#define ui_path_textfield(obj, ...) ui_path_textfield_create(obj, &(UiPathTextFieldArgs) { __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args);
+UIEXPORT UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs *args);
+UIEXPORT UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs *args);
+
+UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_TEXT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_TOOLBAR_H
+#define UI_TOOLBAR_H
+
+#include "toolkit.h"
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiToolbarItemArgs {
+ const char *label;
+ const char *icon;
+ const char *tooltip;
+
+ ui_callback onclick;
+ void* onclickdata;
+
+ const int *groups;
+ const int *visibility_states;
+} UiToolbarItemArgs;
+
+typedef struct UiToolbarToggleItemArgs {
+ const char *label;
+ const char *icon;
+ const char *tooltip;
+
+ const char *varname;
+ ui_callback onchange;
+ void *onchangedata;
+
+ const int *groups;
+ const int *visibility_states;
+} UiToolbarToggleItemArgs;
+
+typedef struct UiToolbarMenuArgs {
+ const char *label;
+ const char *icon;
+ const char *tooltip;
+ const int *visibility_states;
+} UiToolbarMenuArgs;
+
+enum UiToolbarPos {
+ UI_TOOLBAR_LEFT = 0,
+ UI_TOOLBAR_CENTER,
+ UI_TOOLBAR_RIGHT,
+ UI_TOOLBAR_SIDEBAR_LEFT,
+ UI_TOOLBAR_SIDEBAR_RIGHT,
+ UI_TOOLBAR_RIGHTPANEL_LEFT,
+ UI_TOOLBAR_RIGHTPANEL_CENTER,
+ UI_TOOLBAR_RIGHTPANEL_RIGHT
+};
+
+#define ui_toolbar_item(name, ...) ui_toolbar_item_create(name, &(UiToolbarItemArgs){ __VA_ARGS__ } )
+#define ui_toolbar_toggleitem(name, ...) ui_toolbar_toggleitem_create(name, &(UiToolbarToggleItemArgs){ __VA_ARGS__ } )
+
+#define ui_toolbar_menu(name, ...) for(ui_toolbar_menu_create(name, &(UiToolbarMenuArgs){ __VA_ARGS__ });ui_menu_is_open();ui_menu_close())
+#define ui_toolbar_appmenu() for(ui_toolbar_menu_create(NULL, &(UiToolbarMenuArgs){ 0 });ui_menu_is_open();ui_menu_close())
+
+
+UIEXPORT void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args);
+UIEXPORT void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *args);
+UIEXPORT void ui_toolbar_menu_create(const char* name, UiToolbarMenuArgs *args);
+
+UIEXPORT void ui_toolbar_add_default(const char *name, enum UiToolbarPos pos);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_TOOLBAR_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_TOOLKIT_H
+#define UI_TOOLKIT_H
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#ifdef UI_COCOA
+
+#include <stdlib.h>
+#ifdef __OBJC__
+#import <Cocoa/Cocoa.h>
+#endif
+typedef void* UIWIDGET; // NSView*
+typedef void* UIWINDOW; // NSWindow*
+typedef void* UIMENU; // NSMenu*
+
+#elif UI_GTK2 || UI_GTK3 || UI_GTK4
+#define UI_GTK
+
+#include <gtk/gtk.h>
+#define UIWIDGET GtkWidget*
+
+#if UI_GTK2 || UI_GTK3
+#define UIMENU GtkMenu*
+#endif
+#ifdef UI_GTK4
+#define UIMENU GtkPopoverMenu*
+#endif
+
+#define UI_GTK
+
+#ifdef UI_LIBADWAITA
+#include <adwaita.h>
+#endif
+
+#elif defined(UI_QT4) || defined(UI_QT5)
+#define UI_QT
+
+#ifdef __cplusplus
+
+#include <QApplication>
+#include <QWidget>
+#include <QMenu>
+
+#define UIWIDGET QWidget*
+#define UIWINDOW QWidget*
+#define UIMENU QMenu*
+#else /* __cplusplus */
+#define UIWIDGET void*
+#define UIWINDOW void*
+#define UIMENU void*
+#endif
+
+#elif UI_MOTIF
+
+#include <Xm/XmAll.h>
+#define UIWIDGET Widget
+#define UIMENU Widget
+
+
+#elif UI_WIN32
+
+#include "win32.h"
+
+#define UIWIDGET W32Widget*
+#define UIWINDOW void*
+#define UIMENU void*
+
+#elif UI_WINUI
+
+#define UIEXPORT __declspec(dllexport)
+
+#ifdef __cplusplus
+
+#include <functional>
+#ifndef UI_WINUI_PCH
+#include <Windows.h>
+#undef GetCurrentTime
+#include <winrt/Windows.Foundation.Collections.h>
+#include <winrt/Windows.UI.Xaml.Interop.h>
+#include <winrt/Microsoft.UI.Xaml.Controls.h>
+#endif
+
+class UiWindow {
+public:
+ winrt::Microsoft::UI::Xaml::Window window { nullptr };
+
+ UiWindow(winrt::Microsoft::UI::Xaml::Window& win);
+};
+
+class UiWidget {
+public:
+ winrt::Microsoft::UI::Xaml::UIElement uielement;
+ void* data1 = nullptr;
+ void* data2 = nullptr;
+ void* data3 = nullptr;
+ std::function<void()> Show;
+ UiWidget(winrt::Microsoft::UI::Xaml::UIElement& elm);
+
+};
+
+#define UIWIDGET UiWidget*
+#define UIWINDOW UiWindow*
+#define UIMENU void*
+
+/*
+// winrt::Microsoft::UI::Xaml::UIElement
+#define UIWIDGET void*
+// winrt::Microsoft::UI::Xaml::Window
+#define UIWINDOW void*
+#define UIMENU void*
+*/
+
+#else
+#define UIWIDGET void*
+#define UIWINDOW void*
+#define UIMENU void*
+#endif
+
+
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef UIEXPORT
+#define UIEXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_GROUP_SELECTION 20000
+
+#define UI_GROUPS(...) (const int[]){ __VA_ARGS__, -1 }
+
+/* public types */
+#ifndef __cplusplus
+typedef _Bool UiBool;
+#else
+typedef bool UiBool;
+#endif
+
+typedef struct UiObject UiObject;
+typedef struct UiContainerX UiContainerX;
+typedef struct UiEvent UiEvent;
+typedef struct UiMouseEvent UiMouseEvent;
+typedef struct UiObserver UiObserver;
+
+typedef struct UiInteger UiInteger;
+typedef struct UiDouble UiDouble;
+typedef struct UiString UiString;
+typedef struct UiText UiText;
+typedef struct UiList UiList;
+typedef struct UiRange UiRange;
+typedef struct UiGeneric UiGeneric;
+
+typedef struct UiStr UiStr;
+
+typedef struct UiFileList UiFileList;
+
+typedef struct UiListSelection UiListSelection;
+
+typedef struct UiTextStyle UiTextStyle;
+typedef struct UiColor UiColor;
+
+/* begin opaque types */
+typedef struct UiContext UiContext;
+typedef struct UiContainer UiContainer;
+typedef struct UiMenuBuilder UiMenuBuilder;
+
+typedef struct UiIcon UiIcon;
+typedef struct UiImage UiImage;
+
+typedef struct UiDnD UiDnD;
+
+typedef struct UiThreadpool UiThreadpool;
+/* end opaque types */
+
+typedef struct UiTabbedPane UiTabbedPane;
+
+typedef enum UiTri {
+ UI_DEFAULT = 0,
+ UI_ON,
+ UI_OFF
+} UiTri;
+
+enum UiMouseEventType { UI_PRESS = 0, UI_PRESS2 };
+
+typedef enum UiLabelType {
+ UI_LABEL_DEFAULT,
+ UI_LABEL_TEXT,
+ UI_LABEL_ICON,
+ UI_LABEL_TEXT_ICON
+} UiLabelType;
+
+typedef enum UiDnDAction {
+ UI_DND_ACTION_NONE,
+ UI_DND_ACTION_COPY,
+ UI_DND_ACTION_MOVE,
+ UI_DND_ACTION_LINK,
+ UI_DND_ACTION_CUSTOM
+} UiDnDAction;
+
+enum UiAlignment {
+ UI_ALIGN_DEFAULT = 0,
+ UI_ALIGN_LEFT,
+ UI_ALIGN_RIGHT,
+ UI_ALIGN_CENTER
+};
+
+typedef enum UiAlignment UiAlignment;
+
+typedef void(*ui_callback)(UiEvent*, void*); /* event, user data */
+
+typedef void*(*ui_getvaluefunc)(void *elm, int col);
+typedef void*(*ui_getvaluefunc2)(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult);
+typedef UiBool(*ui_getstylefunc)(UiList *list, void *elm, int row, int col, void *userdata, UiTextStyle *style);
+
+typedef int(*ui_threadfunc)(void*);
+
+typedef void(*ui_freefunc)(void*);
+
+typedef void(*ui_enablefunc)(void*, int);
+
+typedef void (*ui_destructor_func)(void *memory);
+
+
+struct UiObject {
+ /*
+ * native widget
+ */
+ UIWIDGET widget;
+
+#if defined(UI_COCOA) || defined(UI_WINUI)
+ /*
+ * native window object
+ */
+ UIWINDOW wobj;
+#endif
+
+ /*
+ * user window data
+ */
+ void *window;
+
+ /*
+ * window context
+ */
+ UiContext *ctx;
+
+ /*
+ * container list
+ * TODO: remove old UiContainer and rename UiContainerX to UiContainer
+ */
+ UiContainerX *container_begin;
+ UiContainerX *container_end;
+
+ /*
+ * next container object
+ */
+ UiObject *next;
+
+ /*
+ * obj destroy func
+ */
+ void (*destroy)(UiObject *obj);
+
+ /*
+ * reference counter
+ */
+ unsigned int ref;
+};
+
+struct UiTabbedPane {
+ /*
+ * native widget
+ */
+ UIWIDGET widget;
+
+ /*
+ * current document
+ */
+ void *document;
+
+ /*
+ * parent context
+ */
+ UiContext *ctx;
+};
+
+struct UiEvent {
+ UiObject *obj;
+ void *document;
+ void *window;
+ void *eventdata;
+ int eventdatatype;
+ int intval;
+ int set;
+};
+
+struct UiMouseEvent {
+ int x;
+ int y;
+ enum UiMouseEventType type;
+ int button;
+};
+
+struct UiObserver {
+ ui_callback callback;
+ void *data;
+ UiObserver *next;
+};
+
+struct UiStr {
+ char *ptr;
+ void (*free)(void *v);
+};
+
+struct UiInteger {
+ int64_t (*get)(UiInteger*);
+ void (*set)(UiInteger*, int64_t);
+ void *obj;
+
+ int64_t value;
+ UiObserver *observers;
+};
+
+struct UiDouble {
+ double (*get)(UiDouble*);
+ void (*set)(UiDouble*, double);
+ void *obj;
+
+ double value;
+ UiObserver *observers;
+};
+
+struct UiString {
+ char* (*get)(UiString*);
+ void (*set)(UiString*, const char*);
+ void *obj;
+
+ UiStr value;
+ UiObserver *observers;
+};
+
+struct UiText {
+ void (*save)(UiText*);
+ void (*destroy)(UiText*);
+ void (*restore)(UiText*);
+ void (*set)(UiText*, const char*);
+ char* (*get)(UiText*);
+ char* (*getsubstr)(UiText*, int, int); /* text, begin, end */
+ void (*insert)(UiText*, int, char*);
+ void (*setposition)(UiText*,int);
+ int (*position)(UiText*);
+ void (*setselection)(UiText*, int, int); /* text, begin, end */
+ void (*selection)(UiText*, int*, int*); /* text, begin, end */
+ int (*length)(UiText*);
+ void (*remove)(UiText*, int, int); /* text, begin, end */
+ UiStr value;
+ int pos;
+ void *obj;
+ int datatype;
+ void *data1;
+ void *data2;
+ // TODO: replacefunc, ...
+ UiObserver *observers;
+};
+
+/* UiText.datatype */
+#define UI_TEXT_TYPE_BUFFER 1
+
+struct UiGeneric {
+ void* (*get)(UiGeneric*);
+ const char* (*get_type)(UiGeneric*);
+ int (*set)(UiGeneric*, void *, const char *type);
+ void (*destroy)(UiGeneric*);
+ void *obj;
+
+ void *value;
+ const char *type;
+ UiObserver *observers;
+};
+
+typedef void (*ui_list_init_func)(UiContext *ctx, UiList *list, void *userdata);
+
+/*
+ * abstract list
+ */
+struct UiList {
+ /* get the first element */
+ void*(*first)(UiList *list);
+ /* get the next element */
+ void*(*next)(UiList *list);
+ /* get the nth element */
+ void*(*get)(UiList *list, int i);
+ /* get the number of elements */
+ int(*count)(UiList *list);
+ /* iterator changes after first() next() and get() */
+ void *iter;
+ /* private - implementation dependent */
+ void *data;
+
+ /* binding functions */
+ void (*update)(UiList *list, int i);
+ UiListSelection (*getselection)(UiList *list);
+ void (*setselection)(UiList *list, UiListSelection selection);
+ /* binding object */
+ void *obj;
+
+ /* list of observers */
+ UiObserver *observers;
+};
+
+
+struct UiListSelection {
+ /*
+ * number of selected items
+ */
+ int count;
+
+ /*
+ * indices of selected rows
+ */
+ int *rows;
+};
+
+struct UiRange {
+ double (*get)(UiRange *range);
+ void (*set)(UiRange *range, double value);
+ void (*setrange)(UiRange *range, double min, double max);
+ void (*setextent)(UiRange *range, double extent);
+ double value;
+ double min;
+ double max;
+ double extent;
+ void *obj;
+ /* list of observers */
+ UiObserver *observers;
+};
+
+struct UiFileList {
+ char **files;
+ size_t nfiles;
+};
+
+typedef struct UiCondVar {
+ void *data;
+ int intdata;
+} UiCondVar;
+
+enum UiEventType {
+ UI_EVENT_DATA_NULL = 0,
+ UI_EVENT_DATA_POINTER,
+ UI_EVENT_DATA_STRING,
+ UI_EVENT_DATA_INTEGER_VALUE,
+ UI_EVENT_DATA_STRING_VALUE,
+ UI_EVENT_DATA_TEXT_VALUE,
+ UI_EVENT_DATA_DOUBLE_VALUE,
+ UI_EVENT_DATA_RANGE_VALUE,
+ UI_EVENT_DATA_LIST_SELECTION,
+ UI_EVENT_DATA_LIST_ELM,
+ UI_EVENT_DATA_DND,
+ UI_EVENT_DATA_SUBLIST,
+ UI_EVENT_DATA_FILE_LIST
+};
+
+#define UI_COLOR(r, g, b) (UiColor){r, g, b}
+struct UiColor {
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+};
+
+#define UI_TEXT_STYLE_BOLD 1
+#define UI_TEXT_STYLE_ITALIC 2
+#define UI_TEXT_STYLE_UNDERLINE 4
+
+struct UiTextStyle {
+ uint32_t text_style;
+ UiColor fg;
+ UiBool fg_set;
+};
+
+
+UIEXPORT void ui_init(const char *appname, int argc, char **argv);
+UIEXPORT const char* ui_appname();
+
+UIEXPORT void ui_add_styledata(const char *styledata, int len);
+
+UIEXPORT UiContext* ui_global_context(void);
+
+UIEXPORT void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata);
+
+UIEXPORT void ui_context_destroy(UiContext *ctx);
+
+UIEXPORT UiContext* ui_context_parent(UiContext *ctx);
+
+UIEXPORT void ui_object_ref(UiObject *obj);
+UIEXPORT int ui_object_unref(UiObject *obj);
+
+UIEXPORT void ui_onstartup(ui_callback f, void *userdata);
+UIEXPORT void ui_onopen(ui_callback f, void *userdata);
+UIEXPORT void ui_onexit(ui_callback f, void *userdata);
+
+UIEXPORT int ui_app_save_settings(void);
+UIEXPORT void ui_app_exit_on_shutdown(UiBool exitapp);
+
+UIEXPORT void ui_main(void);
+UIEXPORT void ui_show(UiObject *obj);
+UIEXPORT void ui_close(UiObject *obj);
+
+UIEXPORT void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd);
+UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td);
+UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads);
+UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool);
+UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd);
+
+UIEXPORT void* ui_document_new(size_t size);
+UIEXPORT void ui_document_destroy(void *doc);
+
+UIEXPORT void* ui_get_subdocument(void *document); // deprecated
+
+UIEXPORT UiContext* ui_document_context(void *doc);
+
+UIEXPORT void ui_attach_document(UiContext *ctx, void *document);
+UIEXPORT void ui_detach_document(UiContext *ctx, void *document);
+
+UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...);
+UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups);
+UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates);
+
+UIEXPORT void ui_set_group(UiContext *ctx, int group);
+UIEXPORT void ui_unset_group(UiContext *ctx, int group);
+UIEXPORT int* ui_active_groups(UiContext *ctx, int *ngroups);
+
+UIEXPORT void* ui_allocator(UiContext *ctx);
+UIEXPORT void* ui_cx_mempool(UiContext *ctx);
+
+UIEXPORT void* ui_malloc(UiContext *ctx, size_t size);
+UIEXPORT void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize);
+UIEXPORT void ui_free(UiContext *ctx, void *ptr);
+UIEXPORT void* ui_realloc(UiContext *ctx, void *ptr, size_t size);
+UIEXPORT char* ui_strdup(UiContext *ctx, const char *str);
+UIEXPORT void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr);
+UIEXPORT void ui_set_destructor(void *mem, ui_destructor_func destr);
+
+// types
+
+UIEXPORT UiInteger* ui_int_new(UiContext *ctx, const char *name);
+UIEXPORT UiDouble* ui_double_new(UiContext *ctx, const char *name);
+UIEXPORT UiString* ui_string_new(UiContext *ctx, const char *name);
+UIEXPORT UiText* ui_text_new(UiContext *ctx, const char *name);
+UIEXPORT UiRange* ui_range_new(UiContext *ctx, const char *name);
+UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, const char *name);
+
+#define ui_get(v) _Generic(v, \
+ UiInteger*: ui_int_get, \
+ UiDouble*: ui_double_get, \
+ UiString*: ui_string_get, \
+ UiText*:ui_text_get) (v)
+
+#define ui_set(v, n) _Generic(v, \
+ UiInteger*: ui_int_set, \
+ UiDouble*: ui_double_set, \
+ UiString*: ui_string_set, \
+ UiText*:ui_text_set) (v, n)
+
+UIEXPORT void ui_int_set(UiInteger *i, int64_t value);
+UIEXPORT int64_t ui_int_get(UiInteger *i);
+UIEXPORT void ui_double_set(UiDouble *d, double value);
+UIEXPORT double ui_double_get(UiDouble *d);
+UIEXPORT void ui_string_set(UiString *s, const char *value);
+UIEXPORT char* ui_string_get(UiString *s);
+UIEXPORT void ui_text_set(UiText *s, const char* value);
+UIEXPORT char* ui_text_get(UiText *s);
+UIEXPORT void ui_range_set(UiRange *r, double value);
+UIEXPORT void ui_range_set_range(UiRange *r, double min, double max);
+UIEXPORT void ui_range_set_extent(UiRange *r, double extent);
+UIEXPORT double ui_range_get(UiRange *r);
+UIEXPORT double ui_range_get_min(UiRange *r);
+UIEXPORT double ui_range_get_max(UiRange *r);
+UIEXPORT double ui_range_get_extent(UiRange *r);
+UIEXPORT void ui_generic_set_image(UiGeneric *g, void *img);
+UIEXPORT void* ui_generic_get_image(UiGeneric *g);
+
+UIEXPORT void ui_var_set_int(UiContext *ctx, const char *name, int64_t value);
+UIEXPORT int64_t ui_var_get_int(UiContext *ctx, const char *name);
+UIEXPORT void ui_var_set_double(UiContext *ctx, const char *name, double value);
+UIEXPORT double ui_var_get_double(UiContext *ctx, const char *name);
+UIEXPORT void ui_var_set_string(UiContext *ctx, const char *name, char *value);
+UIEXPORT char* ui_var_get_string(UiContext *ctx, const char *name);
+
+UIEXPORT UiObserver* ui_observer_new(ui_callback f, void *data);
+UIEXPORT UiObserver* ui_obsvlist_add(UiObserver *list, UiObserver *observer);
+UIEXPORT UiObserver* ui_add_observer(UiObserver *list, ui_callback f, void *data);
+UIEXPORT void ui_notify(UiObserver *observer, void *data);
+UIEXPORT void ui_notify_except(UiObserver *observer, UiObserver *exc, void *data);
+UIEXPORT void ui_notify_evt(UiObserver *observer, UiEvent *event);
+
+
+UIEXPORT UiList* ui_list_new(UiContext *ctx, const char *name);
+UIEXPORT UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func init, void *userdata);
+UIEXPORT void ui_list_free(UiList *list);
+UIEXPORT void* ui_list_first(UiList *list);
+UIEXPORT void* ui_list_next(UiList *list);
+UIEXPORT void* ui_list_get(UiList *list, int i);
+UIEXPORT int ui_list_count(UiList *list);
+UIEXPORT void ui_list_append(UiList *list, void *data);
+UIEXPORT void ui_list_prepend(UiList *list, void *data);
+UIEXPORT void ui_list_remove(UiList *list, int i);
+UIEXPORT void ui_list_clear(UiList *list);
+UIEXPORT void ui_list_update(UiList *list);
+UIEXPORT void ui_list_update_row(UiList *list, int row);
+UIEXPORT UiListSelection ui_list_get_selection(UiList *list);
+UIEXPORT void ui_list_addobsv(UiList *list, ui_callback f, void *data);
+UIEXPORT void ui_list_notify(UiList *list);
+
+UIEXPORT UiListSelection ui_list_getselection(UiList *list);
+UIEXPORT void ui_list_setselection(UiList *list, int index);
+
+UIEXPORT UiFileList ui_filelist_copy(UiFileList list);
+UIEXPORT void ui_filelist_free(UiFileList list);
+
+UIEXPORT void ui_clipboard_set(char *str);
+UIEXPORT char* ui_clipboard_get();
+
+UIEXPORT void ui_add_image(char *imgname, char *filename); // TODO: remove?
+
+
+
+UIEXPORT void ui_listselection_free(UiListSelection selection);
+
+
+UIEXPORT UiStr ui_str(char *cstr);
+UIEXPORT UiStr ui_str_free(char *str, void (*free)(void *v));
+
+
+UIEXPORT char* ui_getappdir(void);
+UIEXPORT char* ui_configfile(const char *name);
+
+UIEXPORT UiCondVar* ui_condvar_create(void);
+UIEXPORT void ui_condvar_wait(UiCondVar *var);
+UIEXPORT void ui_condvar_signal(UiCondVar *var, void *data, int intdata);
+UIEXPORT void ui_condvar_destroy(UiCondVar *var);
+
+UIEXPORT void ui_setop_enable(int set);
+UIEXPORT int ui_get_setop(void);
+
+
+UIEXPORT void ui_global_list_initializer(ui_list_init_func func, void *userdata);
+UIEXPORT void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list));
+UIEXPORT void ui_list_class_set_next(UiList *list, void*(*next)(UiList *list));
+UIEXPORT void ui_list_class_set_get(UiList *list, void*(*get)(UiList *list, int i));
+UIEXPORT void ui_list_class_set_count(UiList *list, int(*count)(UiList *list));
+UIEXPORT void ui_list_class_set_data(UiList *list, void *data);
+UIEXPORT void ui_list_class_set_iter(UiList *list, void *iter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_TOOLKIT_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_TREE_H
+#define UI_TREE_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiModel UiModel;
+typedef struct UiListCallbacks UiListCallbacks;
+typedef struct UiListDnd UiListDnd;
+
+typedef struct UiListArgs UiListArgs;
+typedef struct UiSourceListArgs UiSourceListArgs;
+
+typedef struct UiSubList UiSubList;
+typedef struct UiSubListItem UiSubListItem;
+
+typedef enum UiModelType {
+ UI_STRING = 0,
+ UI_STRING_FREE,
+ UI_INTEGER,
+ UI_ICON,
+ UI_ICON_TEXT,
+ UI_ICON_TEXT_FREE,
+ UI_STRING_EDITABLE,
+ UI_BOOL_EDITABLE
+} UiModelType;
+
+typedef struct UiCellValue {
+ union {
+ const char *string;
+ int64_t i;
+ UiBool b;
+ };
+ UiModelType type;
+} UiCellValue;
+
+typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue *value, void *userdata);
+
+struct UiModel {
+ /*
+ * number of columns
+ */
+ int columns;
+
+ /*
+ * current allocation size (internal)
+ */
+ int alloc;
+
+ /*
+ * array of column types
+ * array length is the number of columns
+ */
+ UiModelType *types;
+
+ /*
+ * array of column titles
+ * array length is the number of columns
+ */
+ char **titles;
+
+ /*
+ * array of column size hints
+ */
+ int *columnsize;
+};
+
+struct UiListCallbacks {
+ /*
+ * selection callback
+ */
+ ui_callback activate;
+
+ /*
+ * cursor callback
+ */
+ ui_callback selection;
+
+ /*
+ * userdata for all callbacks
+ */
+ void *userdata;
+};
+
+struct UiListArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ int width;
+ int height;
+
+ const char *name;
+ const char *style_class;
+ UiList* list;
+ const char* varname;
+ UiModel* model;
+ char **static_elements;
+ size_t static_nelm;
+ ui_getvaluefunc getvalue;
+ ui_getvaluefunc2 getvalue2;
+ void *getvalue2data;
+ ui_getstylefunc getstyle;
+ void *getstyledata;
+ ui_callback onactivate;
+ void* onactivatedata;
+ ui_callback onselection;
+ void* onselectiondata;
+ ui_callback ondragstart;
+ void* ondragstartdata;
+ ui_callback ondragcomplete;
+ void* ondragcompletedata;
+ ui_callback ondrop;
+ void* ondropdata;
+ UiBool multiselection;
+ UiMenuBuilder *contextmenu;
+ ui_list_savefunc onsave;
+ void *onsavedata;
+
+ const int *groups;
+};
+
+typedef void (*ui_sublist_getvalue_func)(UiList *list, void *sublist_userdata, void *rowdata, int index, UiSubListItem *item, void *userdata);
+
+struct UiSubList {
+ UiList *value;
+ const char *varname;
+ const char *header;
+ UiBool separator;
+ void *userdata;
+};
+
+typedef struct UiSubListEventData {
+ UiList *list;
+ int sublist_index;
+ int row_index;
+ void *row_data;
+ void *sublist_userdata;
+ void *event_data;
+} UiSubListEventData;
+
+/*
+ * list item members must be filled by the sublist getvalue func
+ * all members must be allocated (by malloc, strdup, ...) the pointer
+ * will be passed to free
+ */
+struct UiSubListItem {
+ char *icon;
+ char *label;
+ char *button_icon;
+ char *button_label;
+ UiMenuBuilder *button_menu;
+ char *badge;
+ void *eventdata;
+};
+
+struct UiSourceListArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ int width;
+ int height;
+ const char *name;
+ const char *style_class;
+
+ const int *groups;
+
+ /*
+ * static list of sublists
+ * a sublist must have a varname or a value
+ *
+ * the last entry in the list must contain all NULL values or numsublists
+ * must contain the number of sublists
+ *
+ * sublists can be NULL, in which case sublists are dynamically loaded
+ * from dynamic_sublist/varname
+ */
+ UiSubList *sublists;
+ /*
+ * optional number of sublists
+ * if the value is 0, it is assumed, that sublists is null-terminated
+ * (last item contains only NULL values)
+ */
+ size_t numsublists;
+
+ /*
+ * list value, that contains UiSubList* elements
+ */
+ UiList *dynamic_sublist;
+
+ /*
+ * load sublists dynamically from a variable with the specified name
+ */
+ const char *varname;
+
+
+ /*
+ * callback for each list item, that should fill all necessary
+ * UiSubListItem fields
+ */
+ ui_sublist_getvalue_func getvalue;
+
+ /*
+ * getvalue_func userdata
+ */
+ void *getvaluedata;
+
+ /*
+ * is a sublist header a selectable item
+ */
+ UiBool header_is_item;
+
+ /*
+ * activated when a list item is selected
+ */
+ ui_callback onactivate;
+ void *onactivatedata;
+
+ /*
+ * activated, when the additional list item button is clicked
+ */
+ ui_callback onbuttonclick;
+ void *onbuttonclickdata;
+
+ UiMenuBuilder *contextmenu;
+};
+
+#define UI_SUBLIST(...) (UiSubList){ __VA_ARGS__ }
+#define UI_SUBLISTS(...) (UiSubList[]){ __VA_ARGS__, (UiSubList){NULL,NULL,NULL,0} }
+
+
+/*
+ * Creates an UiModel, that specifies columns for a table widget.
+ *
+ * For each column a column type (UiModelType) and a title string
+ * (char*) must be specified. The column list must be terminated
+ * with -1.
+ *
+ * UiModel *model = ui_model(ctx, UI_STRING, "Column 1", UI_STRING, "Column 2", -1);
+ */
+UIEXPORT UiModel* ui_model(UiContext *ctx, ...);
+UIEXPORT UiModel* ui_model_new(UiContext *ctx);
+UIEXPORT void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width);
+UIEXPORT UiModel* ui_model_copy(UiContext *ctx, UiModel* model);
+UIEXPORT void ui_model_free(UiContext *ctx, UiModel *mi);
+
+#define ui_listview(obj, ...) ui_listview_create(obj, &(UiListArgs) { __VA_ARGS__ } )
+#define ui_table(obj, ...) ui_table_create(obj, &(UiListArgs) { __VA_ARGS__ } )
+#define ui_combobox(obj, ...) ui_combobox_create(obj, &(UiListArgs) { __VA_ARGS__ } )
+#define ui_breadcrumbbar(obj, ...) ui_breadcrumbbar_create(obj, &(UiListArgs) { __VA_ARGS__ } )
+#define ui_sourcelist(obj, ...) ui_sourcelist_create(obj, &(UiSourceListArgs) { __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args);
+UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args);
+UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args);
+UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs *args);
+
+UIEXPORT void ui_listview_select(UIWIDGET listview, int index);
+UIEXPORT void ui_combobox_select(UIWIDGET dropdown, int index);
+
+UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args);
+
+UIEXPORT void ui_sublist_item_set_icon(UiSubListItem *item, const char *icon);
+UIEXPORT void ui_sublist_item_set_label(UiSubListItem *item, const char *label);
+UIEXPORT void ui_sublist_item_set_button_icon(UiSubListItem *item, const char *button_icon);
+UIEXPORT void ui_sublist_item_set_button_label(UiSubListItem *item, const char *button_label);
+UIEXPORT void ui_sublist_item_set_button_menu(UiSubListItem *item, UiMenuBuilder *menu);
+UIEXPORT void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge);
+UIEXPORT void ui_sublist_item_set_eventdata(UiSubListItem *item, void *eventdata);
+
+
+
+/*
+ * Only relevant for some language bindings
+ */
+typedef void(*ui_sourcelist_update_func)(void);
+
+/*
+ * The sourcelist update callback is called after any source list
+ * sublist update is completed
+ */
+UIEXPORT void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_TREE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_H
+#define UI_H
+
+#include "toolkit.h"
+#include "widget.h"
+#include "container.h"
+#include "menu.h"
+#include "toolbar.h"
+#include "window.h"
+#include "stock.h"
+#include "button.h"
+#include "text.h"
+#include "properties.h"
+#include "tree.h"
+#include "graphics.h"
+#include "entry.h"
+#include "range.h"
+#include "image.h"
+#include "display.h"
+#include "dnd.h"
+#include "icons.h"
+
+#include "webview.h"
+
+#endif /* UI_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_WEBVIEW_H
+#define UI_WEBVIEW_H
+
+#include "toolkit.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * WebView type string used by UiGeneric
+ */
+#define UI_WEBVIEW_OBJECT_TYPE "webview"
+
+/*
+ * UiWebViewData* is returned by a webviews UiGeneric->get function
+ */
+typedef struct UiWebViewData UiWebViewData;
+
+typedef struct UiWebviewArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+
+ UiGeneric *value;
+ const char *varname;
+
+ const int* groups;
+} UiWebviewArgs;
+
+#define ui_webview(obj, ...) ui_webview_create(obj, &(UiWebviewArgs){ __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args);
+
+UIEXPORT void ui_webview_load_url(UiGeneric *g, const char *url);
+
+UIEXPORT void ui_webview_load_content(
+ UiGeneric *g,
+ const char *uri,
+ const char *content,
+ size_t contentlength,
+ const char *mimetype,
+ const char *encoding);
+
+/*
+ * Frees a UiWebViewData object returned by a webviews UiGeneric->get function
+ */
+UIEXPORT void ui_webview_data_free(UiWebViewData *data);
+
+UIEXPORT void ui_webview_reload(UiGeneric *g);
+UIEXPORT UiBool ui_webview_can_go_back(UiGeneric *g);
+UIEXPORT UiBool ui_webview_can_go_forward(UiGeneric *g);
+UIEXPORT void ui_webview_go_back(UiGeneric *g);
+UIEXPORT void ui_webview_go_forward(UiGeneric *g);
+UIEXPORT const char * ui_webview_get_uri(UiGeneric *g);
+UIEXPORT void ui_webview_enable_javascript(UiGeneric *g, UiBool enable);
+UIEXPORT void ui_webview_set_zoom(UiGeneric *g, double zoom);
+UIEXPORT double ui_webview_get_zoom(UiGeneric *g);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_WEBVIEW_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef UI_WIDGET_H
+#define UI_WIDGET_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiWidgetArgs {
+ UiBool fill;
+ UiBool hexpand;
+ UiBool vexpand;
+ UiBool hfill;
+ UiBool vfill;
+ UiBool override_defaults;
+ int margin;
+ int margin_left;
+ int margin_right;
+ int margin_top;
+ int margin_bottom;
+ int colspan;
+ int rowspan;
+ const char *name;
+ const char *style_class;
+} UiWidgetArgs;
+
+#ifdef UI_GTK
+typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
+#elif defined(UI_QT)
+typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
+#elif defined(UI_MOTIF)
+typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata, Widget parent, Arg *a, int n);
+#elif defined(UI_COCOA)
+typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
+#elif defined(UI_WINUI)
+typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
+#elif defined(UI_WIN32)
+typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
+#endif
+UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args);
+
+#define ui_customwidget(obj, create_widget, userdata, ...) ui_customwidget_create(obj, create_widget, userdata, &(UiWidgetArgs) { __VA_ARGS__ })
+
+
+UIEXPORT UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args);
+
+#define ui_separator(obj, ...) ui_separator_create(obj, &(UiWidgetArgs){ __VA_ARGS__ } )
+
+UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled);
+UIEXPORT void ui_set_visible(UIWIDGET widget, int visible);
+
+UIEXPORT void ui_widget_set_size(UIWIDGET w, int width, int height);
+UIEXPORT void ui_widget_redraw(UIWIDGET w);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_WIDGET_H */
+
--- /dev/null
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2025 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#ifndef UI_WIN32_H\r
+#define UI_WIN32_H\r
+\r
+#include <Windows.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+#define UIEXPORT __declspec(dllexport)\r
+\r
+typedef struct W32WidgetClass W32WidgetClass;\r
+typedef struct W32Widget W32Widget;\r
+typedef struct W32Size W32Size;\r
+\r
+typedef void (*W32LayoutFunc)(void *, int, int);\r
+\r
+struct W32Size {\r
+ int width;\r
+ int height;\r
+};\r
+\r
+struct W32WidgetClass {\r
+ void (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+ void (*show)(W32Widget *widget, BOOLEAN show);\r
+ void (*enable)(W32Widget *widget, BOOLEAN enable);\r
+ W32Size (*get_preferred_size)(W32Widget *widget);\r
+ void (*destroy)(W32Widget *widget);\r
+};\r
+\r
+struct W32Widget {\r
+ W32WidgetClass *wclass;\r
+ HWND hwnd;\r
+ void *userdata;\r
+ void (*layout)(void *layout, int width, int height);\r
+ void *layoutmanager;\r
+};\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif /* UI_WIN32_H */\r
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_WINDOW_H
+#define UI_WINDOW_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_FILEDIALOG_SELECT_SINGLE 0
+#define UI_FILEDIALOG_SELECT_MULTI 1
+#define UI_FILEDIALOG_SELECT_FOLDER 2
+
+typedef struct UiDialogArgs {
+ const char *title;
+ const char *content;
+ const char *button1_label;
+ const char *button2_label;
+ const char *closebutton_label;
+ const char *input_value;
+ UiBool input;
+ UiBool password;
+ ui_callback result;
+ void *resultdata;
+} UiDialogArgs;
+
+typedef struct UiDialogWindowArgs {
+ UiTri modal;
+ UiTri titlebar_buttons;
+ UiTri show_closebutton;
+ const char *title;
+ const char *lbutton1;
+ const char *lbutton2;
+ const char *rbutton3;
+ const char *rbutton4;
+ const int *lbutton1_groups;
+ const int *lbutton2_groups;
+ const int *rbutton3_groups;
+ const int *rbutton4_groups;
+ int default_button;
+ int width;
+ int height;
+ ui_callback onclick;
+ void *onclickdata;
+} UiDialogWindowArgs;
+
+UIEXPORT UiObject *ui_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar);
+UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args);
+
+#define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ __VA_ARGS__ });
+#define ui_dialog_window0(parent) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ 0 });
+
+UIEXPORT void ui_window_size(UiObject *obj, int width, int height);
+UIEXPORT void ui_window_default_size(int width, int height);
+
+UIEXPORT void ui_splitview_window_set_pos(UiObject *obj, int pos);
+UIEXPORT int ui_splitview_window_get_pos(UiObject *obj);
+UIEXPORT void ui_splitview_window_set_default_pos(int pos);
+UIEXPORT void ui_splitview_window_use_property(UiBool enable);
+UIEXPORT void ui_splitview_window_set_visible(UiObject *obj, int pane, UiBool visible);
+
+#define ui_dialog(parent, ...) ui_dialog_create(parent, &(UiDialogArgs){ __VA_ARGS__ } )
+
+UIEXPORT void ui_dialog_create(UiObject *parent, UiDialogArgs *args);
+
+UIEXPORT void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata);
+UIEXPORT void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WINDOW_H */
+