summaryrefslogtreecommitdiff
path: root/apps/plugins/file_picker.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/file_picker.c')
-rw-r--r--apps/plugins/file_picker.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/apps/plugins/file_picker.c b/apps/plugins/file_picker.c
new file mode 100644
index 0000000000..ce9d81728e
--- /dev/null
+++ b/apps/plugins/file_picker.c
@@ -0,0 +1,295 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2024 William Wilgus
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21/*File Picker Plugin
22
23FPP accepts several arguments to help in your file picking adventure
24* NOTE: anything with spaces should be quoted, -f and -l can't be used together *
25 -r "<GOTO_PLUGIN_NAME>" doesn't need to be a full path demos/file.rock works
26 the file (if choosen, cancel exits) will be passed as a parameter
27 -t "<TITLE ( max 64 chars )>"
28 -s "<START DIR>"
29 -f "<.EXT (max 64 chars) >" the extension of the files you are looking for
30 must have the '.' and ".*" accepts any file
31 -l "<.EXT.EXT,.EXT .EXT (max 64 chars) >" list of extensions for files you are looking for
32 each must have the '.' spaces and commas are ignored
33 -a attrib flags eg FILE_ATTR_AUDIO
34 -d disallow changing directories (hide directories doesn't allow changing from start dir)
35*/
36#include "plugin.h"
37#include "lang_enum.h"
38#include "lib/arg_helper.h"
39
40#if defined(DEBUG) || defined(SIMULATOR)
41 #define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n")
42#elif defined(ROCKBOX_HAS_LOGF)
43 #define logf rb->logf
44#else
45 #define logf(...) do { } while(0)
46#endif
47
48#define FIND_NODIRS 0x01
49#define FIND_ATTRIB 0x02
50#define FIND_WILDCARD 0x04
51#define FIND_EXT 0x08
52#define FIND_EXT_IN_LIST 0x10
53
54static struct fpp
55{
56 char return_plugin[MAX_PATH];
57 char start_dir[MAX_PATH];
58 char file_ext[64];
59 char title[64];
60 int tree_attr;
61 int flags;
62}fpp;
63
64static int arg_callback(char argchar, const char **parameter, void *userdata)
65{
66 struct fpp *pfp = userdata;
67 int ret;
68 long num;
69 const char* start = *parameter;
70 while (*parameter[0] > '/' && ispunct(*parameter[0])) (*parameter)++;
71 switch (tolower(argchar))
72 {
73 case 'd' :
74 pfp->flags |= FIND_NODIRS;
75 logf ("Find no dirs");
76 break;
77 case 'r' : /*return_plugin*/
78 logf ("trying PLUGIN_DIR...");
79 size_t l = rb->strlcpy(pfp->return_plugin,
80 PLUGIN_DIR,
81 sizeof(pfp->return_plugin));
82
83 ret = string_parse(parameter,
84 pfp->return_plugin + l,
85 sizeof(pfp->return_plugin) - l);
86
87 if (ret && !rb->file_exists(pfp->return_plugin))
88 {
89 logf("Failed");
90 *parameter = start;
91 string_parse(parameter, pfp->return_plugin,
92 sizeof(pfp->return_plugin));
93 }
94
95 if (ret)
96 {
97 logf ("Ret plugin: Val: %s\n", pfp->return_plugin);
98 logf("ate %d chars\n", ret);
99 }
100 break;
101 case 't' : /* title */
102 ret = string_parse(parameter, pfp->title, sizeof(pfp->title));
103 if (ret)
104 {
105 logf ("Title: Val: %s\n", pfp->title);
106 logf("ate %d chars\n", ret);
107 }
108 break;
109 case 's' : /* start directory */
110 ret = string_parse(parameter, pfp->start_dir, sizeof(pfp->start_dir));
111 if (ret)
112 {
113 if (!rb->dir_exists(pfp->start_dir))
114 {
115 rb->strlcpy(pfp->start_dir, PATH_ROOTSTR, sizeof(pfp->start_dir));
116 }
117
118 logf ("Start dir: Val: %s\n", pfp->start_dir);
119 logf("ate %d chars\n", ret);
120 }
121 break;
122 case 'f' : /* file extension */
123 if (pfp->flags & FIND_EXT_IN_LIST)
124 {
125 rb->splash(HZ*5, "list extensions already active -f ignored");
126 break;
127 }
128 ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext));
129 if (ret)
130 {
131 if (pfp->file_ext[1] == '*')
132 pfp->flags |= FIND_WILDCARD;
133 else
134 pfp->flags |= FIND_EXT;
135 logf ("Extension: Val: %s\n", pfp->file_ext);
136 logf("ate %d chars\n", ret);
137 }
138 break;
139 case 'l' : /* file extension list */
140 if (pfp->flags & FIND_EXT)
141 {
142 rb->splash(HZ*5, "extension already active -l ignored");
143 break;
144 }
145 ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext));
146 if (ret)
147 {
148 char *wr = pfp->file_ext;
149 char *rd = pfp->file_ext;
150 while (*rd != '\0') /* copy the extensions */
151 {
152 if (*rd == ' ' || *rd == ',' || *rd == ';')
153 {
154 /* ignore spaces, commas, and semicolons */
155 rd++;
156 continue;
157 }
158 *wr++ = *rd++;
159 }
160 *wr = '\0';
161 pfp->flags |= FIND_EXT_IN_LIST;
162 logf ("Extension List: Val: %s\n", pfp->file_ext);
163 logf("ate %d chars\n", ret);
164 }
165 break;
166 case 'a' : /* tree attribute */
167 ret = longnum_parse(parameter, &num, NULL);
168 if (ret)
169 {
170 pfp->tree_attr = (num)&FILE_ATTR_MASK;
171 pfp->flags |= FIND_ATTRIB;
172 logf ("Attrib: Val: 0x%x\n", (uint32_t)num);
173 logf("ate %d chars\n", ret);
174 }
175 break;
176 default :
177 rb->splashf(HZ, "Unknown switch '%c'",argchar);
178 logf("Unknown switch '%c'",argchar);
179 //return 0;
180 }
181
182 return 1;
183}
184
185static bool cb_show_item(char *name, int attr, struct tree_context *tc)
186{
187 static int dirlevel = -1;
188 if(attr & ATTR_DIRECTORY)
189 {
190 if (fpp.flags & FIND_NODIRS)
191 {
192 if (tc->dirlevel == dirlevel)
193 return false;
194 dirlevel = tc->dirlevel;
195 if (rb->strcasestr(tc->currdir, fpp.start_dir) == NULL)
196 {
197 tc->is_browsing = false; /* exit immediately */
198 logf("exiting %d", tc->dirlevel);
199 }
200 }
201
202 return true;
203 }
204 if (fpp.flags & FIND_WILDCARD)
205 {
206 return true;
207 }
208 if ((fpp.flags & FIND_ATTRIB) && (fpp.tree_attr & attr) != 0)
209 {
210 return true;
211 }
212 if (fpp.flags & FIND_EXT)
213 {
214 const char *p = rb->strrchr(name, '.' );
215 if (p != NULL && !rb->strcasecmp( p, fpp.file_ext))
216 return true;
217 }
218
219 if (fpp.flags & FIND_EXT_IN_LIST)
220 {
221 const char *p = rb->strrchr(name, '.' );
222 if (p != NULL && rb->strcasestr(fpp.file_ext, p) != NULL)
223 return true;
224 }
225
226 logf("Excluded: %s", name);
227 return false;
228
229}
230
231static int browse_file_dir(struct fpp *pfp)
232{
233 char buf[MAX_PATH];
234 struct browse_context browse = {
235 .dirfilter = SHOW_ALL,
236 .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
237 .title = pfp->title,
238 .icon = Icon_Playlist,
239 .buf = buf,
240 .bufsize = sizeof(buf),
241 .root = pfp->start_dir,
242 .callback_show_item = &cb_show_item,
243 };
244
245 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
246 {
247 if (rb->file_exists(buf))
248 {
249 logf("Loading %s", buf);
250 return rb->plugin_open(pfp->return_plugin, buf);
251 }
252 else
253 {
254 logf("Error opening %s", buf);
255 rb->splashf(HZ *2, "Error Opening %s", buf);
256 return PLUGIN_ERROR;
257 }
258 }
259
260 return PLUGIN_OK;
261}
262
263enum plugin_status plugin_start(const void* parameter)
264{
265 if (!parameter)
266 {
267 rb->splash(HZ *2, "No Args");
268 return PLUGIN_ERROR;
269 }
270
271 argparse((const char*) parameter, -1, &fpp, &arg_callback);
272
273 if (fpp.title[0] == '\0')
274 {
275 if (rb->global_settings->talk_menu)
276 rb->talk_id(LANG_CHOOSE_FILE, true);
277
278 if ((fpp.flags & FIND_EXT) || (fpp.flags & FIND_EXT_IN_LIST))
279 {
280 rb->snprintf(fpp.title, sizeof(fpp.title),
281 "%s (%s)", rb->str(LANG_CHOOSE_FILE), fpp.file_ext);
282 if (rb->global_settings->talk_menu)
283 rb->talk_spell(fpp.file_ext, true);
284 }
285 else
286 {
287 rb->snprintf(fpp.title, sizeof(fpp.title), "%s", rb->str(LANG_CHOOSE_FILE));
288 }
289 rb->talk_force_enqueue_next();
290 }
291 if (fpp.start_dir[0] == '\0' || !rb->dir_exists(fpp.start_dir))
292 rb->strcpy(fpp.start_dir, PATH_ROOTSTR);
293
294 return browse_file_dir(&fpp);
295}