implement connecting to repositories

Mon, 29 Jan 2024 18:50:04 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 29 Jan 2024 18:50:04 +0100
changeset 8
726b24766437
parent 7
905ac52c910f
child 9
0676408f50ad

implement connecting to repositories

application/application.c file | annotate | diff | comparison | revisions
application/application.h file | annotate | diff | comparison | revisions
application/davcontroller.c file | annotate | diff | comparison | revisions
application/davcontroller.h file | annotate | diff | comparison | revisions
application/window.c file | annotate | diff | comparison | revisions
application/window.h file | annotate | diff | comparison | revisions
make/vs/idav/idav.vcxproj file | annotate | diff | comparison | revisions
make/vs/idav/idav.vcxproj.filters file | annotate | diff | comparison | revisions
ui/common/context.c file | annotate | diff | comparison | revisions
ui/common/menu.c file | annotate | diff | comparison | revisions
ui/common/menu.h file | annotate | diff | comparison | revisions
ui/ui/menu.h file | annotate | diff | comparison | revisions
ui/winui/appmenu.cpp file | annotate | diff | comparison | revisions
ui/winui/text.cpp file | annotate | diff | comparison | revisions
--- 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;

mercurial