~kikoodx/jtmm2-devel

file dialogs v1 PROPOSED

Lephenixnoir: 1
 file dialogs

 4 files changed, 209 insertions(+), 0 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~kikoodx/jtmm2-devel/patches/27817/mbox | git am -3
Learn more about email & git

[PATCH] file dialogs Export this patch

Fixes: https://todo.sr.ht/~kikoodx/jtmm2/9
---
Adds file dialogs with a minimal example in src/main.c. Most errors
(malloc/opendir) are ignored, the functions only return 1 if the buffer
is too short to hold the selected file's name. The dialogs can browse
subfolders, input new names, and optionally filter names with
accept_entry().

 CMakeLists.txt   |   1 +
 inc/filedialog.h |   4 +
 src/filedialog.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++
 src/main.c       |  14 ++++
 4 files changed, 209 insertions(+)
 create mode 100644 inc/filedialog.h
 create mode 100644 src/filedialog.c

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..3573d71
--- /dev/null
+++ b/src/filedialog.c
@@ -0,0 +1,190 @@
#include "filedialog.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)
{
	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);

	while (1) {
		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);
	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..666aa11 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"
@@ -28,6 +29,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 (;;) {
--
2.34.0