diff --git a/TODO.md b/TODO.md index c1ca79e..d5a1f62 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,6 @@ - [ ] File upload multiline description (window) - [ ] Separate common structures in the common.h - [ ] Sorting in the file list -- [ ] More intuitive navigation between nested groups (by pressing left and right buttons) - [ ] Cancel upload/download - [x] Input: fix non-Latin letters - [x] Get rid of unsigned integers @@ -30,6 +29,7 @@ - [x] UTF-8 support - [ ] Check MD5 sum after download/upload - [ ] Hotkeys for buttons +- [ ] Manipulation by UI-cursor ## Fixes: - [x] Check and fix registration and login diff --git a/src/file_processor.c b/src/file_processor.c index 8aa348e..d6c0358 100644 --- a/src/file_processor.c +++ b/src/file_processor.c @@ -110,6 +110,7 @@ void file_download(file_args_t *f_args, query_args_t *q_args) { fl_item_t *f_selected = &(f_args->f_selected); static int32_t it_count = 0; static size_t it_interval = 0; + if (it_interval == 0) { it_interval = (f_selected->size / INBUFSIZE / 100) * 5; /* every 1% */ if (it_interval == 0) @@ -161,6 +162,7 @@ void file_download(file_args_t *f_args, query_args_t *q_args) { int32_t file_upload_open(char *dpath, char *fname, query_args_t *q_args) { q_args->file = malloc(sizeof(p_file_t)); q_args->file->path = malloc(strlen(dpath) + strlen(fname) + 2); + q_args->file->signal = sig_continue; sprintf(q_args->file->path, "%s/%s", dpath, fname); int fd = open(q_args->file->path, O_RDONLY); if (fd == -1) { @@ -192,6 +194,8 @@ int32_t file_upload_start(query_args_t *q_args) { int32_t file_upload(query_args_t *q_args) { static int32_t it_count = 0; static size_t it_interval = 0; + static int package_rest = 0; + int send_len = q_args->buf_used; if (it_interval == 0) { it_interval = (q_args->file->size / INBUFSIZE / 100) * 5; /* every 1% */ if (it_interval == 0) @@ -201,10 +205,30 @@ int32_t file_upload(query_args_t *q_args) { (q_args->file->size - q_args->file->rest) * 100 / q_args->file->size; w_pgb_ui_t *pb = (w_pgb_ui_t *)q_args->progress_bar; w_dialogue_t *d = (w_dialogue_t *)q_args->active_dialogue; + + if (package_rest == 0) { + if (q_args->file->signal == sig_cancel) { + /* reset function before exit */ + it_count = 0; + it_interval = 0; + return -2; + } + package_rest = PACKAGE_SIZE; + s_file_pd_t fpd = {.signal = q_args->file->signal, + .package_size = package_rest}; + write(q_args->sd, &fpd, sizeof(s_file_pd_t)); + } else if (package_rest < send_len) { + send_len = package_rest; + } + if (q_args->buf_used > 0) { - int wlen = write(q_args->sd, q_args->buf, q_args->buf_used); + int wlen = write(q_args->sd, q_args->buf, send_len); if (q_args->buf_used != wlen) { /* TODO: ошибка */ + /* reset function before exit */ + it_count = 0; + it_interval = 0; + package_rest = 0; return -1; } else { it_count++; @@ -212,13 +236,43 @@ int32_t file_upload(query_args_t *q_args) { pb->procent = progress; d->needs_update = true; } + q_args->file->rest -= wlen; + package_rest -= wlen; + if (package_rest == 0) { + if (q_args->file->signal == sig_cancel) { + /* reset function before exit */ + it_count = 0; + it_interval = 0; + return -2; + } + package_rest = PACKAGE_SIZE; + s_file_pd_t fpd = {.signal = q_args->file->signal, + .package_size = package_rest}; + write(q_args->sd, &fpd, sizeof(s_file_pd_t)); + /* send the rest */ + if (q_args->buf_used > send_len) { + wlen = write(q_args->sd, q_args->buf + send_len, + q_args->buf_used - send_len); + if (q_args->buf_used != wlen) { + /* TODO: ошибка */ + /* reset function before exit */ + it_count = 0; + it_interval = 0; + package_rest = 0; + return -1; + } + q_args->file->rest -= wlen; + package_rest -= wlen; + } + } q_args->buf[0] = 0; q_args->buf_used = 0; - q_args->file->rest -= wlen; } } else { + /* reset function before exit */ it_count = 0; it_interval = 0; + package_rest = 0; return 1; } return 0; diff --git a/src/file_processor.h b/src/file_processor.h index 200645d..ae085e4 100644 --- a/src/file_processor.h +++ b/src/file_processor.h @@ -3,6 +3,7 @@ #ifndef FILE_PROCESSOR_H #define FILE_PROCESSOR_H + #include "main.h" void file_list(file_args_t *f_args, query_args_t *q_args); diff --git a/src/main.h b/src/main.h index 1500b1f..f4adaba 100644 --- a/src/main.h +++ b/src/main.h @@ -17,6 +17,7 @@ #define DIALOGUE_TITLE 64 #define DIALOGUE_TEXT 4096 #define INPUT_TEXT DIALOGUE_TITLE +#define PACKAGE_SIZE INBUFSIZE * 10 typedef struct params { unsigned short port; @@ -59,12 +60,20 @@ enum state { WAIT_CLIENT, }; +enum package_signal { sig_continue, sig_cancel, sig_finish, sig_pause }; + +typedef struct { + enum package_signal signal; + size_t package_size; +} s_file_pd_t; /* file package descriptor */ + typedef struct p_file { char *name; char *path; char *description; size_t size; size_t rest; + enum package_signal signal; int fd; } p_file_t; @@ -85,18 +94,18 @@ typedef struct file_args { enum main_window_type { mw_fl_server, /* file list on the server */ - mw_fl_local, /* local file list */ - mw_f_desc, /* file description */ + mw_fl_local, /* local file list */ + mw_f_desc, /* file description */ }; -typedef void (draw_f_t) (void *fui) ; +typedef void(draw_f_t)(void *fui); typedef struct main_window { enum main_window_type type; void *ui; /* structure which describes window UI */ - int32_t (*cb_b_press) (void *app, void *args); /* callback on button press */ - void (*cb_ui_refresh) (void *app); /* refresh callback */ - void (*reset) (void *app); /* reset state of the window */ + int32_t (*cb_b_press)(void *app, void *args); /* callback on button press */ + void (*cb_ui_refresh)(void *app); /* refresh callback */ + void (*reset)(void *app); /* function to reset the state of the window */ draw_f_t *draw; struct action_key *b_keys; int32_t b_keys_len; diff --git a/src/modals/load_pr.c b/src/modals/load_pr.c index 7fc1d84..14019ec 100644 --- a/src/modals/load_pr.c +++ b/src/modals/load_pr.c @@ -1,11 +1,17 @@ #include "group.h" -#include #include #include #include +#include void dwn_pr_modal_cb(w_cb_args_t *args) { - /* TODO: cancel action */ + /* only cancel action */ + int32_t key = *((int32_t *)args->data); + if (key != '\n') { + return; + } + app_t *app = args->app; + app->query_args->file->signal = sig_cancel; } w_dialogue_t *m_load_pr_init(app_t *app, bool is_upload) { @@ -17,18 +23,22 @@ w_dialogue_t *m_load_pr_init(app_t *app, bool is_upload) { char text[256]; sprintf(text, "%s file: %s", is_upload ? "Uploading" : "Downloading", - is_upload ? app->query_args->file->name : app->file_args->f_selected.name); + is_upload ? app->query_args->file->name + : app->file_args->f_selected.name); w_group_el_init_t actions[] = { - {.type = w_button, .label = "Cancel", .is_default = true, .val.num = 1}, {.type = w_end}}; + {.type = w_button, .label = "Cancel", .is_default = true, .val.num = 1}, + {.type = w_end}}; app->modal.w.parent_win = &app->win; w_dialogue_init(&(app->modal), is_upload ? "Upload" : "Download", text, - &(app->coordinates)); + &(app->coordinates)); w_dialogue_t *d = &(app->modal); d->w.callback = dwn_pr_modal_cb; - d->g_content = w_group_init(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); - d->g_action = w_group_init(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); + d->g_content = w_group_init(&(d->win), &(d->w), content, &(d->id_map), + horizontal, g_content); + d->g_action = w_group_init(&(d->win), &(d->w), actions, &(d->id_map), + horizontal, g_action); app->query_args->progress_bar = d->g_content->elements[0].element; app->query_args->active_dialogue = d; diff --git a/src/query.c b/src/query.c index 841ea4e..e8358f6 100644 --- a/src/query.c +++ b/src/query.c @@ -45,6 +45,13 @@ int32_t process_user_input(app_t *app, w_cb_args_t *d_args); app_destroy(app, exit_code); \ } +#define FILE_CLEAN(app) \ + if (app->query_args->file != NULL) { \ + FREE_MLC(app->query_args->file->name); \ + FREE_MLC(app->query_args->file->path); \ + FREE_MLC(app->query_args->file); \ + } + void query_loop(app_t *app) { query_args_t *query_args = app->query_args; fd_set readfds; @@ -183,11 +190,7 @@ int process_query(app_t *app) { if (!file_upload_start(query_args)) { query_args->state = S_UPLOAD_FILE; } else { - if (app->query_args->file != NULL) { - FREE_MLC(app->query_args->file->name); - FREE_MLC(app->query_args->file->path) - FREE_MLC(app->query_args->file); - } + FILE_CLEAN(app); query_args->state = WAIT_CLIENT; } break; @@ -198,6 +201,14 @@ int process_query(app_t *app) { query_args->state = S_UPLOAD_SERVER_FINISHES; } else if (res == -1) { /* TODO: Error */ + } else if (res == -2) { + /* cancel case */ + w_notification("Alert", dc_alert, "File %s didn't upload!", + query_args->file->name); + FILE_CLEAN(app); + w_dialogue_t *d = &(app->modal); + d->needs_destroy = true; + query_args->state = WAIT_CLIENT; } break; case S_UPLOAD_SERVER_FINISHES: diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index d9931f6..b3db66f 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -136,18 +136,12 @@ void w_dialogue_callback_default(w_cb_args_t *args) { } break; case KEY_UP: - if (d->active_el->g_type == g_content && - d->g_content->direction == vertical) { - decr_active_id(d); - args->resp_data.code = cbrc_none; - } + decr_active_id(d); + args->resp_data.code = cbrc_none; break; case KEY_DOWN: - if (d->active_el->g_type == g_content && - d->g_content->direction == vertical) { - incr_active_id(d); - args->resp_data.code = cbrc_none; - } + incr_active_id(d); + args->resp_data.code = cbrc_none; break; default: /* run callback function */ @@ -329,7 +323,7 @@ void w_dialogue_destroy(w_dialogue_t *d, void *_app) { } delwin(d->win); d->win = NULL; - d->is_initiated = 0; + d->is_initiated = false; d->needs_destroy = false; app->active_callback = (w_cb_t)app->main_ui.cb_b_press; app->query_args->active_dialogue = NULL; diff --git a/src/widget/input.h b/src/widget/input.h index 7936c77..aa9fde5 100644 --- a/src/widget/input.h +++ b/src/widget/input.h @@ -5,8 +5,8 @@ #define W_INPUT_H #include "../main.h" -#include #include +#include typedef struct { w_t w; @@ -18,11 +18,11 @@ typedef struct { bool is_hidden; } w_input_t; -bool w_input_default_key_action(w_cb_args_t *args); -w_input_t *w_input_init(WINDOW **win, w_t *w_parent, char *label, - int32_t size, int32_t is_hidden_value); -int32_t w_input_draw(w_input_t *input, int32_t active_id); -void w_input_destroy(w_input_t *input); -char *w_input_get_value(w_input_t *input); +bool w_input_default_key_action(w_cb_args_t *args); +w_input_t *w_input_init(WINDOW **win, w_t *w_parent, char *label, int32_t size, + int32_t is_hidden_value); +int32_t w_input_draw(w_input_t *input, int32_t active_id); +void w_input_destroy(w_input_t *input); +char *w_input_get_value(w_input_t *input); #endif \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..7e52439 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,13 @@ +project("My Awesome Tested Project" C) +cmake_minimum_required(VERSION 3.0) # Let's tell it a bit about our environment +SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # We declare a variable to select what to build +set (TARGET_GROUP production CACHE STRING "What Should I Build?") # Modules that exist in both our release and our unit tests, are added now +add_subdirectory(src) if(TARGET_GROUP STREQUAL release) # If we had any files that ONLY were compiled into our release, for example startup code, +# it's really handy to put it here. +elseif(TARGET_GROUP STREQUAL test) # Our tests need to let CTest know we're running tests +include(CTest) # It also needs to understand we have code in source and test directories +add_subdirectory(unity) +add_subdirectory(test) +else() +message(FATAL_ERROR "I don't know the TARGET_GROUP you gave me!") +endif() \ No newline at end of file