Thu, 28 Nov 2024 18:03:12 +0100
implement UI for editing properties, relates to #497
--- a/application/Makefile Wed Nov 27 18:53:11 2024 +0100 +++ b/application/Makefile Thu Nov 28 18:03:12 2024 +0100 @@ -41,6 +41,7 @@ SRC += window.c SRC += settings.c SRC += appsettings.c +SRC += xml.c OBJ = $(SRC:%.c=../build/application/%.$(OBJ_EXT)) @@ -50,5 +51,5 @@ $(CC) -o ../build/bin/idav$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -luitk -lucx -lidav $(LDFLAGS) $(TK_LDFLAGS) $(DAV_LDFLAGS) ../build/application/%.$(OBJ_EXT): %.c - $(CC) ../ucx $(CFLAGS) $(TK_CFLAGS) $(DAV_CFLAGS) -o $@ -c $< + $(CC) $(CFLAGS) $(TK_CFLAGS) $(DAV_CFLAGS) -o $@ -c $<
--- a/application/application.h Wed Nov 27 18:53:11 2024 +0100 +++ b/application/application.h Thu Nov 28 18:03:12 2024 +0100 @@ -48,6 +48,8 @@ #define APP_STATE_BROWSER_SELECTION 110 #define RESOURCEVIEWER_STATE_MODIFIED 2000 +#define RESOURCEVIEWER_STATE_PROP_SELECTED 2001 +#define RESOURCEVIEWER_STATE_PROP_XML 2002 typedef struct DavApp { DavConfig *dav_config; @@ -108,12 +110,28 @@ UiList *resources; } DavBrowser; + +/* + * resource property list entry + */ +typedef struct DavPropertyList { + char *ns; + char *name; + char *value_simplified; + char *value_full; + DavXmlNode *xml; + DavBool update; + DavBool isnew; +} DavPropertyList; + /* * resource view window document object */ typedef struct DavResourceViewer { + UiObject *obj; UiContext *ctx; DavSession *sn; + UiThreadpool *dav_queue; DavResource *current; char *path; DavResourceViewType type; @@ -133,23 +151,27 @@ UiString *info_etag; UiString *info_size; + UiInteger *property_type; + UiString *property_ns; + UiString *property_name; + UiString *property_nsdef; + UiText *property_value; + UiString *property_errormsg; + + DavPropertyList *selected_property; + DavPropertyList *edit_property; + int error; char *message_str; CxBuffer *text_content; char *tmp_file; + bool properties_modified; bool loaded; bool window_closed; } DavResourceViewer; -typedef struct DavPropertyList { - char *ns; - char *name; - char *value_simplified; - char *value_full; -} DavPropertyList; - typedef struct SessionAuthData { UiObject *obj; UiCondVar *cond;
--- a/application/davcontroller.c Wed Nov 27 18:53:11 2024 +0100 +++ b/application/davcontroller.c Thu Nov 28 18:03:12 2024 +0100 @@ -44,6 +44,7 @@ DavBrowser* davbrowser_create(UiObject *toplevel) { DavBrowser *doc = ui_document_new(sizeof(DavBrowser)); UiContext *ctx = ui_document_context(doc); + CxMempool *mp = ui_cx_mempool(ctx); doc->window = toplevel; doc->ctx = ctx; @@ -52,6 +53,7 @@ doc->navstack_pos = 0; doc->dav_queue = ui_threadpool_create(1); + cxMempoolRegister(mp, doc->dav_queue, (cx_destructor_func)ui_threadpool_destroy); doc->res_open_inprogress = cxHashMapCreate(ctx->allocator, CX_STORE_POINTERS, 4); @@ -871,12 +873,16 @@ -DavResourceViewer* dav_resourceviewer_create(DavSession *sn, const char *path, DavResourceViewType type) { +DavResourceViewer* dav_resourceviewer_create(UiObject *toplevel, DavSession *sn, const char *path, DavResourceViewType type) { DavResourceViewer *doc = ui_document_new(sizeof(DavResourceViewer)); UiContext *ctx = ui_document_context(doc); + CxMempool *mp = ui_cx_mempool(ctx); + doc->obj = toplevel; doc->ctx = ctx; doc->sn = dav_session_clone(sn); + doc->dav_queue = ui_threadpool_create(1); + cxMempoolRegister(mp, doc->dav_queue, (cx_destructor_func)ui_threadpool_destroy); doc->path = strdup(path); doc->type = type; @@ -896,6 +902,13 @@ doc->info_etag = ui_string_new(ctx, "info_etag"); doc->info_size = ui_string_new(ctx, "info_size"); + doc->property_type = ui_int_new(ctx, NULL); + doc->property_ns = ui_string_new(ctx, NULL); + doc->property_name = ui_string_new(ctx, NULL); + doc->property_nsdef = ui_string_new(ctx, NULL); + doc->property_value = ui_text_new(ctx, NULL); + doc->property_errormsg = ui_string_new(ctx, NULL); + return doc; } @@ -1017,8 +1030,13 @@ DavPropertyList *prop = ui_malloc(doc->ctx, sizeof(DavPropertyList)); prop->ns = properties[i].ns ? ui_strdup(doc->ctx, properties[i].ns) : NULL; prop->name = ui_strdup(doc->ctx, properties[i].name); + prop->value_simplified = NULL; + prop->value_full = NULL; + prop->update = FALSE; + prop->isnew = FALSE; DavXmlNode *xval = dav_get_property_ns(res, prop->ns, prop->name); + prop->xml = xval; if(xval) { if(dav_xml_isstring(xval)) { char *value = dav_xml_getstring(xval); @@ -1090,6 +1108,7 @@ static void uithr_upload_text_finished(UiEvent *event, void *data) { ResourceViewerUploadFile *upload = data; + ui_object_unref(event->obj); if(upload->error) { cxmutstr errormsg = cx_asprintf("Upload failed: %d", upload->sn->error); // TODO: add full error message @@ -1098,31 +1117,129 @@ ui_set_group(event->obj->ctx, RESOURCEVIEWER_STATE_MODIFIED); } - dav_session_destroy(upload->sn); - free(upload->path); free(upload->text.ptr); free(upload); } +static int jobthr_store_properties(void *data) { + DavResourceViewer *res = data; + res->error = dav_store(res->current); + return 0; +} + +static void uithr_store_properties_finished(UiEvent *event, void *data) { + DavResourceViewer *res = data; + ui_object_unref(event->obj); + if(res->error) { + cxmutstr errormsg = cx_asprintf("Proppatch failed: %d", res->sn->error); // TODO: add full error message + ui_dialog(event->obj, .title = "Error", .content = errormsg.ptr, .closebutton_label = "OK"); + free(errormsg.ptr); + ui_set_group(event->obj->ctx, RESOURCEVIEWER_STATE_MODIFIED); + res->properties_modified = TRUE; + } else { + CxList *properties = res->properties->data; + CxIterator i = cxListIterator(properties); + cx_foreach(DavPropertyList *, prop, i) { + prop->update = FALSE; + prop->isnew = FALSE; + } + } +} + void dav_resourceviewer_save(UiObject *ui, DavResourceViewer *res) { if(res->type == DAV_RESOURCE_VIEW_TEXT) { - DavSession *newsn = dav_session_clone(res->current->session); ResourceViewerUploadFile *upload = malloc(sizeof(ResourceViewerUploadFile)); upload->ui = ui; - upload->sn = newsn; - upload->path = strdup(res->current->path); + upload->sn = res->sn; + upload->path = res->current->path; char *text = ui_get(res->text); - size_t textlen = strlen(text); - upload->text = cx_strdup(cx_strn(text, textlen)); - ui_job(ui, jobthr_upload_text, upload, uithr_upload_text_finished, upload); - ui_unset_group(ui->ctx, RESOURCEVIEWER_STATE_MODIFIED); + upload->text = cx_strdup(cx_str(text)); + ui_object_ref(res->obj); + ui_threadpool_job(res->dav_queue, ui, jobthr_upload_text, upload, uithr_upload_text_finished, upload); } + + if(res->properties_modified) { + CxList *properties = res->properties->data; + CxIterator i = cxListIterator(properties); + cx_foreach(DavPropertyList *, prop, i) { + if(prop->update) { + if(prop->value_full) { + // text + dav_set_string_property_ns(res->current, prop->ns, prop->name, prop->value_full); + } else { + // xml + dav_set_property_ns(res->current, prop->ns, prop->name, prop->xml); + } + } + } + + ui_object_ref(res->obj); + ui_threadpool_job(res->dav_queue, ui, jobthr_store_properties, res, uithr_store_properties_finished, res); + } + + ui_unset_group(ui->ctx, RESOURCEVIEWER_STATE_MODIFIED); } void dav_resourceviewer_destroy(DavResourceViewer *res) { } +void dav_resourceviewer_property_remove(DavResourceViewer *res, DavPropertyList *prop) { + if(!prop->isnew) { + dav_remove_property_ns(res->current, prop->ns, prop->name); + ui_set_group(res->obj->ctx, RESOURCEVIEWER_STATE_MODIFIED); + res->properties_modified = TRUE; + } + + CxList *properties = res->properties->data; + cxListFindRemove(properties, prop); + ui_free(res->ctx, prop->ns); + ui_free(res->ctx, prop->name); + ui_free(res->ctx, prop->value_simplified); + ui_free(res->ctx, prop->value_full); + // prop->xml freed by DavSession + ui_free(res->ctx, prop); + ui_list_update(res->properties); +} + +void dav_resourceviewer_property_update_text(DavResourceViewer *res, DavPropertyList *prop, const char *text) { + ui_free(res->ctx, prop->value_simplified); + ui_free(res->ctx, prop->value_full); + prop->xml = NULL; + prop->value_full = ui_strdup(res->ctx, text); + prop->value_simplified = NULL; + prop->update = TRUE; + + ui_set_group(res->obj->ctx, RESOURCEVIEWER_STATE_MODIFIED); + res->properties_modified = TRUE; + ui_list_update(res->properties); +} + +void dav_resourceviewer_property_update_xml(DavResourceViewer *res, DavPropertyList *prop, DavXmlNode *xml) { + +} + +void dav_resourceviewer_property_add_text(DavResourceViewer *res, const char *ns, const char *name, const char *text) { + DavPropertyList *prop = ui_malloc(res->ctx, sizeof(DavPropertyList)); + prop->ns = ui_strdup(res->ctx, ns); + prop->name = ui_strdup(res->ctx, name); + prop->value_simplified = NULL; + prop->value_full = ui_strdup(res->ctx, text); + prop->xml = NULL; + prop->isnew = TRUE; + prop->update = TRUE; + + ui_list_append(res->properties, prop); + ui_set_group(res->obj->ctx, RESOURCEVIEWER_STATE_MODIFIED); + res->properties_modified = TRUE; + ui_list_update(res->properties); +} + +void dav_resourceviewer_property_add_xml(DavResourceViewer *res, const char *ns, const char *name, const char *nsdef, DavXmlNode *xml) { + +} + + uint64_t dav_transfer_speed(TransferProgress *progress, time_t current) { size_t bytes = progress->transferred_bytes - progress->speedtest_bytes;
--- a/application/davcontroller.h Wed Nov 27 18:53:11 2024 +0100 +++ b/application/davcontroller.h Thu Nov 28 18:03:12 2024 +0100 @@ -91,7 +91,7 @@ void davbrowser_rename(UiObject *ui, DavBrowser *browser, UiListSelection selection); -DavResourceViewer* dav_resourceviewer_create(DavSession *sn, const char *path, DavResourceViewType type); +DavResourceViewer* dav_resourceviewer_create(UiObject *toplevel, DavSession *sn, const char *path, DavResourceViewType type); void dav_resourceviewer_load(UiObject *ui, DavResourceViewer *res); @@ -99,6 +99,13 @@ void dav_resourceviewer_destroy(DavResourceViewer *res); +void dav_resourceviewer_property_remove(DavResourceViewer *res, DavPropertyList *prop); +void dav_resourceviewer_property_update_text(DavResourceViewer *res, DavPropertyList *prop, const char *text); +void dav_resourceviewer_property_update_xml(DavResourceViewer *res, DavPropertyList *prop, DavXmlNode *xml); +void dav_resourceviewer_property_add_text(DavResourceViewer *res, const char *ns, const char *name, const char *text); +void dav_resourceviewer_property_add_xml(DavResourceViewer *res, const char *ns, const char *name, const char *nsdef, DavXmlNode *xml); + + uint64_t dav_transfer_speed(TransferProgress *progress, time_t current); #ifdef __cplusplus
--- a/application/main.c Wed Nov 27 18:53:11 2024 +0100 +++ b/application/main.c Thu Nov 28 18:03:12 2024 +0100 @@ -46,6 +46,7 @@ application_init(); ui_onstartup(application_startup, NULL); + ui_main(); sys_uninit();
--- a/application/window.c Wed Nov 27 18:53:11 2024 +0100 +++ b/application/window.c Thu Nov 28 18:03:12 2024 +0100 @@ -30,6 +30,7 @@ #include "davcontroller.h" #include "appsettings.h" +#include "xml.h" #include <ui/stock.h> #include <ui/dnd.h> @@ -188,17 +189,13 @@ UiObject *win = ui_simple_window(name, NULL); ui_window_size(win, 600, 600); - // TODO: when properties can be modified, always add the headerbar - if(type == DAV_RESOURCE_VIEW_TEXT) { - ui_headerbar(win, .showtitle = TRUE) { - ui_headerbar_start(win) { - ui_button(win, .label = "Save", .style_class = "suggested-action", .onclick = action_resourceviewer_save, .groups = UI_GROUPS(RESOURCEVIEWER_STATE_MODIFIED)); - } + ui_headerbar(win, .showtitle = TRUE) { + ui_headerbar_start(win) { + ui_button(win, .label = "Save", .onclick = action_resourceviewer_save, .groups = UI_GROUPS(RESOURCEVIEWER_STATE_MODIFIED)); } } - - DavResourceViewer *doc = dav_resourceviewer_create(browser->sn, path, type); + DavResourceViewer *doc = dav_resourceviewer_create(win, browser->sn, path, type); ui_attach_document(win->ctx, doc); ui_context_closefunc(win->ctx, resourceviewer_close, doc); @@ -255,7 +252,12 @@ ui_tab(win, "Properties") { UiModel* model = ui_model(win->ctx, UI_STRING, "Namespace", UI_STRING, "Name", UI_STRING, "Value", -1); model->getvalue = (ui_getvaluefunc) resourceviewer_proplist_getvalue; - ui_table(win, .fill = UI_ON, .model = model, .varname = "properties"); + ui_table(win, .fill = UI_ON, .model = model, .varname = "properties", .onselection = action_resourceviewer_property_select, .onactivate = action_resourceviewer_property_activate); + ui_hbox(win, .fill = UI_OFF, .margin = 4, .spacing = 4) { + ui_button(win, .label = "Add", .onclick = action_resourceviewer_property_add); + ui_button(win, .label = "Edit", .onclick = action_resourceviewer_property_edit, .groups = UI_GROUPS(RESOURCEVIEWER_STATE_PROP_SELECTED)); + ui_button(win, .label = "Remove", .onclick = action_resourceviewer_property_remove, .groups = UI_GROUPS(RESOURCEVIEWER_STATE_PROP_SELECTED)); + } } } } @@ -545,3 +547,196 @@ DavResourceViewer *doc = event->document; dav_resourceviewer_save(event->obj, doc); } + +void action_resourceviewer_property_select(UiEvent *event, void *data) { + DavResourceViewer *doc = event->document; + UiListSelection *selection = event->eventdata; + if(selection->count == 1) { + ui_set_group(event->obj->ctx, RESOURCEVIEWER_STATE_PROP_SELECTED); + doc->selected_property = ui_list_get(doc->properties, selection->rows[0]); + } else { + ui_unset_group(event->obj->ctx, RESOURCEVIEWER_STATE_PROP_SELECTED); + doc->selected_property = NULL; + } +} + +void action_resourceviewer_property_activate(UiEvent *event, void *data) { + action_resourceviewer_property_select(event, data); + action_resourceviewer_property_edit(event, data); +} + + +typedef struct PropertyDialog { + UiInteger *type; + UiString *ns; + UiString *name; + UiText *value; +} PropertyDialog; + +static void propertydialog_action(UiEvent *event, void *data) { + DavResourceViewer *res = data; + if(event->intval == 4) { + char *ns = ui_get(res->property_ns); + char *name = ui_get(res->property_name); + int type = ui_get(res->property_type); + char *nsdef = ui_get(res->property_nsdef); + char *value = ui_get(res->property_value); + + if(strlen(ns) == 0) { + ui_set(res->property_errormsg, "Namespace must not be empty!"); + return; + } + if(strlen(name) == 0) { + ui_set(res->property_errormsg, "Name must not be empty!"); + return; + } + + char *textvalue = NULL; + DavXmlNode *xmlvalue = NULL; + if(type == 0) { + // text value + textvalue = value; + } else { + // xml value + } + + DavBool add = FALSE; + if(res->edit_property) { + if(strcmp(res->edit_property->ns, ns) || strcmp(res->edit_property->name, name)) { + // name or namespace changed, remove existing and create new property + dav_resourceviewer_property_remove(res, res->edit_property); + add = TRUE; + } + } else { + add = TRUE; + } + + if(add) { + if(textvalue) { + dav_resourceviewer_property_add_text(res, ns, name, textvalue); + } else { + dav_resourceviewer_property_add_xml(res, ns, name, nsdef, xmlvalue); + } + } else { + if(textvalue) { + dav_resourceviewer_property_update_text(res, res->edit_property, textvalue); + } else { + dav_resourceviewer_property_update_xml(res, res->edit_property, xmlvalue); + } + } + } + ui_close(event->obj); +} + +static void prop_type_changed(UiEvent *event, void *data) { + DavResourceViewer *res = data; + switch(ui_get(res->property_type)) { + case 0: { + ui_unset_group(event->obj->ctx, RESOURCEVIEWER_STATE_PROP_XML); + break; + } + case 1: { + ui_set_group(event->obj->ctx, RESOURCEVIEWER_STATE_PROP_XML); + char *ns = ui_get(res->property_ns); + char *nsdef = ui_get(res->property_nsdef); + if(strlen(nsdef) == 0) { + cxmutstr def = cx_asprintf("xmlns:x0=\"%s\"", ns); + ui_set(res->property_nsdef, def.ptr); + free(def.ptr); + } + + break; + } + } +} + +static void edit_property_dialog(DavResourceViewer *res, const char *title, DavPropertyList *prop) { + res->edit_property = prop; + + UiObject *obj = ui_dialog_window(res->obj, + .title = title, + .show_closebutton = UI_OFF, + .lbutton1 = "Cancel", + .rbutton4 = "Save", + .default_button = 4, + .onclick = propertydialog_action, + .onclickdata = res, + .width = 600, + .height = 500); + + ui_grid(obj, .margin = 16, .columnspacing = 8, .rowspacing = 12) { + ui_llabel(obj, .label = "Namespace"); + ui_textfield(obj, .hexpand = TRUE, .value = res->property_ns); + ui_newline(obj); + + ui_llabel(obj, .label = "Property Name"); + ui_textfield(obj, .hexpand = TRUE, .value = res->property_name); + ui_newline(obj); + + ui_llabel(obj, .label = "Type"); + ui_hbox(obj, .spacing = 8, .colspan = 2) { + ui_radiobutton(obj, .label = "Text", .value = res->property_type, .onchange = prop_type_changed, .onchangedata = res); + ui_radiobutton(obj, .label = "XML", .value = res->property_type, .onchange = prop_type_changed, .onchangedata = res); + } + ui_newline(obj); + + ui_llabel(obj, .label = "Namespace Declarations"); + ui_textfield(obj, .hexpand = TRUE, .value = res->property_nsdef, .groups = UI_GROUPS(RESOURCEVIEWER_STATE_PROP_XML)); + ui_newline(obj); + + ui_textarea(obj, .value = res->property_value, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2); + ui_newline(obj); + + ui_llabel(obj, .colspan = 2, .value = res->property_errormsg); + } + + if(prop && prop->ns && prop->name) { + ui_set(res->property_ns, prop->ns); + ui_set(res->property_name, prop->name); + if(prop->value_full) { + ui_set(res->property_type, 0); + ui_set(res->property_nsdef, ""); + ui_set(res->property_value, prop->value_full); + ui_unset_group(obj->ctx, RESOURCEVIEWER_STATE_PROP_XML); + } else if(prop->xml) { + ui_set(res->property_type, 1); + cxmutstr xml; + cxmutstr nsdef; + property_xml2str(prop->xml, prop->ns, &xml, &nsdef); + ui_set(res->property_nsdef, nsdef.ptr); + ui_set(res->property_value, xml.ptr); + free(xml.ptr); + free(nsdef.ptr); + ui_set_group(obj->ctx, RESOURCEVIEWER_STATE_PROP_XML); + } + } else { + ui_set(res->property_ns, ""); + ui_set(res->property_name, ""); + ui_set(res->property_nsdef, ""); + ui_set(res->property_type, 0); + ui_set(res->property_value, ""); + } + + ui_set(res->property_errormsg, ""); + + ui_show(obj); +} + +void action_resourceviewer_property_add(UiEvent *event, void *data) { + DavResourceViewer *doc = event->document; + edit_property_dialog(doc, "Add Property", NULL); +} + +void action_resourceviewer_property_edit(UiEvent *event, void *data) { + DavResourceViewer *doc = event->document; + edit_property_dialog(doc, "Edit Property", doc->selected_property); +} + +void action_resourceviewer_property_remove(UiEvent *event, void *data) { + DavResourceViewer *doc = event->document; + if(!doc->selected_property) { + return; // shouldn't happen + } + dav_resourceviewer_property_remove(doc, doc->selected_property); +} +
--- a/application/window.h Wed Nov 27 18:53:11 2024 +0100 +++ b/application/window.h Thu Nov 28 18:03:12 2024 +0100 @@ -80,6 +80,11 @@ void action_resourceviewer_text_modified(UiEvent *event, void *data); void action_resourceviewer_save(UiEvent *event, void *data); +void action_resourceviewer_property_select(UiEvent *event, void *data); +void action_resourceviewer_property_activate(UiEvent *event, void *data); +void action_resourceviewer_property_add(UiEvent *event, void *data); +void action_resourceviewer_property_edit(UiEvent *event, void *data); +void action_resourceviewer_property_remove(UiEvent *event, void *data); #ifdef __cplusplus }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/xml.c Thu Nov 28 18:03:12 2024 +0100 @@ -0,0 +1,79 @@ +/* + * 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 <cx/printf.h> +#include <cx/hash_map.h> +#include <cx/buffer.h> + +#include <libidav/xml.h> + +#include "xml.h" + + + + +void property_xml2str(DavXmlNode *content, const char *namespace, cxmutstr *out_xmlstr, cxmutstr *out_nsdef) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 2048, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + + CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + cxMapPut(nsmap, namespace, strdup("x0")); + + dav_print_node(&buf, (cx_write_func)cxBufferWrite, nsmap, content); + + cxmutstr ret = cx_mutstrn(buf.space, buf.size); + cxBufferPut(&buf, 0); + + + CxBuffer nsbuf; + cxBufferInit(&nsbuf, NULL, 2048, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + CxIterator i = cxMapIterator(nsmap); + int addSpace = 0; + cx_foreach(CxMapEntry *, entry, i) { + const char *ns = entry->key->data; + const char *pre = entry->value; + if(addSpace) { + cxBufferPut(&nsbuf, ' '); + } else { + addSpace = 1; + } + cx_bprintf(&nsbuf, "xmlns:%s=\"%s\"", pre, ns); + } + cxmutstr nsdef = cx_mutstrn(nsbuf.space, nsbuf.size); + cxBufferPut(&nsbuf, 0); + + // cleanup namespace map + cxMapDestroy(nsmap); + + + *out_xmlstr = ret; + *out_nsdef = nsdef; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/xml.h Thu Nov 28 18:03:12 2024 +0100 @@ -0,0 +1,47 @@ +/* + * 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 APP_XML_H +#define APP_XML_H + +#include <libidav/webdav.h> +#include <cx/string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void property_xml2str(DavXmlNode *content, const char *namespace, cxmutstr *out_xmlstr, cxmutstr *out_nsdef); + + +#ifdef __cplusplus +} +#endif + +#endif /* APP_XML_H */ +
--- a/libidav/methods.c Wed Nov 27 18:53:11 2024 +0100 +++ b/libidav/methods.c Thu Nov 28 18:03:12 2024 +0100 @@ -836,7 +836,7 @@ { char prefix[8]; int pfxnum = 0; - if (data->set) { + if (data->set && cxListSize(data->set) > 0) { CxIterator i = cxListIterator(data->set); cx_foreach(DavProperty*, p, i) { if (strcmp(p->ns->name, "DAV:")) { @@ -845,7 +845,7 @@ } } } - if (data->remove) { + if (data->remove && cxListSize(data->remove) > 0) { CxIterator i = cxListIterator(data->remove); cx_foreach(DavProperty*, p, i) { if (strcmp(p->ns->name, "DAV:")) {
--- a/libidav/resource.c Wed Nov 27 18:53:11 2024 +0100 +++ b/libidav/resource.c Thu Nov 28 18:03:12 2024 +0100 @@ -1043,7 +1043,7 @@ // store properties int r = 0; sn->error = DAV_OK; - if(data->set || data->remove) { + if(data->set || data->remove > 0) { CxBuffer *request = create_proppatch_request(data); CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); //printf("request:\n%.*s\n\n", request->pos, request->space);
--- a/libidav/xml.c Wed Nov 27 18:53:11 2024 +0100 +++ b/libidav/xml.c Thu Nov 28 18:03:12 2024 +0100 @@ -174,8 +174,8 @@ prefix = cxMapGet(nsmap, cx_hash_key_str(node->namespace)); if(!prefix) { cxmutstr newpre = cx_asprintf("x%zu", cxMapSize(nsmap)+1); - // TODO: fix namespace declaration - //ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr); + // TODO: fix + //cxMapPut(nsmap, node->namespace, newpre.ptr); prefix = newpre.ptr; prefix_fr = prefix; cx_fprintf(
--- a/ui/common/document.c Wed Nov 27 18:53:11 2024 +0100 +++ b/ui/common/document.c Thu Nov 28 18:03:12 2024 +0100 @@ -86,12 +86,13 @@ CxMempool *mp = cxMempoolCreate(256, NULL); const CxAllocator *a = mp->allocator; UiContext *ctx = cxCalloc(a, 1, sizeof(UiContext)); + ctx->mp = mp; ctx->attach_document = uic_context_attach_document; ctx->detach_document2 = uic_context_detach_document2; ctx->allocator = a; ctx->vars = cxHashMapCreate(a, CX_STORE_POINTERS, 16); - void *document = cxCalloc(a, 1, size); + void *document = cxCalloc(a, size, 1); cxMapPut(documents, cx_hash_key(&document, sizeof(void*)), ctx); return document; }
--- a/ui/gtk/button.c Wed Nov 27 18:53:11 2024 +0100 +++ b/ui/gtk/button.c Thu Nov 28 18:03:12 2024 +0100 @@ -391,6 +391,23 @@ event->callback(&e, event->userdata); } +typedef struct UiRadioButtonData { + UiInteger *value; + UiVarEventData *eventdata; + UiBool first; +} UiRadioButtonData; + +static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) { + ui_destroy_vardata(w, data->eventdata); + if(data->first) { + g_slist_free(data->value->obj); + data->value->obj = NULL; + data->value->get = NULL; + data->value->set = NULL; + } + free(data); +} + UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs args) { UiObject* current = uic_current_obj(obj); @@ -435,18 +452,21 @@ 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); - if(first) { - g_signal_connect( + g_signal_connect( rbutton, "destroy", - G_CALLBACK(ui_destroy_vardata), - event); - } + G_CALLBACK(destroy_radiobutton), + rbdata); } if(args.onchange) {
--- a/ui/gtk/text.c Wed Nov 27 18:53:11 2024 +0100 +++ b/ui/gtk/text.c Thu Nov 28 18:03:12 2024 +0100 @@ -112,8 +112,8 @@ current->container->add(current->container, scroll_area, TRUE); // bind value - UiText *value = var->value; - if(value) { + if(var) { + UiText *value = var->value; GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_area)); if(value->value.ptr) { @@ -165,7 +165,9 @@ } void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea) { - ui_destroy_boundvar(textarea->ctx, textarea->var); + if(textarea->var) { + ui_destroy_boundvar(textarea->ctx, textarea->var); + } free(textarea); }