--- 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); +} +