all repos — mgba @ 495c0ee54e80ad27adf3bc7ad8c723d650156224

mGBA Game Boy Advance Emulator

src/util/gui/file-select.c (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "file-select.h"
  7
  8#include "util/gui/font.h"
  9#include "util/vector.h"
 10#include "util/vfs.h"
 11
 12DECLARE_VECTOR(FileList, char*);
 13DEFINE_VECTOR(FileList, char*);
 14
 15static void _cleanFiles(struct FileList* currentFiles) {
 16	size_t size = FileListSize(currentFiles);
 17	size_t i;
 18	for (i = 1; i < size; ++i) {
 19		free(*FileListGetPointer(currentFiles, i));
 20	}
 21	FileListClear(currentFiles);
 22}
 23
 24static void _upDirectory(char* currentPath) {
 25	char* end = strrchr(currentPath, '/');
 26	if (!end) {
 27		return;
 28	}
 29	if (end == currentPath) {
 30		end[1] = '\0';
 31		return;
 32	}
 33	end[0] = '\0';
 34	if (end[1]) {
 35		return;
 36	}
 37	// TODO: What if there was a trailing slash?
 38}
 39
 40static bool _refreshDirectory(const char* currentPath, struct FileList* currentFiles, bool (*filter)(struct VFile*)) {
 41	_cleanFiles(currentFiles);
 42
 43	struct VDir* dir = VDirOpen(currentPath);
 44	if (!dir) {
 45		return false;
 46	}
 47	*FileListAppend(currentFiles) = "(Up)";
 48	struct VDirEntry* de;
 49	while ((de = dir->listNext(dir))) {
 50		const char* name = de->name(de);
 51		if (name[0] == '.') {
 52			continue;
 53		}
 54		if (de->type(de) == VFS_FILE) {
 55			struct VFile* vf = dir->openFile(dir, name, O_RDONLY);
 56			if (!vf) {
 57				continue;
 58			}
 59			if (!filter || filter(vf)) {
 60				*FileListAppend(currentFiles) = strdup(name);
 61			}
 62			vf->close(vf);
 63		} else {
 64			*FileListAppend(currentFiles) = strdup(name);
 65		}
 66	}
 67	dir->close(dir);
 68	return true;
 69}
 70
 71bool selectFile(const struct GUIParams* params, const char* basePath, char* outPath, char* currentPath, size_t outLen, bool (*filter)(struct VFile*)) {
 72	if (!currentPath[0]) {
 73		strncpy(currentPath, basePath, outLen);
 74	}
 75	size_t fileIndex = 0;
 76	size_t start = 0;
 77
 78	struct FileList currentFiles;
 79	FileListInit(&currentFiles, 0);
 80	_refreshDirectory(currentPath, &currentFiles, filter);
 81
 82	int inputHistory[GUI_INPUT_MAX] = { 0 };
 83
 84	while (true) {
 85		int input = params->pollInput();
 86		int newInput = 0;
 87		for (int i = 0; i < GUI_INPUT_MAX; ++i) {
 88			if (input & (1 << i)) {
 89				++inputHistory[i];
 90			} else {
 91				inputHistory[i] = -1;
 92			}
 93			if (!inputHistory[i] || (inputHistory[i] >= 30 && !(inputHistory[i] % 6))) {
 94				newInput |= (1 << i);
 95			}
 96		}
 97
 98		if (newInput & (1 << GUI_INPUT_UP) && fileIndex > 0) {
 99			--fileIndex;
100		}
101		if (newInput & (1 << GUI_INPUT_DOWN) && fileIndex < FileListSize(&currentFiles) - 1) {
102			++fileIndex;
103		}
104		if (fileIndex < start) {
105			start = fileIndex;
106		}
107		while ((fileIndex - start + 4) * GUIFontHeight(params->font) > params->height) {
108			++start;
109		}
110		if (newInput & (1 << GUI_INPUT_CANCEL)) {
111			_cleanFiles(&currentFiles);
112			FileListDeinit(&currentFiles);
113			return false;
114		}
115		if (newInput & (1 << GUI_INPUT_SELECT)) {
116			if (fileIndex == 0) {
117				_upDirectory(currentPath);
118				_refreshDirectory(currentPath, &currentFiles, filter);
119			} else {
120				size_t len = strlen(currentPath);
121				const char* sep = PATH_SEP;
122				if (currentPath[len - 1] == *sep) {
123					sep = "";
124				}
125				snprintf(outPath, outLen, "%s%s%s", currentPath, sep, *FileListGetPointer(&currentFiles, fileIndex));
126				if (!_refreshDirectory(outPath, &currentFiles, filter)) {
127					return true;
128				}
129				strncpy(currentPath, outPath, outLen);
130			}
131			fileIndex = 0;
132		}
133		if (newInput & (1 << GUI_INPUT_BACK)) {
134			if (strncmp(currentPath, basePath, outLen) == 0) {
135				_cleanFiles(&currentFiles);
136				FileListDeinit(&currentFiles);
137				return false;
138			}
139			_upDirectory(currentPath);
140			_refreshDirectory(currentPath, &currentFiles, filter);
141			fileIndex = 0;
142		}
143
144		params->drawStart();
145		int y = GUIFontHeight(params->font);
146		GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, "Current directory: %s", currentPath);
147		y += 2 * GUIFontHeight(params->font);
148		size_t i;
149		for (i = start; i < FileListSize(&currentFiles); ++i) {
150			int color = 0xE0A0A0A0;
151			char bullet = ' ';
152			if (i == fileIndex) {
153				color = 0xFFFFFFFF;
154				bullet = '>';
155			}
156			GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, *FileListGetPointer(&currentFiles, i));
157			y += GUIFontHeight(params->font);
158			if (y + GUIFontHeight(params->font) > params->height) {
159				break;
160			}
161		}
162		y += GUIFontHeight(params->font) * 2;
163
164		params->drawEnd();
165	}
166}