Wed, 23 Oct 2024 17:40:45 +0200
add authentication dialog
--- a/application/Makefile Wed Oct 23 10:37:43 2024 +0200 +++ b/application/Makefile Wed Oct 23 17:40:45 2024 +0200 @@ -34,7 +34,6 @@ SRC = main.c SRC += application.c SRC += config.c -SRC += connect.c SRC += davcontroller.c SRC += system.c SRC += window.c
--- a/application/application.h Wed Oct 23 10:37:43 2024 +0200 +++ b/application/application.h Wed Oct 23 17:40:45 2024 +0200 @@ -135,6 +135,15 @@ char *value_full; } DavPropertyList; +typedef struct SessionAuthData { + UiObject *obj; + UiCondVar *cond; + DavSession *sn; + char *user; + char *password; +} SessionAuthData; + + void application_init(void); /*
--- a/application/connect.c Wed Oct 23 10:37:43 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/* - * 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 "connect.h" - -#include <libidav/utils.h> - -#include <cx/string.h> -#include <cx/utils.h> - -DavSession* connect_to_repo(DavContext *ctx, DavCfgRepository *repo, const char *path, dav_auth_func authfunc) { - cxmutstr decodedpw = dav_repository_get_decodedpassword(repo); - - char *user = repo->user.value.ptr; - char *password = decodedpw.ptr; - - if(!user && !password) { - if(!get_stored_credentials(repo->stored_user.value.ptr, &user, &password)) { - get_location_credentials(repo, path, &user, &password); - } - } - - DavSession *sn = dav_session_new_auth(ctx, repo->url.value.ptr, user, password); - if(password) { - free(password); - } - - sn->flags = dav_repository_get_flags(repo); - sn->key = dav_context_get_key(ctx, repo->default_key.value.ptr); - // TODO: reactivate - //curl_easy_setopt(sn->handle, CURLOPT_HTTPAUTH, repo->authmethods); - curl_easy_setopt(sn->handle, CURLOPT_SSLVERSION, repo->ssl_version); - if(repo->cert.value.ptr) { - curl_easy_setopt(sn->handle, CURLOPT_CAINFO, repo->cert.value.ptr); - } - /* - if(!repo->verification.value || cmd_getoption(a, "insecure")) { - curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYHOST, 0); - } - */ - /* - if(!cmd_getoption(a, "noinput")) { - dav_session_set_authcallback(sn, authfunc, repo); - } - */ - return sn; -} - -int request_auth(DavSession *sn, void *userdata) { - DavCfgRepository *repo = userdata; - - cxstring user = {NULL, 0}; - char ubuf[256]; - if(repo->user.value.ptr) { - user = cx_strcast(repo->user.value); - } else { - fprintf(stderr, "User: "); - fflush(stderr); - user = cx_str(fgets(ubuf, 256, stdin)); - } - if(!user.ptr) { - return 0; - } - if(user.length > 0 && user.ptr[user.length-1] == '\n') { - user.length--; - } - if(user.length > 0 && user.ptr[user.length-1] == '\r') { - user.length--; - } - - char *password = util_password_input("Password: "); - if(!password || strlen(password) == 0) { - return 0; - } - - dav_session_set_auth_s(sn, user, cx_str(password)); - free(password); - - return 0; -}
--- a/application/connect.h Wed Oct 23 10:37:43 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * 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 DAV_CONNECT_H -#define DAV_CONNECT_H - -#include "config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -DavSession* connect_to_repo(DavContext *ctx, DavCfgRepository *repo, const char *path, dav_auth_func authfunc); - - -#ifdef __cplusplus -} -#endif - -#endif /* DAV_CONNECT_H */ -
--- a/application/davcontroller.c Wed Oct 23 10:37:43 2024 +0200 +++ b/application/davcontroller.c Wed Oct 23 17:40:45 2024 +0200 @@ -93,8 +93,46 @@ char *dav_path = util_concat_path(browser->repo_base, path); ui_set(browser->path, dav_path); free(dav_path); + + SessionAuthData *auth = cxMalloc(sn->mp->allocator, sizeof(SessionAuthData)); + auth->obj = ui; + auth->cond = ui_condvar_create(); + auth->sn = sn; + auth->user = repo->user.value.ptr ? cx_strdup_a(sn->mp->allocator, cx_strcast(repo->user.value)).ptr : NULL; + auth->password = NULL; + dav_session_set_authcallback(sn, jobthr_davbrowser_auth, auth); + + davbrowser_query_path(ui, browser, path); +} - davbrowser_query_path(ui, browser, path); +// ------------------------------ davbrowser_auth ------------------------------ + +static int davbrowser_auth_dialog(void *data) { + SessionAuthData *auth = data; + auth_dialog(auth); + return 0; +} + +void davbrowser_auth_set_user_pwd(SessionAuthData *auth, const char *user, const char *password) { + dav_session_free(auth->sn, auth->user); + dav_session_free(auth->sn, auth->password); + auth->user = user ? dav_session_strdup(auth->sn, user) : NULL; + auth->password = password ? dav_session_strdup(auth->sn, password) : NULL; +} + +int jobthr_davbrowser_auth(DavSession *sn, void *data) { + SessionAuthData *auth = data; + + ui_call_mainthread(davbrowser_auth_dialog, auth); + ui_condvar_wait(auth->cond); + + if(auth->cond->intdata) { + dav_session_set_auth(sn, auth->user, auth->password); + } + dav_session_free(auth->sn, auth->password); + auth->password = NULL; + + return 0; }
--- a/application/davcontroller.h Wed Oct 23 10:37:43 2024 +0200 +++ b/application/davcontroller.h Wed Oct 23 17:40:45 2024 +0200 @@ -45,6 +45,9 @@ DavBrowser* davbrowser_create(UiObject *toplevel); +int jobthr_davbrowser_auth(DavSession *sn, void *data); +void davbrowser_auth_set_user_pwd(SessionAuthData *auth, const char *user, const char *password); + void davbrowser_set_collection(UiObject *ui, DavBrowser *browser, DavResource *collection); void davbrowser_connect2repo(UiObject *ui, DavBrowser *browser, DavCfgRepository *repo, const char *path);
--- a/application/window.c Wed Oct 23 10:37:43 2024 +0200 +++ b/application/window.c Wed Oct 23 17:40:45 2024 +0200 @@ -35,6 +35,8 @@ #include <libidav/utils.h> +#include <cx/printf.h> + static UiIcon* folder_icon; static UiIcon* file_icon; @@ -205,6 +207,64 @@ return NULL; } + +typedef struct AuthDialogWindow { + UiString *user; + UiString *password; +} AuthDialogWindow; + +static void auth_dialog_action(UiEvent *event, void *data) { + SessionAuthData *auth = data; + AuthDialogWindow *wdata = event->window; + int result = 0; + if(event->intval == 4) { + char *user = ui_get(wdata->user); + char *password = ui_get(wdata->password); + davbrowser_auth_set_user_pwd(auth, user, password); + result = 1; + } + ui_condvar_signal(auth->cond, NULL, result); + ui_close(event->obj); +} + +void auth_dialog(SessionAuthData *auth) { + UiObject *obj = ui_dialog_window(auth->obj, + .title = "Authentication", + .lbutton1 = "Cancel", + .rbutton4 = "Connect", + .default_button = 4, + .show_closebutton = UI_OFF, + .onclick = auth_dialog_action, + .onclickdata = auth); + + AuthDialogWindow *wdata = ui_malloc(obj->ctx, sizeof(AuthDialogWindow)); + wdata->user = ui_string_new(obj->ctx, NULL); + wdata->password = ui_string_new(obj->ctx, NULL); + obj->window = wdata; + + ui_grid(obj, .margin = 20, .columnspacing = 12, .rowspacing = 12) { + cxmutstr heading = cx_asprintf("Authentication required for: %s", auth->sn->base_url); + ui_llabel(obj, .label = heading.ptr, .colspan = 2); + free(heading.ptr); + ui_newline(obj); + + ui_llabel(obj, .label = "User"); + ui_textfield(obj, .value = wdata->user, .hexpand = TRUE); + ui_newline(obj); + + ui_llabel(obj, .label = "Password"); + ui_passwordfield(obj, .value = wdata->password, .hexpand = TRUE); + } + + if(auth->user) { + ui_set(wdata->user, auth->user); + } + + ui_show(obj); +} + + + static UiPathElm* dav_get_pathelm(const char *full_path, size_t len, size_t *ret_nelm, void* data) { cxstring fpath = cx_strn(full_path, len); int protocol = 0;
--- a/application/window.h Wed Oct 23 10:37:43 2024 +0200 +++ b/application/window.h Wed Oct 23 17:40:45 2024 +0200 @@ -59,6 +59,9 @@ void resourceviewer_new(DavBrowser *browser, const char *path, DavResourceViewType type); void* resourceviewer_proplist_getvalue(DavPropertyList *property, int col); + +void auth_dialog(SessionAuthData *auth); + void action_go_back(UiEvent *event, void *data); void action_go_forward(UiEvent *event, void *data);
--- a/ucx/mempool.c Wed Oct 23 10:37:43 2024 +0200 +++ b/ucx/mempool.c Wed Oct 23 17:40:45 2024 +0200 @@ -113,6 +113,7 @@ void *p, void *ptr ) { + if (!ptr) return; struct cx_mempool_s *pool = p; struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/condvar.c Wed Oct 23 17:40:45 2024 +0200 @@ -0,0 +1,70 @@ +/* + * 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 "condvar.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); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/condvar.h Wed Oct 23 17:40:45 2024 +0200 @@ -0,0 +1,53 @@ +/* + * 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 */ +
--- a/ui/common/context.c Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/common/context.c Wed Oct 23 17:40:45 2024 +0200 @@ -523,19 +523,28 @@ cxListDestroy(groups); } +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 = cxListSize(groups); - gw.groups = cxCalloc(a, gw.numgroups, sizeof(int)); + gw.numgroups = numgroups; + gw.groups = cxCalloc(a, numgroups, sizeof(int)); // copy groups - int *intgroups = cxListAt(groups, 0); - if(intgroups) { - memcpy(gw.groups, intgroups, gw.numgroups * sizeof(int)); + if(groups) { + memcpy(gw.groups, groups, gw.numgroups * sizeof(int)); } cxListAdd(ctx->group_widgets, &gw); @@ -554,7 +563,7 @@ } void ui_free(UiContext *ctx, void *ptr) { - if(ctx) { + if(ctx && ptr) { cxFree(ctx->allocator, ptr); } }
--- a/ui/common/context.h Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/common/context.h Wed Oct 23 17:40:45 2024 +0200 @@ -134,8 +134,10 @@ 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); #ifdef __cplusplus
--- a/ui/common/objs.mk Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/common/objs.mk Wed Oct 23 17:40:45 2024 +0200 @@ -39,6 +39,7 @@ COMMON_OBJ += toolbar.o COMMON_OBJ += ucx_properties.o COMMON_OBJ += threadpool.o +COMMON_OBJ += condvar.o TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)%) TOOLKITSOURCE += $(COMMON_OBJ:%.o=common/%.c)
--- a/ui/gtk/button.c Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/button.c Wed Oct 23 17:40:45 2024 +0200 @@ -59,7 +59,8 @@ const char *label, const char *icon, ui_callback onclick, - void *userdata) + void *userdata, + int event_value) { GtkWidget *button = gtk_button_new_with_label(label); ui_button_set_icon_name(button, icon); @@ -69,7 +70,7 @@ event->obj = obj; event->userdata = userdata; event->callback = onclick; - event->value = 0; + event->value = event_value; event->customdata = NULL; g_signal_connect( @@ -89,8 +90,9 @@ UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs args) { UiObject* current = uic_current_obj(obj); - GtkWidget *button = ui_create_button(obj, args.label, args.icon, args.onclick, args.onclickdata); + GtkWidget *button = ui_create_button(obj, args.label, args.icon, args.onclick, args.onclickdata, 0); ui_set_name_and_style(button, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, button, args.groups); UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, button, FALSE); return button; @@ -232,6 +234,7 @@ ui_setup_togglebutton(current, widget, args.label, args.icon, args.varname, args.value, args.onchange, args.onchangedata); ui_set_name_and_style(widget, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, widget, args.groups); UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, widget, FALSE); @@ -282,6 +285,9 @@ args.onchange, args.onchangedata); + ui_set_name_and_style(widget, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, widget, args.groups); + UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, widget, FALSE); @@ -343,6 +349,7 @@ 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) {
--- a/ui/gtk/button.h Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/button.h Wed Oct 23 17:40:45 2024 +0200 @@ -46,7 +46,8 @@ const char *label, const char *icon, ui_callback onclick, - void *userdata); + void *userdata, + int event_value); void ui_setup_togglebutton( UiObject *obj,
--- a/ui/gtk/container.c Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/container.c Wed Oct 23 17:40:45 2024 +0200 @@ -242,7 +242,7 @@ -static GtkWidget* box_set_margin(GtkWidget *box, int margin) { +GtkWidget* ui_box_set_margin(GtkWidget *box, int margin) { GtkWidget *ret = box; #if GTK_MAJOR_VERSION >= 3 #if GTK_MAJOR_VERSION * 1000 + GTK_MINOR_VERSION >= 3012 @@ -270,7 +270,7 @@ GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args.spacing) : ui_gtk_hbox_new(args.spacing); ui_set_name_and_style(box, args.name, args.style_class); - GtkWidget *widget = args.margin > 0 ? box_set_margin(box, args.margin) : box; + GtkWidget *widget = args.margin > 0 ? ui_box_set_margin(box, args.margin) : box; ct->add(ct, widget, TRUE); UiObject *newobj = uic_object_new(obj, box); @@ -288,7 +288,7 @@ return ui_box_create(obj, args, UI_CONTAINER_HBOX); } -static GtkWidget* create_grid(int colspacing, int rowspacing) { +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); @@ -306,9 +306,9 @@ UI_APPLY_LAYOUT1(current, args); GtkWidget *widget; - GtkWidget *grid = create_grid(args.columnspacing, args.rowspacing); + GtkWidget *grid = ui_create_grid_widget(args.columnspacing, args.rowspacing); ui_set_name_and_style(grid, args.name, args.style_class); - widget = box_set_margin(grid, args.margin); + widget = ui_box_set_margin(grid, args.margin); current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, grid); @@ -621,13 +621,13 @@ break; } case UI_CONTAINER_GRID: { - sub = create_grid(data->columnspacing, data->rowspacing); + sub = ui_create_grid_widget(data->columnspacing, data->rowspacing); newobj->container = ui_grid_container(newobj, sub); break; } } newobj->widget = sub; - GtkWidget *widget = box_set_margin(sub, data->margin); + GtkWidget *widget = ui_box_set_margin(sub, data->margin); data->add_tab(data->widget, tab_index, name, widget);
--- a/ui/gtk/container.h Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/container.h Wed Oct 23 17:40:45 2024 +0200 @@ -137,11 +137,13 @@ UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame); void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); +GtkWidget* ui_box_set_margin(GtkWidget *box, int margin); UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type); UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type); void ui_box_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); +GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing); UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid); void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill);
--- a/ui/gtk/headerbar.c Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/headerbar.c Wed Oct 23 17:40:45 2024 +0200 @@ -114,7 +114,7 @@ UiObject *obj, enum UiToolbarPos pos) { - GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata); + GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata, 0); WIDGET_ADD_CSS_CLASS(button, "flat"); headerbar_add(headerbar, box, button, pos); }
--- a/ui/gtk/toolkit.c Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/toolkit.c Wed Oct 23 17:40:45 2024 +0200 @@ -166,8 +166,11 @@ } void ui_close(UiObject *obj) { - // TODO - //gtk_widget_destroy(obj->widget); +#if GTK_CHECK_VERSION(4, 0, 0) + gtk_window_close(GTK_WINDOW(obj->widget)); +#else + gtk_widget_destroy(obj->widget); +#endif } @@ -345,6 +348,9 @@ ".pathbar-button-inactive {\n" " color: alpha(currentColor, 0.5);" "}\n" +".ui_test {\n" +" background-color: red;\n" +"}\n" ; #elif GTK_MAJOR_VERSION == 3 @@ -409,3 +415,13 @@ } } + +void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups) { + if(!groups) { + return; + } + + size_t ngroups = uic_group_array_size(groups); + uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); + ui_set_enabled(widget, FALSE); +}
--- a/ui/gtk/toolkit.h Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/toolkit.h Wed Oct 23 17:40:45 2024 +0200 @@ -59,7 +59,7 @@ #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_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 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)) @@ -73,8 +73,8 @@ #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_end(GTK_BOX(box), child, TRUE, TRUE, 0) -#define BOX_ADD_NO_EXPAND(box, child) gtk_box_pack_end(GTK_BOX(box), child, TRUE, FALSE, 0) +#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 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) @@ -159,6 +159,7 @@ 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_destroy_userdata(GtkWidget *object, void *userdata); void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data);
--- a/ui/gtk/window.c Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/gtk/window.c Wed Oct 23 17:40:45 2024 +0200 @@ -42,6 +42,7 @@ #include "toolbar.h" #include "container.h" #include "headerbar.h" +#include "button.h" static int nwindows = 0; @@ -586,3 +587,158 @@ 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) +#else +#define HEADERBAR_SHOW_CLOSEBUTTON(headerbar, set) gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(headerbar), set) +#endif + + + +UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { + GtkWidget *dialog = DIALOG_NEW(); + 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); + } + + CxMempool *mp = cxBasicMempoolCreate(256); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + obj->ctx = uic_context(obj, mp); + obj->widget = dialog; + 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 + 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); + obj->container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX); + 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, args.onclick, args.onclickdata, 1); + gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 1) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + } + } + if(args.lbutton2) { + GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2); + gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 2) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + } + } + + if(args.rbutton4) { + GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4); + gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 4) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + } + } + if(args.rbutton3) { + GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3); + gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 3) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + } + } + 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_box_set_margin(grid, 16); + gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); + + if(args.lbutton1) { + GtkWidget *button = ui_create_button(obj, args.lbutton1, NULL, args.onclick, args.onclickdata, 1); + gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1); + } + if(args.lbutton2) { + GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2); + gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1); + } + GtkWidget *space = gtk_label_new(NULL); + gtk_widget_set_hexpand(space, TRUE); + gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1); + if(args.rbutton3) { + GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3); + gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1); + } + if(args.rbutton3) { + GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4); + gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1); + } + + 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; +}
--- a/ui/ui/button.h Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/ui/button.h Wed Oct 23 17:40:45 2024 +0200 @@ -50,6 +50,8 @@ UiLabelType labeltype; ui_callback onclick; void* onclickdata; + + const int* groups; } UiButtonArgs; typedef struct UiToggleArgs { @@ -69,6 +71,8 @@ const char* varname; ui_callback onchange; void* onchangedata; + + const int* groups; } UiToggleArgs; #define ui_button(obj, ...) ui_button_create(obj, (UiButtonArgs){ __VA_ARGS__ } )
--- a/ui/ui/toolkit.h Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/ui/toolkit.h Wed Oct 23 17:40:45 2024 +0200 @@ -147,7 +147,7 @@ #define UI_GROUP_SELECTION 20000 -#define UI_GROUPS(...) (int[]){ __VA_ARGS__, -1 } +#define UI_GROUPS(...) (const int[]){ __VA_ARGS__, -1 } /* public types */ typedef int UiBool; @@ -202,7 +202,7 @@ typedef void(*ui_freefunc)(void*); -typedef void(*ui_enablefunc)(void*, UiBool); +typedef void(*ui_enablefunc)(void*, int); struct UiObject { /* @@ -402,6 +402,11 @@ size_t nfiles; }; +typedef struct UiCondVar { + void *data; + int intdata; +} UiCondVar; + UIEXPORT void ui_init(const char *appname, int argc, char **argv); UIEXPORT const char* ui_appname(); @@ -539,6 +544,11 @@ UIEXPORT char* ui_getappdir(); UIEXPORT char* ui_configfile(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); + #ifdef __cplusplus } #endif
--- a/ui/ui/window.h Wed Oct 23 10:37:43 2024 +0200 +++ b/ui/ui/window.h Wed Oct 23 17:40:45 2024 +0200 @@ -50,8 +50,32 @@ 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_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);