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
15#define ITERATION_SIZE 5
16#define SCANNING_THRESHOLD 20
17
18static void _cleanFiles(struct FileList* currentFiles) {
19 size_t size = FileListSize(currentFiles);
20 size_t i;
21 for (i = 1; i < size; ++i) {
22 free(*FileListGetPointer(currentFiles, i));
23 }
24 FileListClear(currentFiles);
25}
26
27static void _upDirectory(char* currentPath) {
28 char* end = strrchr(currentPath, '/');
29 if (!end) {
30 return;
31 }
32 if (end == currentPath) {
33 end[1] = '\0';
34 return;
35 }
36 end[0] = '\0';
37 if (end[1]) {
38 return;
39 }
40 // TODO: What if there was a trailing slash?
41}
42
43static bool _refreshDirectory(const struct GUIParams* params, const char* currentPath, struct FileList* currentFiles, bool (*filter)(struct VFile*)) {
44 _cleanFiles(currentFiles);
45
46 struct VDir* dir = VDirOpen(currentPath);
47 if (!dir) {
48 return false;
49 }
50 *FileListAppend(currentFiles) = "(Up)";
51 size_t i = 0;
52 struct VDirEntry* de;
53 while ((de = dir->listNext(dir))) {
54 ++i;
55 if (i == SCANNING_THRESHOLD) {
56 params->drawStart();
57 GUIFontPrintf(params->font, 0, GUIFontHeight(params->font), GUI_TEXT_LEFT, 0xFFFFFFFF, "%s", currentPath);
58 GUIFontPrintf(params->font, 0, GUIFontHeight(params->font) * 2, GUI_TEXT_LEFT, 0xFFFFFFFF, "(scanning)");
59 params->drawEnd();
60 }
61 const char* name = de->name(de);
62 if (name[0] == '.') {
63 continue;
64 }
65 if (de->type(de) == VFS_FILE) {
66 struct VFile* vf = dir->openFile(dir, name, O_RDONLY);
67 if (!vf) {
68 continue;
69 }
70 if (!filter || filter(vf)) {
71 *FileListAppend(currentFiles) = strdup(name);
72 }
73 vf->close(vf);
74 } else {
75 *FileListAppend(currentFiles) = strdup(name);
76 }
77 }
78 dir->close(dir);
79 return true;
80}
81
82bool selectFile(const struct GUIParams* params, const char* basePath, char* outPath, char* currentPath, size_t outLen, bool (*filter)(struct VFile*)) {
83 if (!currentPath[0]) {
84 strncpy(currentPath, basePath, outLen);
85 }
86 size_t fileIndex = 0;
87 size_t start = 0;
88
89 struct FileList currentFiles;
90 FileListInit(¤tFiles, 0);
91 _refreshDirectory(params, currentPath, ¤tFiles, filter);
92
93 int inputHistory[GUI_INPUT_MAX] = { 0 };
94
95 while (true) {
96 int input = params->pollInput();
97 int newInput = 0;
98 for (int i = 0; i < GUI_INPUT_MAX; ++i) {
99 if (input & (1 << i)) {
100 ++inputHistory[i];
101 } else {
102 inputHistory[i] = -1;
103 }
104 if (!inputHistory[i] || (inputHistory[i] >= 30 && !(inputHistory[i] % 6))) {
105 newInput |= (1 << i);
106 }
107 }
108
109 if (newInput & (1 << GUI_INPUT_UP) && fileIndex > 0) {
110 --fileIndex;
111 }
112 if (newInput & (1 << GUI_INPUT_DOWN) && fileIndex < FileListSize(¤tFiles) - 1) {
113 ++fileIndex;
114 }
115 if (newInput & (1 << GUI_INPUT_LEFT)) {
116 if (fileIndex >= ITERATION_SIZE) {
117 fileIndex -= ITERATION_SIZE;
118 } else {
119 fileIndex = 0;
120 }
121 }
122 if (newInput & (1 << GUI_INPUT_RIGHT)) {
123 if (fileIndex + ITERATION_SIZE < FileListSize(¤tFiles)) {
124 fileIndex += ITERATION_SIZE;
125 } else {
126 fileIndex = FileListSize(¤tFiles) - 1;
127 }
128 }
129
130 if (fileIndex < start) {
131 start = fileIndex;
132 }
133 while ((fileIndex - start + 4) * GUIFontHeight(params->font) > params->height) {
134 ++start;
135 }
136 if (newInput & (1 << GUI_INPUT_CANCEL)) {
137 _cleanFiles(¤tFiles);
138 FileListDeinit(¤tFiles);
139 return false;
140 }
141 if (newInput & (1 << GUI_INPUT_SELECT)) {
142 if (fileIndex == 0) {
143 _upDirectory(currentPath);
144 _refreshDirectory(params, currentPath, ¤tFiles, filter);
145 } else {
146 size_t len = strlen(currentPath);
147 const char* sep = PATH_SEP;
148 if (currentPath[len - 1] == *sep) {
149 sep = "";
150 }
151 snprintf(outPath, outLen, "%s%s%s", currentPath, sep, *FileListGetPointer(¤tFiles, fileIndex));
152 if (!_refreshDirectory(params, outPath, ¤tFiles, filter)) {
153 return true;
154 }
155 strncpy(currentPath, outPath, outLen);
156 }
157 fileIndex = 0;
158 }
159 if (newInput & (1 << GUI_INPUT_BACK)) {
160 if (strncmp(currentPath, basePath, outLen) == 0) {
161 _cleanFiles(¤tFiles);
162 FileListDeinit(¤tFiles);
163 return false;
164 }
165 _upDirectory(currentPath);
166 _refreshDirectory(params, currentPath, ¤tFiles, filter);
167 fileIndex = 0;
168 }
169
170 params->drawStart();
171 unsigned y = GUIFontHeight(params->font);
172 GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, "%s", currentPath);
173 y += 2 * GUIFontHeight(params->font);
174 size_t i;
175 for (i = start; i < FileListSize(¤tFiles); ++i) {
176 int color = 0xE0A0A0A0;
177 char bullet = ' ';
178 if (i == fileIndex) {
179 color = 0xFFFFFFFF;
180 bullet = '>';
181 }
182 GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, *FileListGetPointer(¤tFiles, i));
183 y += GUIFontHeight(params->font);
184 if (y + GUIFontHeight(params->font) > params->height) {
185 break;
186 }
187 }
188 y += GUIFontHeight(params->font) * 2;
189
190 params->drawEnd();
191 }
192}