# HG changeset patch # User Olaf Wintermann # Date 1732729508 -3600 # Node ID e92c72705da4a4275051a75291e8830d98edf372 # Parent 7fdf1489b82fa4d59d7495ed538a227bf030012e add upload/download list, resolves #512 diff -r 7fdf1489b82f -r e92c72705da4 application/application.c --- a/application/application.c Wed Nov 27 17:14:57 2024 +0100 +++ b/application/application.c Wed Nov 27 18:45:08 2024 +0100 @@ -113,6 +113,10 @@ ui_menu_itemlist(.varname = "repolist", .getvalue = davrepo_getname, .onselect = action_repo_selected); } } + + ui_menu("View") { + ui_menu_itemlist(.varname = "transferlist", .getvalue = transfers_getlabel, .onselect = action_transfer_selected); + } // toolbar ui_toolbar_item("Home", .icon = UI_ICON_HOME); @@ -138,6 +142,10 @@ 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)); ui_menuseparator(); + ui_menu("Downloads/Uploads") { + ui_menu_itemlist(.varname = "transferlist", .getvalue = transfers_getlabel, .onselect = action_transfer_selected); + } + ui_menuseparator(); ui_menuitem("Preferences", .onclick = action_open_settings); } @@ -162,7 +170,10 @@ DavApp *doc = ui_document_new(sizeof(DavApp)); UiContext *ctx = ui_document_context(doc); doc->repos = ui_list_new(ctx, "repolist"); - + doc->transfers = ui_list_new(ctx, "transferlist"); + CxList *transfers = doc->transfers->data; + transfers->collection.cmpfunc = cx_cmp_ptr; + // create repo list application_update_repolist(doc); @@ -186,6 +197,44 @@ return davctx; } +void* transfers_getlabel(void *data, int col) { + DavTransfer *trans = data; + return trans->label; +} + +void application_register_transfer(DavTransfer *trans) { + DavApp *app = get_application(); + ui_list_append(app->transfers, trans); + application_update_transferlist(); +} + +void application_remove_transfer(DavTransfer *trans) { + DavApp *app = get_application(); + CxList *transfers = app->transfers->data; + cxListFindRemove(transfers, trans); + application_update_transferlist(); +} + +/* +static int transfers_cmp(const DavTransfer *left, const DavTransfer *right) { + return cx_cmp_double(&right->progress, &left->progress); +} +*/ + +void application_update_transferlist(void) { + DavApp *app = get_application(); + + /* + CxList *transfers = app->transfers->data; + transfers->collection.cmpfunc = (cx_compare_func)transfers_cmp; + cxListSort(transfers); + transfers->collection.cmpfunc = cx_cmp_ptr; + */ + + ui_list_update(app->transfers); + ui_list_notify(app->transfers); +} + void action_window_new(UiEvent *event, void *data) { UiObject *win = window_create(); @@ -390,3 +439,7 @@ } ui_listselection_free(sel); } + +void action_transfer_selected(UiEvent *event, void *data) { + +} diff -r 7fdf1489b82f -r e92c72705da4 application/application.h --- a/application/application.h Wed Nov 27 17:14:57 2024 +0100 +++ b/application/application.h Wed Nov 27 18:45:08 2024 +0100 @@ -52,8 +52,17 @@ typedef struct DavApp { DavConfig *dav_config; UiList *repos; + UiList *transfers; } DavApp; +// download/upload +typedef struct DavTransfer { + char *label; + size_t label_len; + double progress; + UiObject *window; + time_t last_update; +} DavTransfer; typedef enum DavResourceViewType { DAV_RESOURCE_VIEW_PROPERTIES = 0, @@ -67,6 +76,8 @@ * main window document object */ typedef struct DavBrowser { + UiObject *window; + UiContext *ctx; DavSession *sn; UiThreadpool *dav_queue; @@ -162,6 +173,12 @@ DavApp* get_application(void); +void* transfers_getlabel(void *data, int col); + +void application_register_transfer(DavTransfer *trans); +void application_remove_transfer(DavTransfer *trans); +void application_update_transferlist(void); + DavApp* application_create_app_document(void); void application_update_repolist(DavApp *app); @@ -196,6 +213,8 @@ void action_open_properties(UiEvent *event, void *data); +void action_transfer_selected(UiEvent *event, void *data); + #ifdef __cplusplus } #endif diff -r 7fdf1489b82f -r e92c72705da4 application/davcontroller.c --- a/application/davcontroller.c Wed Nov 27 17:14:57 2024 +0100 +++ b/application/davcontroller.c Wed Nov 27 18:45:08 2024 +0100 @@ -44,6 +44,7 @@ DavBrowser* davbrowser_create(UiObject *toplevel) { DavBrowser *doc = ui_document_new(sizeof(DavBrowser)); UiContext *ctx = ui_document_context(doc); + doc->window = toplevel; doc->ctx = ctx; doc->navigation_stack = cxLinkedListCreateSimple(CX_STORE_POINTERS); @@ -468,6 +469,7 @@ DavFileUpload *upload = dav_upload_create(browser, dialog, files); transfer_window_init(dialog, action_upload_cancel); dav_upload_start(upload); + application_register_transfer(&upload->trans); } void davbrowser_download(UiObject *ui, DavBrowser *browser, DavResource *reslist, const char *local_path) { @@ -478,6 +480,7 @@ DavFileDownload *download = dav_download_create(browser, dialog, reslist, local_path); transfer_window_init(dialog, action_download_cancel); dav_download_start(download); + application_register_transfer(&download->trans); } diff -r 7fdf1489b82f -r e92c72705da4 application/download.c --- a/application/download.c Wed Nov 27 17:14:57 2024 +0100 +++ b/application/download.c Wed Nov 27 18:45:08 2024 +0100 @@ -48,6 +48,8 @@ return 1; } + time_t t = time(NULL); + char *sz_total = util_size_str(FALSE, download->progress.total_bytes); char *sz_downloaded = util_size_str2(FALSE, download->progress.transferred_bytes, download->progress.total_bytes, 2); char *sz_downloaded_end = strchr(sz_downloaded, ' '); @@ -56,8 +58,16 @@ } if (download->progress.total_bytes > 0) { - double progress = (double)download->progress.transferred_bytes / (double)download->progress.total_bytes; - ui_set(download->progressbar, progress*100); + double progress = ((double)download->progress.transferred_bytes / (double)download->progress.total_bytes)*100; + ui_set(download->progressbar, progress); + download->trans.progress = progress; + /* + if(t > download->trans.last_update + 2) { + snprintf(download->trans.label+download->trans.label_len, 12, " %d%%", (int)progress); + application_update_transferlist(); + download->trans.last_update = t; + } + */ } @@ -81,7 +91,6 @@ free(label1.ptr); time_t start = download->progress.speedtest_start; - time_t t = time(NULL); if(t >= download->progress.speedtest_start + 4) { uint64_t bytesPerSeconds = dav_transfer_speed(&download->progress, t); if(start > 0) { @@ -263,13 +272,6 @@ } -static void download_window_closed(UiEvent *event, void *data) { - DavFileDownload *download = event->obj->window; - - dav_session_destroy(download->sn); - ui_threadpool_destroy(download->queue); -} - void action_download_cancel(UiEvent *event, void *data) { DavFileDownload *download = event->window; if(!download->cancel) { @@ -278,14 +280,25 @@ } } +static void dav_file_download_cleanup(DavFileDownload *download) { + application_remove_transfer(&download->trans); + ui_object_unref(download->browser->window); +} DavFileDownload* dav_download_create(DavBrowser *browser, UiObject *dialog, DavResource *reslist, const char *local_path) { UiContext *ctx = dialog->ctx; + CxMempool *mp = ui_cx_mempool(ctx); DavFileDownload *download = ui_malloc(ctx, sizeof(DavFileDownload)); memset(download, 0, sizeof(DavFileDownload)); download->dialog = dialog; dialog->window = download; ui_object_ref(dialog); + ui_object_ref(browser->window); + + size_t label_len = strlen(reslist->name) + 16; + download->trans.label = cxCalloc(mp->allocator, label_len, 1); + download->trans.label_len = snprintf(download->trans.label, label_len, "< %s%s", reslist->name, reslist->next ? " ..." : ""); + download->trans.window = dialog; download->browser = browser; download->sn = reslist->session; @@ -295,9 +308,9 @@ download->queue = ui_threadpool_create(1); - CxMempool *mp = ui_cx_mempool(ctx); cxMempoolRegister(mp, download->download_sn, (cx_destructor_func)dav_session_destroy); cxMempoolRegister(mp, download->queue, (cx_destructor_func)ui_threadpool_destroy); + cxMempoolSetDestructor(download, (cx_destructor_func)dav_file_download_cleanup); download->progressbar = ui_double_new(ctx, "progressbar"); download->label_top_left = ui_string_new(ctx, "label_top_left"); diff -r 7fdf1489b82f -r e92c72705da4 application/download.h --- a/application/download.h Wed Nov 27 17:14:57 2024 +0100 +++ b/application/download.h Wed Nov 27 18:45:08 2024 +0100 @@ -38,6 +38,7 @@ typedef struct DavFileDownload { + DavTransfer trans; DavBrowser *browser; DavSession *sn; diff -r 7fdf1489b82f -r e92c72705da4 application/upload.c --- a/application/upload.c Wed Nov 27 17:14:57 2024 +0100 +++ b/application/upload.c Wed Nov 27 18:45:08 2024 +0100 @@ -57,6 +57,15 @@ double progress = upload_progress(upload); ui_set(upload->progressbar, progress); + upload->trans.progress = progress; + time_t t = time(NULL); + /* + if(t > upload->trans.last_update + 2) { + snprintf(upload->trans.label+upload->trans.label_len, 12, " %d%%", (int)progress); + application_update_transferlist(); + upload->trans.last_update = t; + } + */ cxmutstr label1; if (upload->progress.total_files + upload->progress.total_directories > 1) { @@ -84,7 +93,6 @@ } time_t start = upload->progress.speedtest_start; - time_t t = time(NULL); if(t >= upload->progress.speedtest_start + 4) { uint64_t bytesPerSeconds = dav_transfer_speed(&upload->progress, t); if(start > 0) { @@ -407,13 +415,25 @@ } } +static void dav_file_upload_cleanup(DavFileUpload *upload) { + application_remove_transfer(&upload->trans); + ui_object_unref(upload->browser->window); +} + DavFileUpload* dav_upload_create(DavBrowser *browser, UiObject *obj, UiFileList files) { UiContext *ctx = obj->ctx; + CxMempool *mp = ui_cx_mempool(ctx); DavFileUpload *upload = ui_malloc(ctx, sizeof(DavFileUpload)); memset(upload, 0, sizeof(DavFileUpload)); upload->dialog = obj; obj->window = upload; ui_object_ref(obj); + ui_object_ref(browser->window); + + size_t label_len = strlen(util_resource_name(files.files[0])) + 16; + upload->trans.label = cxCalloc(mp->allocator, label_len, 1); + upload->trans.label_len = snprintf(upload->trans.label, label_len, "> %s%s", util_resource_name(files.files[0]), files.nfiles > 1 ? " ..." : ""); + upload->trans.window = obj; upload->progressbar = ui_double_new(ctx, "progressbar"); upload->label_top_left = ui_string_new(ctx, "label_top_left"); @@ -436,9 +456,9 @@ upload->collection = browser->current; upload->collection_ctn = browser->res_counter; - CxMempool *mp = ui_cx_mempool(ctx); cxMempoolRegister(mp, upload_session, (cx_destructor_func)dav_session_destroy); cxMempoolRegister(mp, upload->queue, (cx_destructor_func)ui_threadpool_destroy); + cxMempoolSetDestructor(upload, (cx_destructor_func)dav_file_upload_cleanup); ui_set(upload->label_top_left, ""); ui_set(upload->label_top_right, ""); diff -r 7fdf1489b82f -r e92c72705da4 application/upload.h --- a/application/upload.h Wed Nov 27 17:14:57 2024 +0100 +++ b/application/upload.h Wed Nov 27 18:45:08 2024 +0100 @@ -37,6 +37,7 @@ #endif typedef struct DavFileUpload { + DavTransfer trans; DavBrowser *browser; DavSession *sn; UiFileList files; diff -r 7fdf1489b82f -r e92c72705da4 application/window.c --- a/application/window.c Wed Nov 27 17:14:57 2024 +0100 +++ b/application/window.c Wed Nov 27 18:45:08 2024 +0100 @@ -87,7 +87,6 @@ } ui_path_textfield(obj, .fill = UI_ON, .getpathelm = dav_get_pathelm, .onactivate = action_path_selected, .varname = "path"); - ui_progressspinner(obj, .value = wdata->progress); } diff -r 7fdf1489b82f -r e92c72705da4 ui/common/types.c --- a/ui/common/types.c Wed Nov 27 17:14:57 2024 +0100 +++ b/ui/common/types.c Wed Nov 27 18:45:08 2024 +0100 @@ -544,3 +544,36 @@ } free(list.files); } + + +typedef struct UiObserverDestructor { + UiList *list; + UiObserver *observer; +} UiObserverDestructor; + +static void observer_destructor(UiObserverDestructor *destr) { + UiObserver *remove_obs = destr->observer; + UiObserver *obs = destr->list->observers; + UiObserver *prev = NULL; + while(obs) { + if(obs == remove_obs) { + if(prev) { + prev->next = obs->next; + } else { + destr->list->observers = obs->next; + } + break; + } + prev = obs; + obs = obs->next; + } + free(remove_obs); +} + +void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObserver *observer) { + CxMempool *mp = ctx->mp; + UiObserverDestructor *destr = cxMalloc(mp->allocator, sizeof(UiObserverDestructor)); + destr->list = list; + destr->observer = observer; + cxMempoolSetDestructor(destr, (cx_destructor_func)observer_destructor); +} diff -r 7fdf1489b82f -r e92c72705da4 ui/common/types.h --- a/ui/common/types.h Wed Nov 27 17:14:57 2024 +0100 +++ b/ui/common/types.h Wed Nov 27 18:45:08 2024 +0100 @@ -58,6 +58,8 @@ void uic_range_unbind(UiRange *r); void uic_list_unbind(UiList *l); void uic_generic_unbind(UiGeneric *g); + +void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObserver *observer); #ifdef __cplusplus } diff -r 7fdf1489b82f -r e92c72705da4 ui/gtk/menu.c --- a/ui/gtk/menu.c Wed Nov 27 17:14:57 2024 +0100 +++ b/ui/gtk/menu.c Wed Nov 27 18:45:08 2024 +0100 @@ -35,6 +35,7 @@ #include "toolkit.h" #include "../common/context.h" #include "../common/menu.h" +#include "../common/types.h" #include "../ui/properties.h" #include "../ui/window.h" #include "container.h" @@ -252,10 +253,9 @@ ls->callback = il->callback; ls->userdata = il->userdata; - ls->list->observers = ui_add_observer( - ls->list->observers, - (ui_callback)ui_update_menuitem_list, - ls); + UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls); + list->observers = ui_obsvlist_add(list->observers, observer); + uic_list_register_observer_destructor(obj->ctx, list, observer); ui_update_menuitem_list(NULL, ls); } @@ -510,10 +510,9 @@ ls->callback = il->callback; ls->userdata = il->userdata; - list->observers = ui_add_observer( - list->observers, - (ui_callback)ui_update_gmenu_item_list, - ls); + UiObserver *observer = ui_observer_new((ui_callback)ui_update_gmenu_item_list, ls); + list->observers = ui_obsvlist_add(list->observers, observer); + uic_list_register_observer_destructor(obj->ctx, list, observer); GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i")); g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));