diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a626ff..979ca54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ set(SOURCES src/polarity.c src/time.c src/missile.c + src/filedialog.c ) set(ASSETS diff --git a/inc/filedialog.h b/inc/filedialog.h new file mode 100644 index 0000000..c233efe --- /dev/null +++ b/inc/filedialog.h @@ -0,0 +1,4 @@ +#pragma once + +int filedialog_open(char *buf, int n); +int filedialog_save(char *buf, int n); diff --git a/src/filedialog.c b/src/filedialog.c new file mode 100644 index 0000000..26bdfe2 --- /dev/null +++ b/src/filedialog.c @@ -0,0 +1,189 @@ +#include "filedialog.h" +#include +#include +#include +#include +#include +#include + +static void +path_append(char **path, char const *folder) +{ + char *newpath = malloc(strlen(*path) + strlen(folder) + 2); + strcpy(newpath, *path); + if (strcmp(*path, "/") != 0) strcat(newpath, "/"); + strcat(newpath, folder); + free(*path); + *path = newpath; +} + +static void +path_remove(char **path) +{ + if (!strcmp(*path, "/")) return; + *strrchr(*path, '/') = 0; +} + +static int +accept_entry(struct dirent *ent) +{ + if (!strcmp(ent->d_name, "@MainMem")) return 0; + if (!strcmp(ent->d_name, "SAVE-F")) return 0; + if (!strcmp(ent->d_name, ".")) return 0; + if (!strcmp(ent->d_name, "..")) return 0; + /* here we could filter based on suffix */ + return 1; +} + +static void +load_folder(char const *path, DIR **dp, int *total, int allow_new_file) +{ + struct dirent *ent; + + if (*dp) closedir(*dp); + *dp = (DIR *)gint_world_switch(GINT_CALL(opendir, path)); + *total = allow_new_file; + + while ((ent = readdir(*dp))) + *total += accept_entry(ent); +} + +static int +letter_for_key(int key, int alpha) +{ + if (alpha) { + int row = 6 - (key >> 4); + int col = (key & 0x0f) - 1; + int i = 6 * row + col; + return "abcdefghijklmno\0\0\0pqrst\0uvwxy\0z \"\0\0\0"[i]; + } + + int digit = keycode_digit(key); + if (digit >= 0) return digit + '0'; + + if (key == KEY_DOT) return '.'; + + return 0; +} + +static int +do_dialog(char *buf, int n, int allow_new_file) +{ + char *path = strdup("/"), *entry_name, *selected_name; + DIR *dp = NULL; + struct dirent *ent, *selected_ent; + int total = 0, cursor = 0, scroll = 0, edit = -1, isfolder = 0, rc = 0; + /* both sizes below can be changed */ + const int visible = 13; + char new_name[32]; + + load_folder(path, &dp, &total, allow_new_file); + + for (;;) { + rewinddir(dp); + dclear(C_BLACK); + dprint(1, 1, C_WHITE, "Browsing: %s", path); + + selected_name = NULL; + selected_ent = NULL; + + for (int i = -scroll; i < visible;) { + int selected = (cursor == scroll + i); + + /* when editing is enabled offer an entry "new file" */ + if (i == -scroll && allow_new_file) { + entry_name = edit >= 0 ? new_name + : ""; + isfolder = 0; + } else { + ent = readdir(dp); + if (!ent) break; + if (!accept_entry(ent)) continue; + entry_name = ent->d_name; + isfolder = (ent->d_type == DT_DIR); + if (selected) selected_ent = ent; + } + if (i < 0) { + i++; + continue; + } + int y = 20 + 15 * i; + if (selected) { + selected_name = entry_name; + drect(0, y - 1, DWIDTH - 1, y + 13, C_WHITE); + } + dprint(1, y, selected ? C_BLACK : C_WHITE, "%s%s", + entry_name, + isfolder ? "/" + : entry_name == new_name ? "|" + : ""); + i++; + } + + dupdate(); + + key_event_t ev = getkey(); + int key = ev.key; + if (key == KEY_UP && cursor > 0) cursor--; + if (key == KEY_DOWN && cursor < total - 1) cursor++; + + if (scroll > 0 && cursor <= scroll) scroll = cursor - 1; + if (scroll + visible < total && cursor >= scroll + visible - 2) + scroll = cursor - visible + 2; + + int changed_path = 0; + if (key == KEY_EXIT && edit >= 0) { + edit = -1; + } else if (key == KEY_EXIT) { + if (!strcmp(path, "/")) goto end; + path_remove(&path); + changed_path = 1; + } else if (key == KEY_EXE && edit > 0) { + break; + } else if (key == KEY_EXE && !selected_ent) { + memset(new_name, 0, sizeof new_name); + edit = 0; + } else if (key == KEY_EXE && selected_ent->d_type == DT_DIR) { + path_append(&path, selected_ent->d_name); + changed_path = 1; + } else if (key == KEY_EXE && selected_ent->d_type != DT_DIR) { + break; + } + + if (edit > 0 && key == KEY_DEL) { + new_name[--edit] = 0; + } else if (edit >= 0 && new_name[sizeof new_name - 2] == 0) { + int letter = letter_for_key(key, ev.alpha); + if (letter) new_name[edit++] = letter; + } + + if (changed_path) { + load_folder(path, &dp, &total, allow_new_file); + cursor = scroll = 0; + } + } + + if ((size_t)n >= strlen(path) + strlen(selected_name) + 2) { + strcpy(buf, path); + if (strcmp(path, "/") != 0) strcat(buf, "/"); + strcat(buf, selected_name); + } else + rc = 1; + +end: + free(path); + closedir(dp); + return rc; +} + +int +filedialog_open(char *buf, int n) +{ + return do_dialog(buf, n, 0); +} + +int +filedialog_save(char *buf, int n) +{ + return do_dialog(buf, n, 1); +} diff --git a/src/main.c b/src/main.c index 2ec235a..4cadc72 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include "conf.h" #include "editor.h" +#include "filedialog.h" #include "input.h" #include "level.h" #include "missile.h" @@ -9,6 +10,7 @@ #include #include #include +#include #include static struct Player player; @@ -28,6 +30,19 @@ main(void) int frameskip = 1; init(); + + //- + char path[128]; + int rc = filedialog_save(path, 128); + has_ticked = 0; + dclear(C_BLACK); + dprint(1, 1, C_WHITE, "%s (%d)", *path ? path : "(null)", rc); + dupdate(); + getkey(); + input_update(); + input_update(); + //- + level_load(0); for (;;) {