all repos — mgba @ d67844e95fe95343efc7e45378b4a99859453cad

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 = 0; 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	struct VDirEntry* de;
 48	while ((de = dir->listNext(dir))) {
 49		const char* name = de->name(de);
 50		if (name[0] == '.') {
 51			continue;
 52		}
 53		if (de->type(de) == VFS_FILE) {
 54			struct VFile* vf = dir->openFile(dir, name, O_RDONLY);
 55			if (!vf) {
 56				continue;
 57			}
 58			if (!filter || filter(vf)) {
 59				*FileListAppend(currentFiles) = strdup(name);
 60			}
 61			vf->close(vf);
 62		} else {
 63			*FileListAppend(currentFiles) = strdup(name);
 64		}
 65	}
 66	dir->close(dir);
 67	return true;
 68}
 69
 70bool selectFile(const struct GUIParams* params, const char* basePath, char* outPath, char* currentPath, size_t outLen, bool (*filter)(struct VFile*)) {
 71	if (!currentPath[0]) {
 72		strncpy(currentPath, basePath, outLen);
 73	}
 74	size_t fileIndex = 0;
 75	size_t start = 0;
 76
 77	struct FileList currentFiles;
 78	FileListInit(&currentFiles, 0);
 79	_refreshDirectory(currentPath, &currentFiles, filter);
 80
 81	int inputHistory[GUI_INPUT_MAX] = { 0 };
 82
 83	while (true) {
 84		int input = params->pollInput();
 85		int newInput = 0;
 86		for (int i = 0; i < GUI_INPUT_MAX; ++i) {
 87			if (input & (1 << i)) {
 88				++inputHistory[i];
 89			} else {
 90				inputHistory[i] = -1;
 91			}
 92			if (!inputHistory[i] || (inputHistory[i] >= 30 && !(inputHistory[i] % 6))) {
 93				newInput |= (1 << i);
 94			}
 95		}
 96
 97		if (newInput & (1 << GUI_INPUT_UP) && fileIndex > 0) {
 98			--fileIndex;
 99		}
100		if (newInput & (1 << GUI_INPUT_DOWN) && fileIndex < FileListSize(&currentFiles) - 1) {
101			++fileIndex;
102		}
103		if (fileIndex < start) {
104			start = fileIndex;
105		}
106		while ((fileIndex - start + 4) * GUIFontHeight(params->font) > params->height) {
107			++start;
108		}
109		if (newInput & (1 << GUI_INPUT_CANCEL)) {
110			_cleanFiles(&currentFiles);
111			FileListDeinit(&currentFiles);
112			return false;
113		}
114		if (newInput & (1 << GUI_INPUT_SELECT) && FileListSize(&currentFiles)) {
115			size_t len = strlen(currentPath);
116			const char* sep = PATH_SEP;
117			if (currentPath[len - 1] == *sep) {
118				sep = "";
119			}
120			snprintf(outPath, outLen, "%s%s%s", currentPath, sep, *FileListGetPointer(&currentFiles, fileIndex));
121			if (!_refreshDirectory(outPath, &currentFiles, filter)) {
122				return true;
123			}
124			strncpy(currentPath, outPath, outLen);
125			fileIndex = 0;
126		}
127		if (newInput & (1 << GUI_INPUT_BACK)) {
128			if (strncmp(currentPath, basePath, outLen) == 0) {
129				_cleanFiles(&currentFiles);
130				FileListDeinit(&currentFiles);
131				return false;
132			}
133			_upDirectory(currentPath);
134			_refreshDirectory(currentPath, &currentFiles, filter);
135			fileIndex = 0;
136		}
137
138		params->drawStart();
139		int y = GUIFontHeight(params->font);
140		GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, "Current directory: %s", currentPath);
141		y += 2 * GUIFontHeight(params->font);
142		size_t i;
143		for (i = start; i < FileListSize(&currentFiles); ++i) {
144			int color = 0xE0A0A0A0;
145			char bullet = ' ';
146			if (i == fileIndex) {
147				color = 0xFFFFFFFF;
148				bullet = '>';
149			}
150			GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, *FileListGetPointer(&currentFiles, i));
151			y += GUIFontHeight(params->font);
152			if (y + GUIFontHeight(params->font) > params->height) {
153				break;
154			}
155		}
156		y += GUIFontHeight(params->font) * 2;
157
158		params->drawEnd();
159	}
160}