#include "window.h"
#include "davcontroller.h"
#include "appsettings.h"
#include "xml.h"
#include <ui/stock.h>
#include <ui/dnd.h>
#include <libidav/utils.h>
#include <cx/printf.h>
static UiIcon* folder_icon;
static UiIcon* file_icon;
static UiPathElm* dav_get_pathelm(
const char *full_path,
size_t len,
size_t *ret_nelm,
void* data);
static UiMenuBuilder *contextmenu;
void window_init(
void) {
folder_icon = ui_foldericon(
16);
file_icon = ui_fileicon(
16);
ui_contextmenu(&contextmenu) {
ui_menuitem(.label =
"New Folder", .onclick = action_mkcol, .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION));
ui_menuitem(.label =
"New File", .onclick = action_newfile, .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION));
ui_menuseparator();
ui_menuitem(.label =
"Download", .onclick = action_download, .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION,
APP_STATE_BROWSER_SELECTION));
ui_menuitem(.label =
"Delete", .onclick = action_delete, .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION));
ui_menuitem(.label =
"Select All", .onclick = action_selectall, .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION));
ui_menuseparator();
ui_menuitem(.label =
"Rename", .onclick = action_rename, .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION,
APP_STATE_BROWSER_SELECTION));
ui_menuseparator();
ui_menuitem(
"Open Properties", .onclick = action_open_properties, .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION,
APP_STATE_BROWSER_SELECTION));
ui_menuitem(
"Open as Text File", .onclick = action_open_properties, .onclickdata =
"text/plain", .groups =
UI_GROUPS(
APP_STATE_BROWSER_SESSION,
APP_STATE_BROWSER_SELECTION));
}
}
UiObject* window_create(
void) {
UiObject* obj = ui_window(
"iDAV",
NULL);
ui_window_size(obj,
900,
700);
MainWindow* wdata = ui_malloc(obj->ctx,
sizeof (MainWindow));
memset(wdata,
0,
sizeof (MainWindow));
obj->window = wdata;
wdata->progress = ui_int_new(obj->ctx,
"progress");
ui_hbox(obj, .fill =
UI_OFF, .margin =
8, .spacing =
8) {
ui_hbox(obj, .fill =
UI_OFF, .style_class=
"linked") {
ui_button(obj, .icon =
UI_ICON_GO_BACK, .onclick = action_go_back);
ui_button(obj, .icon =
UI_ICON_GO_FORWARD, .onclick = action_go_forward);
}
ui_path_textfield(obj, .fill =
UI_ON, .getpathelm = dav_get_pathelm, .onactivate = action_path_selected, .varname =
"path");
ui_progressspinner(obj, .value = wdata->progress);
}
UiModel* model = ui_model(obj->ctx,
UI_ICON_TEXT,
"Name",
UI_STRING_FREE,
"Flags",
UI_STRING,
"Type",
UI_STRING_FREE,
"Last Modified",
UI_STRING_FREE,
"Size", -
1);
model->columnsize[
0] = -
1;
model->columnsize[
2] =
150;
model->getvalue = (ui_getvaluefunc) window_resource_table_getvalue;
ui_table(obj,
.fill =
UI_ON,
.model = model,
.onselection = action_list_selection,
.onactivate = action_list_activate,
.ondragstart = action_dnd_start,
.ondragcomplete = action_dnd_end,
.ondrop = action_dnd_drop,
.varname =
"reslist",
.multiselection =
TRUE,
.contextmenu = contextmenu);
ui_hbox(obj, .fill =
UI_OFF) {
ui_label(obj, .label =
"");
}
return obj;
}
void* window_resource_table_getvalue(DavResource *res,
int col) {
switch (col) {
case 0: {
return res->iscollection ? folder_icon : file_icon;
}
case 1: {
return res->name;
}
case 2: {
char *keyprop = dav_get_string_property_ns(
res,
DAV_NS,
"crypto-key");
DavXmlNode *lockdiscovery = dav_get_property(res,
"D:lockdiscovery");
char *executable = dav_get_string_property_ns(
res,
"http://apache.org/dav/props/",
"executable");
cxmutstr flags = cx_asprintf(
"%s%s%s",
appsettings_get_cryptoflag(keyprop ?
1 :
0),
appsettings_get_lockflag(lockdiscovery ?
1 :
0),
appsettings_get_execflag(executable ?
1 :
0));
return flags.ptr;
}
case 3: {
return res->iscollection ?
"Collection" : (res->contenttype ? res->contenttype :
"Resource");
}
case 4: {
return util_date_str(res->lastmodified);
}
case 5: {
return util_size_str(res->iscollection, res->contentlength);
}
}
return NULL;
}
void window_progress(MainWindow *win,
int on) {
ui_set(win->progress, on);
}
void action_dnd_start(UiEvent *event,
void *data) {
char *uri =
"file:///export/home/olaf/test.txt";
ui_selection_seturis(event->eventdata, &uri,
1);
}
void action_dnd_end(UiEvent *event,
void *data) {
}
static void resourceviewer_close(UiEvent *event,
void *data) {
DavResourceViewer *doc = data;
doc->window_closed =
TRUE;
if(doc->loaded) {
dav_resourceviewer_destroy(doc);
}
if (doc->tmp_file) {
unlink(doc->tmp_file);
free(doc->tmp_file);
}
}
void resourceviewer_new(DavBrowser *browser,
const char *path, DavResourceViewType type) {
const char *name = util_resource_name(path);
UiObject *win = ui_simple_window(name,
NULL);
ui_window_size(win,
600,
600);
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(win, browser->sn, path, type);
ui_attach_document(win->ctx, doc);
ui_context_closefunc(win->ctx, resourceviewer_close, doc);
ui_tabview(win, .tabview =
UI_TABVIEW_INVISIBLE, .varname =
"tabview") {
ui_tab(win,
NULL) {
ui_hbox(win, .margin =
16, .spacing =
10, .fill =
UI_OFF) {
ui_progressspinner(win, .varname =
"loading");
ui_label(win, .varname =
"message");
}
}
ui_tab(win,
NULL) {
ui_tabview0(win) {
if(type ==
DAV_RESOURCE_VIEW_TEXT) {
ui_tab(win,
"Content") {
ui_textarea(win, .varname =
"text", .onchange = action_resourceviewer_text_modified);
}
}
else if(type ==
DAV_RESOURCE_VIEW_IMAGE) {
ui_tab(win,
"Preview") {
ui_imageviewer(win, .varname =
"image");
}
}
ui_tab(win,
"Info") {
ui_grid(win, .margin =
16, .columnspacing =
30, .rowspacing =
6) {
ui_llabel(win, .label =
"URL");
ui_llabel(win, .varname =
"info_url");
ui_newline(win);
ui_llabel(win, .label =
"Name");
ui_llabel(win, .varname =
"info_name");
ui_newline(win);
ui_llabel(win, .label =
"Type");
ui_llabel(win, .varname =
"info_type");
ui_newline(win);
ui_llabel(win, .label =
"Encrypted");
ui_llabel(win, .varname =
"info_encrypted");
ui_newline(win);
ui_llabel(win, .label =
"ETag");
ui_llabel(win, .varname =
"info_etag");
ui_newline(win);
ui_llabel(win, .label =
"Size");
ui_llabel(win, .varname =
"info_size");
ui_newline(win);
}
}
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", .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));
}
}
}
}
}
dav_resourceviewer_load(win, doc);
ui_show(win);
}
void* resourceviewer_proplist_getvalue(DavPropertyList *property,
int col) {
switch(col) {
case 0: {
return property->ns;
}
case 1: {
return property->name;
}
case 2: {
return property->value_simplified ? property->value_simplified : property->value_full;
}
}
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);
}
void transfer_window_init(UiObject *dialog, ui_callback btncallback) {
ui_window_size(dialog,
550,
120);
ui_grid(dialog, .margin =
10, .spacing =
10, .fill =
TRUE) {
ui_llabel(dialog, .varname =
"label_top_left", .hexpand =
TRUE);
ui_rlabel(dialog, .varname =
"label_top_right");
ui_newline(dialog);
ui_progressbar(dialog, .varname =
"progressbar", .min =
0, .max =
100, .colspan =
2, .hexpand =
TRUE);
ui_newline(dialog);
ui_llabel(dialog, .varname =
"label_bottom_left", .hexpand =
TRUE);
ui_rlabel(dialog, .varname =
"label_bottom_right");
ui_newline(dialog);
ui_label(dialog, .vexpand =
TRUE);
ui_newline(dialog);
ui_hbox(dialog, .colspan =
2, .hexpand =
TRUE) {
ui_label(dialog, .hexpand =
TRUE);
ui_button(dialog, .label =
"Cancel", .onclick = btncallback);
}
}
}
static UiPathElm* dav_get_pathelm(
const char *full_path,
size_t len,
size_t *ret_nelm,
void* data) {
if (len ==
0) {
*ret_nelm =
0;
return NULL;
}
cxstring fpath = cx_strn(full_path, len);
int protocol =
0;
if (cx_strcaseprefix(fpath,
CX_STR(
"http://"))) {
protocol =
7;
}
else if (cx_strcaseprefix(fpath,
CX_STR(
"https://"))) {
protocol =
8;
}
size_t start =
0;
size_t end =
0;
for (
size_t i = protocol; i < len; i++) {
if (full_path[i] ==
'/') {
end = i;
break;
}
}
int skip =
0;
if (end ==
0) {
end = len >
0 && full_path[
0] ==
'/' ?
1 : len;
}
else if (end +
1 <= len) {
skip++;
}
cxmutstr base = cx_strdup(cx_strn(full_path, end));
cxmutstr base_path = cx_strcat(
2, cx_strcast(base),
CX_STR(
"/"));
cxstring path = cx_strsubs(fpath, end + skip);
cxstring trail = cx_str(len >
0 && full_path[len-
1] ==
'/' ?
"/" :
"");
cxstring *pathelms;
size_t nelm =
0;
if (path.length >
0) {
nelm = cx_strsplit_a(cxDefaultAllocator, path,
CX_STR(
"/"),
4096, &pathelms);
if (nelm ==
0) {
*ret_nelm =
0;
return NULL;
}
}
UiPathElm* elms = (UiPathElm*) calloc(nelm +
1,
sizeof (UiPathElm));
size_t n = nelm +
1;
elms[
0].name = base.ptr;
elms[
0].name_len = base.length;
elms[
0].path = base_path.ptr;
elms[
0].path_len = base_path.length;
int j =
1;
for (
int i =
0; i < nelm; i++) {
cxstring c = pathelms[i];
if (c.length ==
0) {
if (i ==
0) {
c.length =
1;
}
else {
n--;
continue;
}
}
cxmutstr m = cx_strdup(c);
elms[j].name = m.ptr;
elms[j].name_len = m.length;
size_t elm_path_len = c.ptr + c.length - full_path;
cxmutstr elm_path = cx_strcat(
2, cx_strn(full_path, elm_path_len), i+
1 < nelm ?
CX_STR(
"/") : trail);
elms[j].path = elm_path.ptr;
elms[j].path_len = elm_path.length;
j++;
}
*ret_nelm = n;
return elms;
}
void action_go_parent(UiEvent *event,
void *data) {
DavBrowser *browser = event->document;
davbrowser_navigation_parent(event->obj, browser);
}
void action_go_back(UiEvent *event,
void *data) {
DavBrowser *browser = event->document;
davbrowser_navigation_back(event->obj, browser);
}
void action_go_forward(UiEvent *event,
void *data) {
DavBrowser *browser = event->document;
davbrowser_navigation_forward(event->obj, browser);
}
void action_path_selected(UiEvent *event,
void *data) {
DavBrowser *browser = event->document;
char *path = event->eventdata;
if (path && strlen(path) >
0) {
davbrowser_query_url(event->obj, browser, path);
}
}
void action_list_selection(UiEvent *event,
void *data) {
UiListSelection *selection = event->eventdata;
if (selection->count >
0) {
ui_set_group(event->obj->ctx,
APP_STATE_BROWSER_SELECTION);
}
else {
ui_unset_group(event->obj->ctx,
APP_STATE_BROWSER_SELECTION);
}
}
void action_list_activate(UiEvent *event,
void *data) {
UiListSelection *selection = event->eventdata;
DavBrowser *browser = event->document;
if (selection->count ==
1) {
DavResource *res = ui_list_get(browser->resources, selection->rows[
0]);
if (res) {
if (res->iscollection) {
davbrowser_query_path(event->obj, browser, res->path);
}
else {
davbrowser_open_resource(event->obj, browser, res,
NULL);
}
}
}
}
static int filelist_uri2path(UiFileList *files) {
for(
int i=
0;i<files->nfiles;i++) {
char *uri = files->files[i];
if(uri[
0] ==
'/') {
continue;
}
cxstring uri_s = cx_str(uri);
if(!cx_strprefix(uri_s,
CX_STR(
"file://"))) {
return 1;
}
files->files[i] = cx_strdup(cx_strsubs(uri_s,
7)).ptr;
free(uri);
}
return 0;
}
void action_dnd_drop(UiEvent *event,
void *data) {
UiDnD *dnd = event->eventdata;
UiFileList files = ui_selection_geturis(dnd);
if(files.nfiles ==
0) {
return;
}
if(filelist_uri2path(&files)) {
ui_dnd_accept(dnd,
FALSE);
ui_filelist_free(files);
return;
}
davbrowser_upload_files(event->obj, event->document, files);
}
void action_resourceviewer_text_modified(UiEvent *event,
void *data) {
DavResourceViewer *doc = event->document;
if(doc->loaded) {
ui_set_group(event->obj->ctx,
RESOURCEVIEWER_STATE_MODIFIED);
}
}
void action_resourceviewer_save(UiEvent *event,
void *data) {
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) {
textvalue = value;
}
else {
}
DavBool add =
FALSE;
if(res->edit_property) {
if(strcmp(res->edit_property->ns, ns) || strcmp(res->edit_property->name, name)) {
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;
}
dav_resourceviewer_property_remove(doc, doc->selected_property);
}