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(¤tFiles, 0);
80 _refreshDirectory(currentPath, ¤tFiles, 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(¤tFiles) - 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(¤tFiles);
112 FileListDeinit(¤tFiles);
113 return false;
114 }
115 if (newInput & (1 << GUI_INPUT_SELECT)) {
116 if (fileIndex == 0) {
117 _upDirectory(currentPath);
118 _refreshDirectory(currentPath, ¤tFiles, 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(¤tFiles, fileIndex));
126 if (!_refreshDirectory(outPath, ¤tFiles, 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(¤tFiles);
136 FileListDeinit(¤tFiles);
137 return false;
138 }
139 _upDirectory(currentPath);
140 _refreshDirectory(currentPath, ¤tFiles, 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(¤tFiles); ++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(¤tFiles, 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}