jtmm2/src/filedialog.c

194 lines
4.4 KiB
C

#include "filedialog.h"
#include "input.h"
#include <dirent.h>
#include <gint/display.h>
#include <gint/gint.h>
#include <gint/keyboard.h>
#include <stdlib.h>
#include <string.h>
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)
{
extern volatile int has_ticked;
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
: "<Create a new file>";
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);
has_ticked = 0;
input_set_down(K_EXIT);
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);
}