summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2024-07-30 12:47:36 -0400
committerWilliam Wilgus <me.theuser@yahoo.com>2024-07-30 14:55:07 -0400
commite3097bf92c9fc74ed3b724caf338dd24d7f21621 (patch)
treea35143bc929914630258467cca20880e732ad147
parentef9490f68329821fc36b2ef9d3c19dc6d7698c55 (diff)
downloadrockbox-e3097bf92c9fc74ed3b724caf338dd24d7f21621.tar.gz
rockbox-e3097bf92c9fc74ed3b724caf338dd24d7f21621.zip
[Feature] File Picker Plugin
allows viewers to pop a file browser to let the user pick a relevant file which then gets run by the viewer (only lua so far) Change-Id: I7e6b4c2827fab5e9f596d336f546100636c4b871
-rw-r--r--apps/lang/english.lang14
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/file_picker.c295
-rw-r--r--apps/plugins/lua/rocklua.c17
5 files changed, 328 insertions, 0 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index aa13faf77d..0635f7b569 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -16486,3 +16486,17 @@
16486 *: none 16486 *: none
16487 </voice> 16487 </voice>
16488</phrase> 16488</phrase>
16489<phrase>
16490 id: LANG_CHOOSE_FILE
16491 desc: file_picker plugin ask user to select a file
16492 user: core
16493 <source>
16494 *: "Choose File"
16495 </source>
16496 <dest>
16497 *: "Choose File"
16498 </dest>
16499 <voice>
16500 *: "Choose File"
16501 </voice>
16502</phrase>
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 814fb2f130..79186b7aed 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -35,6 +35,7 @@ doom,games
35duke3d,games 35duke3d,games
36euroconverter,apps 36euroconverter,apps
37fft,demos 37fft,demos
38file_picker,viewers
38fire,demos 39fire,demos
39fireworks,demos 40fireworks,demos
40firmware_flash,apps 41firmware_flash,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 0b1d48d5de..a070637a77 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -12,6 +12,7 @@ cube.c
12cue_playlist.c 12cue_playlist.c
13dart_scorer.c 13dart_scorer.c
14dict.c 14dict.c
15file_picker.c
15jackpot.c 16jackpot.c
16keybox.c 17keybox.c
17keyremap.c 18keyremap.c
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}
diff --git a/apps/plugins/lua/rocklua.c b/apps/plugins/lua/rocklua.c
index 3cf0fce945..650c4d6dae 100644
--- a/apps/plugins/lua/rocklua.c
+++ b/apps/plugins/lua/rocklua.c
@@ -241,6 +241,20 @@ static void display_traceback(const char *errstr)
241 rb->splash(10 * HZ, errstr); 241 rb->splash(10 * HZ, errstr);
242#endif 242#endif
243} 243}
244
245int browse_scripts(void)
246{
247 static char buf[MAX_PATH];
248 const char *fname = rb->plugin_get_current_filename();
249 /* strip plugin dir to save space in the param buffer */
250 if (rb->strncmp(fname, PLUGIN_DIR, sizeof(PLUGIN_DIR) - 1) == 0)
251 fname += sizeof(PLUGIN_DIR) - 1; /* leave slash */
252 /* -r return to this plugin, -f looking for lua files,
253 -s start in lua_scripts, -d lock to that directory */
254 snprintf(buf, sizeof(buf), "-r'%s'-f'.lua'-s'%s'-d",
255 fname, PLUGIN_DEMOS_DIR"/lua_scripts/");
256 return rb->plugin_open(VIEWERS_DIR "/file_picker.rock", buf);
257}
244/***************** Plugin Entry Point *****************/ 258/***************** Plugin Entry Point *****************/
245enum plugin_status plugin_start(const void* parameter) 259enum plugin_status plugin_start(const void* parameter)
246{ 260{
@@ -249,7 +263,10 @@ enum plugin_status plugin_start(const void* parameter)
249 if (parameter == NULL) 263 if (parameter == NULL)
250 { 264 {
251 if (!Ls) 265 if (!Ls)
266 {
252 rb->splash(HZ, "Play a .lua file!"); 267 rb->splash(HZ, "Play a .lua file!");
268 return browse_scripts();
269 }
253 } 270 }
254 else 271 else
255 { 272 {