Mon, 29 Jan 2024 18:50:04 +0100
implement connecting to repositories
--- a/application/application.c Mon Jan 29 12:09:24 2024 +0100 +++ b/application/application.c Mon Jan 29 18:50:04 2024 +0100 @@ -35,6 +35,7 @@ #include "window.h" #include "config.h" +#include "davcontroller.h" static DavContext* davctx; @@ -51,8 +52,15 @@ exit(-1); } + window_init(); + + // create new window UiObject *win = window_create(); + // create new browser document and attach it to the main window + DavBrowser *doc = davbrowser_create(win); + ui_attach_document(win->ctx, doc); + // create document for global settings (repolist, ...) DavApp *app = application_create_app_document(); UiContext *global = ui_global_context(); ui_attach_document(global, app); @@ -60,6 +68,11 @@ ui_show(win); } +static void* davrepo_getname(void *elm, int unused) { + DavCfgRepository *repo = elm; + return repo->name.value.ptr; +} + void application_create_menu(void) { // menu @@ -68,7 +81,7 @@ ui_toolbar_item("Home", .icon = "Home"); ui_toolbar_item("NewWindow", .icon = "NewWindow"); ui_toolbar_menu("Repo", .label = "Repository") { - ui_menu_itemlist(.varname = "repolist", .onselect = action_repo_selected); + ui_menu_itemlist(.varname = "repolist", .getvalue = davrepo_getname, .onselect = action_repo_selected); } ui_toolbar_item("Refresh", .icon = "Refresh"); ui_toolbar_item("NewFolder", .icon = "NewFolder"); @@ -115,13 +128,10 @@ DavConfig *config = get_config(); DavCfgRepository *repo = config->repositories; - - // TODO: free list content ptr ui_list_clear(app->repos); for (DavCfgRepository *repo = config->repositories; repo; repo = repo->next) { - // TODO: copy repo name - ui_list_append(app->repos, repo->name.value.ptr); + ui_list_append(app->repos, repo); } } @@ -132,5 +142,7 @@ void action_repo_selected(UiEvent *event, void *data) { - + DavCfgRepository *repo = event->eventdata; + DavBrowser *browser = event->document; + davbrowser_connect2repo(event->obj, browser, repo); }
--- a/application/application.h Mon Jan 29 12:09:24 2024 +0100 +++ b/application/application.h Mon Jan 29 18:50:04 2024 +0100 @@ -47,6 +47,28 @@ UiList *repos; } DavApp; +/* + * main window document object + */ +typedef struct DavBrowser { + UiContext *ctx; + DavSession *sn; + UiThreadpool *dav_queue; + + DavCfgRepository *repository; + + DavResource *current; + + /* + * path textfield value + */ + UiString *path; + + /* + * children of the current collection + */ + UiList *resources; +} DavBrowser; void application_init(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/davcontroller.c Mon Jan 29 18:50:04 2024 +0100 @@ -0,0 +1,147 @@ +/* +* 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 "davcontroller.h" +#include "window.h" + +#include <cx/printf.h> + +#include <libidav/utils.h> + +DavBrowser* davbrowser_create(UiObject *toplevel) { + DavBrowser *doc = ui_document_new(sizeof(DavBrowser)); + UiContext *ctx = ui_document_context(doc); + doc->ctx = ctx; + + doc->dav_queue = ui_threadpool_create(1); + + doc->path = ui_string_new(ctx, "path"); + doc->resources = ui_list_new(ctx, "reslist"); + + return doc; +} + + +void davbrowser_set_collection(UiObject *ui, DavBrowser *browser, DavResource *collection) { + ui_list_clear(browser->resources); + + for (DavResource *res = collection->children; res; res = res->next) { + ui_list_append(browser->resources, res); + } + + browser->resources->update(browser->resources, 0); +} + +// ------------------------------ davbrowser_connect2repo ------------------------------ + +static int connect2repo(void *data) { + DavBrowser *browser = data; + DavCfgRepository *repo = browser->repository; + + // TODO: cleanup current session + DavSession *sn = dav_session_new(application_dav_context(), repo->url.value.ptr); + if (repo->user.value.ptr && repo->password.value.ptr) { + cxmutstr decodedpw = dav_repository_get_decodedpassword(repo); + dav_session_set_auth(sn, repo->user.value.ptr, decodedpw.ptr); + free(decodedpw.ptr); + } + browser->sn = sn; + + return 0; +} + +static void repo_connected(UiEvent *event, void *data) { + DavBrowser *browser = event->document; + ui_set(browser->path, browser->repository->name.value.ptr); + + davbrowser_query_path(event->obj, browser, "/"); +} + +void davbrowser_connect2repo(UiObject *ui, DavBrowser *browser, DavCfgRepository *repo) { + browser->repository = repo; + ui_threadpool_job(browser->dav_queue, ui, connect2repo, browser, repo_connected, NULL); +} + + +// ------------------------------ davbrowser_query_path ------------------------------ + +typedef struct DavBrowserQueryPath { + DavBrowser *browser; + char *path; + DavResource *result; +} DavBrowserQueryPath; + +static int browser_query_path(void *data) { + DavBrowserQueryPath *query = data; + DavSession *sn = query->browser->sn; + + DavResource *res = dav_query(sn, "select `idav:crypto-name`,`idav:crypto-key`,D:lockdiscovery from %s with depth = 1 order by iscollection desc, name", query->path); + query->result = res; + + return 0; +} + +static void browser_query_finished(UiEvent *event, void *data) { + DavBrowserQueryPath *query = data; + DavBrowser *browser = event->document; + + if (query->result) { + davbrowser_set_collection(event->obj, browser, query->result); + } else { + // TODO: error + } + + free(query->path); + free(query); + + window_progress(event->window, 0); +} + +void davbrowser_query_path(UiObject *ui, DavBrowser *browser, const char *path) { + // for comparison, we need the current base_url/repo_name + path + size_t len = path ? strlen(path) : 0; + if (len == 1 && *path == '/') { + path = ""; + } + char *full_path = util_concat_path(browser->repository->name.value.ptr, path); + // compare new path with current path + // in theory we could always set path, but maybe this is faster + char *current_path = ui_get(browser->path); + if (strcmp(full_path, current_path)) { + ui_set(browser->path, full_path); + } + free(full_path); + + DavBrowserQueryPath *query = malloc(sizeof(DavBrowserQueryPath)); + query->browser = browser; + query->path = strdup(path); + query->result = NULL; + ui_threadpool_job(browser->dav_queue, ui, browser_query_path, query, browser_query_finished, query); + + window_progress(ui->window, 1); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/davcontroller.h Mon Jan 29 18:50:04 2024 +0100 @@ -0,0 +1,58 @@ +/* +* 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 IDAV_DAV_CONTROLLER_H +#define IDAV_DAV_CONTROLLER_H + +#include <ui/ui.h> +#include <libidav/webdav.h> +#include <libidav/config.h> + +#include "application.h" + +#ifdef __cplusplus +extern "C" { +#endif + +DavBrowser* davbrowser_create(UiObject *toplevel); + +void davbrowser_set_collection(UiObject *ui, DavBrowser *browser, DavResource *collection); + +void davbrowser_connect2repo(UiObject *ui, DavBrowser *browser, DavCfgRepository *repo); + +void davbrowser_query_path(UiObject *ui, DavBrowser *browser, const char *path); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* IDAV_DAV_CONTROLLER_H */ +
--- a/application/window.c Mon Jan 29 12:09:24 2024 +0100 +++ b/application/window.c Mon Jan 29 18:50:04 2024 +0100 @@ -28,8 +28,17 @@ #include "window.h" +#include "davcontroller.h" + #include <ui/stock.h> +static UiIcon* folder_icon; +static UiIcon* file_icon; + +void window_init(void) { + folder_icon = ui_foldericon(16); + file_icon = ui_fileicon(16); +} UiObject* window_create(void) { UiObject* obj = ui_window("iDAV", NULL); @@ -38,19 +47,22 @@ memset(wdata, 0, sizeof(MainWindow)); obj->window = wdata; - wdata->dav_queue = ui_threadpool_create(1); + wdata->progress = ui_int_new(obj->ctx, "progress"); // navigation bar ui_hbox(obj, .fill = UI_OFF, .margin = 8) { ui_button(obj, .icon = "Back"); ui_button(obj, .icon = "Forward"); - ui_path_textfield(obj, .fill = UI_ON); + ui_path_textfield(obj, .fill = UI_ON, .varname = "path"); + + ui_progressspinner(obj, .value = wdata->progress); } // main content UiModel* model = ui_model(obj->ctx, UI_ICON_TEXT, "Name", UI_STRING, "Type", UI_STRING, "Last Modified", UI_STRING, "Size", -1); - ui_table(obj, .fill = UI_ON, .model = model); + model->getvalue = (ui_getvaluefunc)window_resource_table_getvalue; + ui_table(obj, .fill = UI_ON, .model = model, .varname = "reslist"); // status bar ui_hbox(obj, .fill = UI_OFF) { @@ -61,3 +73,28 @@ return obj; } + +void* window_resource_table_getvalue(DavResource *res, int col) { + switch (col) { + case 0: { // icon + return res->iscollection ? folder_icon : file_icon; + } + case 1: { // resource name + return res->name; + } + case 2: { // type + return ""; // TODO + } + case 3: { // last modified + return ""; // TODO + } + case 4: { // size + return ""; // TODO + } + } + return NULL; +} + +void window_progress(MainWindow *win, int on) { + ui_set(win->progress, on); +}
--- a/application/window.h Mon Jan 29 12:09:24 2024 +0100 +++ b/application/window.h Mon Jan 29 18:50:04 2024 +0100 @@ -31,6 +31,9 @@ #include <ui/ui.h> #include <libidav/webdav.h> +#include <libidav/config.h> + +#include "application.h" #ifdef __cplusplus extern "C" { @@ -40,12 +43,16 @@ * toplevel UiObject* window data */ typedef struct MainWindow { - DavSession *sn; - UiThreadpool *dav_queue; + UiInteger *progress; } MainWindow; +void window_init(void); + UiObject* window_create(void); +void* window_resource_table_getvalue(DavResource *res, int col); + +void window_progress(MainWindow *win, int on); #ifdef __cplusplus
--- a/make/vs/idav/idav.vcxproj Mon Jan 29 12:09:24 2024 +0100 +++ b/make/vs/idav/idav.vcxproj Mon Jan 29 18:50:04 2024 +0100 @@ -162,6 +162,7 @@ <ItemGroup> <ClCompile Include="..\..\..\application\application.c" /> <ClCompile Include="..\..\..\application\config.c" /> + <ClCompile Include="..\..\..\application\davcontroller.c" /> <ClCompile Include="..\..\..\application\main.c" /> <ClCompile Include="..\..\..\application\pwd.c" /> <ClCompile Include="..\..\..\application\system.c" /> @@ -170,6 +171,7 @@ <ItemGroup> <ClInclude Include="..\..\..\application\application.h" /> <ClInclude Include="..\..\..\application\config.h" /> + <ClInclude Include="..\..\..\application\davcontroller.h" /> <ClInclude Include="..\..\..\application\pwd.h" /> <ClInclude Include="..\..\..\application\system.h" /> <ClInclude Include="..\..\..\application\window.h" />
--- a/make/vs/idav/idav.vcxproj.filters Mon Jan 29 12:09:24 2024 +0100 +++ b/make/vs/idav/idav.vcxproj.filters Mon Jan 29 18:50:04 2024 +0100 @@ -37,6 +37,9 @@ <ClCompile Include="..\..\..\application\pwd.c"> <Filter>src</Filter> </ClCompile> + <ClCompile Include="..\..\..\application\davcontroller.c"> + <Filter>src</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\application\application.h"> @@ -54,5 +57,8 @@ <ClInclude Include="..\..\..\application\pwd.h"> <Filter>src</Filter> </ClInclude> + <ClInclude Include="..\..\..\application\davcontroller.h"> + <Filter>src</Filter> + </ClInclude> </ItemGroup> </Project> \ No newline at end of file
--- a/ui/common/context.c Mon Jan 29 12:09:24 2024 +0100 +++ b/ui/common/context.c Mon Jan 29 18:50:04 2024 +0100 @@ -316,12 +316,19 @@ case UI_VAR_LIST: { // TODO: not sure how correct this is + UiList *f = from->value; + UiList *t = to->value; + if (f->obj) { + t->obj = f->obj; + t->update = f->update; + } + UiVar tmp = *from; *from = *to; *to = tmp; - UiList* t = to->value; - ui_notify(t->observers, NULL); + UiList* t2 = to->value; + ui_notify(t2->observers, NULL); break; }
--- a/ui/common/menu.c Mon Jan 29 12:09:24 2024 +0100 +++ b/ui/common/menu.c Mon Jan 29 18:50:04 2024 +0100 @@ -196,6 +196,7 @@ 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);
--- a/ui/common/menu.h Mon Jan 29 12:09:24 2024 +0100 +++ b/ui/common/menu.h Mon Jan 29 18:50:04 2024 +0100 @@ -107,10 +107,11 @@ }; struct UiMenuItemList { - UiMenuItemI item; - ui_callback callback; - void *userdata; - const char *varname; + UiMenuItemI item; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; + const char *varname; };
--- a/ui/ui/menu.h Mon Jan 29 12:09:24 2024 +0100 +++ b/ui/ui/menu.h Mon Jan 29 18:50:04 2024 +0100 @@ -61,6 +61,7 @@ typedef struct UiMenuItemListArgs { const char* varname; + ui_getvaluefunc getvalue; ui_callback onselect; void* onselectdata; } UiMenuItemListArgs;
--- a/ui/winui/appmenu.cpp Mon Jan 29 12:09:24 2024 +0100 +++ b/ui/winui/appmenu.cpp Mon Jan 29 18:50:04 2024 +0100 @@ -165,11 +165,13 @@ class UiMenuList { public: + UiObject *obj = nullptr; winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent = { nullptr }; UiMenuItemType type; int prevSize = 0; int insertPos = 0; UiVar* var = nullptr; + ui_getvaluefunc getvalue = nullptr; ui_callback callback = nullptr; void* userdata = nullptr; @@ -189,12 +191,26 @@ int count = 0; void* elm = list->first(list); while (elm) { + char *menuItemLabel = (char*) (getvalue ? getvalue(elm, 0) : elm); MenuFlyoutItem mi = MenuFlyoutItem(); - wchar_t* wlabel = str2wstr((char*)elm, NULL); + wchar_t* wlabel = str2wstr(menuItemLabel ? menuItemLabel : "", NULL); mi.Text(wlabel); free(wlabel); + if (callback) { + mi.Click([this, elm, count](Windows::Foundation::IInspectable const& sender, RoutedEventArgs const& e) + { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = elm; + evt.intval = count; + callback(&evt, userdata); + }); + } + parent.InsertAt(insertPos + count, mi); elm = list->next(list); @@ -235,7 +251,9 @@ UiVar* var = uic_create_var(ui_global_context(), it->varname, UI_VAR_LIST); UiMenuList* mlist = new UiMenuList(); + mlist->obj = obj; mlist->parent = parent; + mlist->getvalue = it->getvalue; mlist->callback = it->callback; mlist->userdata = it->userdata; mlist->prevSize = 0;
--- a/ui/winui/text.cpp Mon Jan 29 12:09:24 2024 +0100 +++ b/ui/winui/text.cpp Mon Jan 29 18:50:04 2024 +0100 @@ -452,7 +452,7 @@ ui_context_add_widget_destructor(current->ctx, widget); // bind var - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); if (var) { UiString* value = (UiString*)var->value; value->obj = uipathbar;