summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2020-08-17 11:05:26 -0400
committerWilliam Wilgus <wilgus.william@gmail.com>2020-08-17 22:17:15 -0400
commit889bcc0f763fcb31b9ac66a23199ce31746664ed (patch)
treeab566a98dfb4bad3812c2ba3a124196b223d7618
parent96e1bb655651ad7e0d5bd7841ce5b4642e9458dc (diff)
downloadrockbox-889bcc0f763fcb31b9ac66a23199ce31746664ed.tar.gz
rockbox-889bcc0f763fcb31b9ac66a23199ce31746664ed.zip
WIP open_plugins.rock viewer
OP allows you to use Open With.. to call plugins with parameters called directly it acts as a shortcut list for plugins open_plugins.rock interfaces with the open_plugin core When opened directly it acts as a viewer for the plugin.dat file this allows you to edit the paths and parameters for core shortcuts as well as your added plugins If a plugin is supplied to the viewer it is added to the dat file If instead the plugin has previously been added then it is run with the parameters you previously supplied ----------------------------------------------------------------------------- Added export to .opx files this allows shortcuts to plugins with parameters to be called from the file browser Change-Id: Ib8b05a60b049fb1d5881031ca09a07e3307d375a
-rw-r--r--apps/lang/english.lang98
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/open_plugins.c823
-rw-r--r--apps/plugins/plugins.make5
-rw-r--r--apps/plugins/viewers.config2
-rwxr-xr-xtools/buildzip.pl6
7 files changed, 936 insertions, 0 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 8af6e55c5e..fc5a37c569 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -16206,3 +16206,101 @@
16206 *: "Parameter" 16206 *: "Parameter"
16207 </voice> 16207 </voice>
16208</phrase> 16208</phrase>
16209<phrase>
16210 id: LANG_NAME
16211 desc:
16212 user: core
16213 <source>
16214 *: "Name"
16215 </source>
16216 <dest>
16217 *: "Name"
16218 </dest>
16219 <voice>
16220 *: "Name"
16221 </voice>
16222</phrase>
16223<phrase>
16224 id: LANG_ADD
16225 desc:
16226 user: core
16227 <source>
16228 *: "Add"
16229 </source>
16230 <dest>
16231 *: "Add"
16232 </dest>
16233 <voice>
16234 *: "Add"
16235 </voice>
16236</phrase>
16237<phrase>
16238 id: LANG_BACK
16239 desc:
16240 user: core
16241 <source>
16242 *: "Back"
16243 </source>
16244 <dest>
16245 *: "Back"
16246 </dest>
16247 <voice>
16248 *: "Back"
16249 </voice>
16250</phrase>
16251<phrase>
16252 id: LANG_EDIT
16253 desc:
16254 user: core
16255 <source>
16256 *: "Edit"
16257 </source>
16258 <dest>
16259 *: "Edit"
16260 </dest>
16261 <voice>
16262 *: "Edit"
16263 </voice>
16264</phrase>
16265<phrase>
16266 id: LANG_RUN
16267 desc:
16268 user: core
16269 <source>
16270 *: "Run"
16271 </source>
16272 <dest>
16273 *: "Run"
16274 </dest>
16275 <voice>
16276 *: "Run"
16277 </voice>
16278</phrase>
16279<phrase>
16280 id: LANG_EXPORT
16281 desc:
16282 user: core
16283 <source>
16284 *: "Export"
16285 </source>
16286 <dest>
16287 *: "Export"
16288 </dest>
16289 <voice>
16290 *: "Export"
16291 </voice>
16292</phrase>
16293<phrase>
16294 id: LANG_BROWSE
16295 desc:
16296 user: core
16297 <source>
16298 *: "Browse"
16299 </source>
16300 <dest>
16301 *: "Browse"
16302 </dest>
16303 <voice>
16304 *: "Browse"
16305 </voice>
16306</phrase>
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 07bc4df847..d3093689f9 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -67,6 +67,7 @@ mosaique,demos
67mp3_encoder,apps 67mp3_encoder,apps
68mpegplayer,viewers 68mpegplayer,viewers
69nim,games 69nim,games
70open_plugins,viewers
70oscilloscope,demos 71oscilloscope,demos
71otp,apps 72otp,apps
72pacbox,games 73pacbox,games
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 46a3c61537..408dc6bc67 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -137,6 +137,7 @@ chopper.c
137demystify.c 137demystify.c
138jewels.c 138jewels.c
139minesweeper.c 139minesweeper.c
140open_plugins.c
140oscilloscope.c 141oscilloscope.c
141pegbox.c 142pegbox.c
142periodic_table.c 143periodic_table.c
diff --git a/apps/plugins/open_plugins.c b/apps/plugins/open_plugins.c
new file mode 100644
index 0000000000..f06a790d02
--- /dev/null
+++ b/apps/plugins/open_plugins.c
@@ -0,0 +1,823 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
5 * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2020 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
22/* open_plugins.rock interfaces with the open_plugin core
23 *
24 * When opened directly it acts as a viewer for the plugin.dat file
25 * this allows you to edit the paths and parameters for
26 * core shortcuts as well as your added plugins
27 *
28 * If a plugin is supplied to the viewer it is added to the dat file
29 *
30 * If instead the plugin has previously been added then it is run
31 * with the parameters previously supplied
32 */
33
34#include "plugin.h"
35#include "lang_enum.h"
36#include "../open_plugin.h"
37
38#define ROCK_EXT "rock"
39#define OP_EXT "opx"
40
41#define OP_PLUGIN_RESTART (PLUGIN_GOTO_PLUGIN | 0x8000)
42
43#define MENU_ID_MAIN "0"
44#define MENU_ID_EDIT "1"
45
46static int fd_dat;
47static struct gui_synclist lists;
48struct open_plugin_entry_t op_entry;
49const off_t op_entry_sz = sizeof(struct open_plugin_entry_t);
50
51/* we only need the names for the first menu so don't bother reading paths yet */
52const off_t op_name_sz = OPEN_PLUGIN_NAMESZ + (op_entry.name - (char*)&op_entry);
53
54static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter);
55
56static bool _yesno_pop(const char* text)
57{
58 const char *lines[]={text};
59 const struct text_message message={lines, 1};
60 bool ret = (rb->gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
61 FOR_NB_SCREENS(i)
62 rb->screens[i]->clear_viewport();
63 return ret;
64}
65
66static size_t pathbasename(const char *name, const char **nameptr)
67{
68 const char *p = name;
69 const char *q = p;
70 const char *r = q;
71
72 while (*(p = GOBBLE_PATH_SEPCH(p)))
73 {
74 q = p;
75 p = GOBBLE_PATH_COMP(++p);
76 r = p;
77 }
78
79 if (r == name && p > name)
80 q = p, r = q--; /* root - return last slash */
81 /* else path is an empty string */
82
83 *nameptr = q;
84 return r - q;
85}
86
87static bool op_entry_read(int fd, int selected_item, off_t data_sz)
88{
89 rb->memset(&op_entry, 0, op_entry_sz);
90 op_entry.lang_id = -1;
91 return ((selected_item >= 0) &&
92 (rb->lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) &&
93 (rb->read(fd, &op_entry, data_sz) == data_sz));
94}
95
96static bool op_entry_read_name(int fd, int selected_item)
97{
98 return op_entry_read(fd, selected_item, op_name_sz);
99}
100
101static void op_entry_export(int selection)
102{
103 int len;
104 int fd = -1;
105 char filename [MAX_PATH + 1];
106
107 if (!op_entry_read(fd_dat, selection, op_entry_sz))
108 goto failure;
109
110 rb->snprintf(filename, MAX_PATH, "%s/%s", PLUGIN_APPS_DIR, op_entry.name);
111
112 if( !rb->kbd_input( filename, MAX_PATH, NULL ) )
113 {
114 len = rb->strlen(filename);
115 if(len > 4 && filename[len] != PATH_SEPCH &&
116 rb->strcasecmp(&((filename)[len-4]), "." OP_EXT) != 0)
117 {
118 rb->strcat(filename, "." OP_EXT);
119 }
120
121 fd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
122
123 if (fd >= 0 && rb->write(fd, &op_entry, op_entry_sz) == op_entry_sz)
124 {
125 rb->close(fd);
126 rb->splashf( 1*HZ, "File Saved (%s)", filename );
127 return;
128 }
129 rb->close(fd);
130 }
131
132failure:
133 rb->splashf( 2*HZ, "Save Failed (%s)", filename );
134
135}
136
137static void op_entry_set_name(void)
138{
139 char tmp_buf[OPEN_PLUGIN_NAMESZ+1];
140 rb->strlcpy(tmp_buf, op_entry.name, OPEN_PLUGIN_NAMESZ);
141 if (rb->kbd_input(tmp_buf, OPEN_PLUGIN_NAMESZ, NULL) >= 0)
142 rb->strlcpy(op_entry.name, tmp_buf, OPEN_PLUGIN_NAMESZ);
143}
144
145static int op_entry_set_path(void)
146{
147 int ret = 0;
148 struct browse_context browse;
149 char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
150
151 if (op_entry.path[0] == '\0')
152 rb->strcpy(op_entry.path, PLUGIN_DIR"/");
153
154 rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "",
155 Icon_Plugin, op_entry.path, NULL);
156
157 browse.buf = tmp_buf;
158 browse.bufsize = OPEN_PLUGIN_BUFSZ;
159
160 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
161 {
162 ret = rb->strlcpy(op_entry.path, tmp_buf, OPEN_PLUGIN_BUFSZ);
163 if (ret > OPEN_PLUGIN_BUFSZ)
164 ret = 0;
165 }
166 return ret;
167}
168
169static int op_entry_set_param_path(void)
170{
171 int ret = 0;
172 struct browse_context browse;
173 char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
174
175 if (op_entry.param[0] == '\0')
176 rb->strcpy(tmp_buf, "/");
177 else
178 rb->strcpy(tmp_buf, op_entry.param);
179
180 rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "",
181 Icon_Plugin, tmp_buf, NULL);
182
183 browse.buf = tmp_buf;
184 browse.bufsize = OPEN_PLUGIN_BUFSZ;
185
186 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
187 {
188 ret = rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ);
189 if (ret > OPEN_PLUGIN_BUFSZ)
190 ret = 0;
191 }
192 return ret;
193}
194
195static void op_entry_set_param(void)
196{
197 if (_yesno_pop(ID2P(LANG_BROWSE)))
198 op_entry_set_param_path();
199
200 char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
201 rb->strlcpy(tmp_buf, op_entry.param, OPEN_PLUGIN_BUFSZ);
202 if (rb->kbd_input(tmp_buf, OPEN_PLUGIN_BUFSZ, NULL) >= 0)
203 rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ);
204}
205
206static int op_et_exclude_hash(struct open_plugin_entry_t *op_entry, int item, void *data)
207{
208 (void)item;
209
210 if (op_entry->hash == 0 || op_entry->name[0] == '\0')
211 return 0;
212
213 if (data)
214 {
215 uint32_t *hash = data;
216 if (op_entry->hash != *hash)
217 return 1;
218 }
219 return 0;
220}
221
222static int op_et_exclude_builtin(struct open_plugin_entry_t *op_entry, int item, void *data)
223{
224 (void)item;
225 (void)data;
226
227 if (op_entry->lang_id >= 0)
228 return 0;
229 else if(op_entry->hash == 0 || op_entry->name[0] == '\0')
230 return 0;
231
232 return 1;
233}
234
235static int op_et_exclude_user(struct open_plugin_entry_t *op_entry, int item, void *data)
236{
237 (void)item;
238 (void)data;
239
240 if (op_entry->lang_id < 0)
241 return 0;
242 else if (op_entry->hash == 0 || op_entry->name[0] == '\0')
243 return 0;
244
245 return 1;
246}
247
248static int op_entry_transfer(int fd, int fd_tmp,
249 int(*compfn)(struct open_plugin_entry_t*, int, void*),
250 void *data)
251{
252 int entries = -1;
253 if (fd_tmp && fd && rb->lseek(fd, 0, SEEK_SET) == 0)
254 {
255 entries = 0;
256 while (rb->read(fd, &op_entry, op_entry_sz) == op_entry_sz)
257 {
258 if (compfn && compfn(&op_entry, entries, data) > 0)
259 {
260 rb->write(fd_tmp, &op_entry, op_entry_sz);
261 entries++;
262 }
263 }
264 }
265 return entries + 1;
266}
267
268static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter)
269{
270 int len;
271 uint32_t hash;
272 char *pos = "";;
273 int fd_tmp = -1;
274
275 if (key)
276 {
277 open_plugin_get_hash(key, &hash);
278 op_entry.hash = hash;
279 }
280 else if (op_entry.lang_id < 0 && plugin)
281 {
282 /* need to keep the old hash so we can remove the old entry */
283 hash = op_entry.hash;
284 open_plugin_get_hash(plugin, &op_entry.hash);
285 }
286 else
287 hash = op_entry.hash;
288
289 if (plugin)
290 {
291 /* name */
292 if (pathbasename(plugin, (const char **)&pos) == 0)
293 pos = "\0";
294 if (op_entry.name[0] == '\0' || op_entry.lang_id >= 0)
295 rb->strlcpy(op_entry.name, pos, OPEN_PLUGIN_NAMESZ);
296
297 len = rb->strlen(pos);
298 if(len > 5 && rb->strcasecmp(&(pos[len-5]), "." ROCK_EXT) == 0)
299 {
300 fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666);
301 if (fd_tmp < 0)
302 return 0;
303
304 /* path */
305 if (plugin != op_entry.path)
306 rb->strlcpy(op_entry.path, plugin, OPEN_PLUGIN_BUFSZ);
307
308 if(parameter)
309 {
310 if (parameter[0] == '\0' &&
311 _yesno_pop(ID2P(LANG_PARAMETER)))
312 {
313 op_entry_set_param();
314 }
315 else
316 rb->strlcpy(op_entry.param, parameter, OPEN_PLUGIN_BUFSZ);
317 }
318
319 rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */
320 }
321 else
322 {
323 if (op_entry.lang_id != LANG_SHORTCUTS)
324 rb->splashf(HZ / 2, rb->str(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos);
325 return 0;
326 }
327 }
328
329 if (op_entry_transfer(fd_dat, fd_tmp, op_et_exclude_hash, &hash) > 0)
330 {
331 rb->close(fd_tmp);
332 rb->close(fd_dat);
333 rb->remove(OPEN_PLUGIN_DAT);
334 rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
335 }
336 else
337 {
338 rb->close(fd_tmp);
339 rb->remove(OPEN_PLUGIN_DAT ".tmp");
340 hash = 0;
341 }
342
343 return hash;
344}
345
346void op_entry_browse_add(int selection)
347{
348 char* key;
349 op_entry_read(fd_dat, selection, op_entry_sz);
350 if (op_entry_set_path() > 0)
351 {
352 if (op_entry.lang_id >= 0)
353 key = rb->str(op_entry.lang_id);
354 else
355 key = op_entry.path;
356
357 op_entry_add_path(key, op_entry.path, NULL);
358 }
359}
360
361static void op_entry_remove(int selection)
362{
363
364 int entries = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz;
365 int32_t hash = 0;
366 int lang_id = -1;
367
368 if (entries > 0 && _yesno_pop(ID2P(LANG_REMOVE)))
369 {
370 op_entry_read(fd_dat, selection, op_entry_sz);
371 if (rb->lseek(fd_dat, selection * op_entry_sz, SEEK_SET) >= 0)
372 {
373 if (op_entry.lang_id >= 0)
374 {
375 lang_id = op_entry.lang_id;
376 hash = op_entry.hash;
377 }
378 rb->memset(&op_entry, 0, op_entry_sz);
379 op_entry.lang_id = lang_id;
380 op_entry.hash = hash;
381 rb->write(fd_dat, &op_entry, op_entry_sz);
382 }
383 }
384}
385
386static void op_entry_remove_empty(void)
387{
388 bool resave = false;
389 if (fd_dat && rb->lseek(fd_dat, 0, SEEK_SET) == 0)
390 {
391 while (resave == false &&
392 rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
393 {
394 if (op_entry.hash == 0)
395 resave = true;
396 }
397 }
398
399 if (resave)
400 {
401 int fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666);
402 if (fd_tmp < 0)
403 return;
404
405 if ((op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_builtin, NULL)
406 + op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_user, NULL)) > 0)
407 {
408 rb->close(fd_tmp);
409 rb->close(fd_dat);
410 rb->remove(OPEN_PLUGIN_DAT);
411 rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
412 }
413 else
414 {
415 rb->close(fd_tmp);
416 rb->remove(OPEN_PLUGIN_DAT ".tmp");
417 }
418 }
419
420}
421
422static int op_entry_run(void)
423{
424 int ret = PLUGIN_ERROR;
425 char* path;
426 char* param;
427 if (op_entry.hash != 0 && op_entry.path[0] != '\0')
428 {
429 rb->splash(1, ID2P(LANG_OPEN_PLUGIN));
430 path = op_entry.path;
431 param = op_entry.param;
432 if (param[0] == '\0')
433 param = NULL;
434
435 ret = rb->plugin_open(path, param);
436 }
437 return ret;
438}
439
440static const char* list_get_name_cb(int selected_item, void* data,
441 char* buf, size_t buf_len)
442{
443 /*TODO memoize names so we don't keep reading the disk when not necessary */
444 if (data == &MENU_ID_MAIN)
445 {
446 if (op_entry_read_name(fd_dat, selected_item))
447 {
448 if (op_entry.lang_id >= 0)
449 rb->snprintf(buf, buf_len, "%s [%s] ",
450 rb->str(op_entry.lang_id), op_entry.name);
451 else if (rb->strlcpy(buf, op_entry.name, buf_len) >= buf_len)
452 rb->strcpy(&buf[buf_len-10], " ...");
453 }
454 else
455 return "?";
456 }
457 else /* op_entry should already be loaded */
458 {
459 switch(selected_item)
460 {
461 case 0:
462 return ID2P(LANG_NAME);
463 case 1:
464 if (op_entry.lang_id >= 0)
465 rb->snprintf(buf, buf_len, "%s [%s] ",
466 rb->str(op_entry.lang_id), op_entry.name);
467 else if (rb->strlcpy(buf, op_entry.name, buf_len) >= buf_len)
468 rb->strcpy(&buf[buf_len-10], " ...");
469 break;
470 case 2:
471 return ID2P(LANG_DISPLAY_FULL_PATH);
472 case 3:
473 if (rb->strlcpy(buf, op_entry.path, buf_len) >= buf_len)
474 rb->strcpy(&buf[buf_len-10], " ...");
475 break;
476 case 4:
477 return ID2P(LANG_PARAMETER);
478 case 5:
479 if (op_entry.param[0] == '\0')
480 return "[NULL]";
481 else if (rb->strlcpy(buf, op_entry.param, buf_len) >= buf_len)
482 rb->strcpy(&buf[buf_len-10], " ...");
483 break;
484 case 6:
485 return "";
486 case 7:
487 return ID2P(LANG_BACK);
488 default:
489 return "?";
490 }
491 }
492
493 return buf;
494}
495
496static int list_voice_cb(int list_index, void* data)
497{
498 if (data == &MENU_ID_MAIN)
499 {
500 if (op_entry_read_name(fd_dat, list_index))
501 {
502 if (op_entry.lang_id >= 0)
503 {
504 rb->talk_id(op_entry.lang_id, false);
505 rb->talk_id(VOICE_PAUSE, true);
506 rb->talk_force_enqueue_next();
507 }
508 return rb->talk_spell(op_entry.name, false);
509 }
510 }
511 else
512 {
513 switch(list_index)
514 {
515 case 0:
516 rb->talk_id(LANG_NAME, false);
517 rb->talk_id(VOICE_PAUSE, true);
518
519 if (op_entry.lang_id >= 0)
520 {
521 rb->talk_id(op_entry.lang_id, true);
522 rb->talk_id(VOICE_PAUSE, true);
523 rb->talk_force_enqueue_next();
524 }
525 return rb->talk_spell(op_entry.name, false);
526 case 2:
527 return rb->talk_id(LANG_DISPLAY_FULL_PATH, false);
528 case 4:
529 return rb->talk_id(LANG_PARAMETER, false);
530 case 6:
531 return rb->talk_id(LANG_BACK, false);
532 default:
533 return 0;
534 }
535 }
536
537 return 0;
538}
539
540static void synclist_set(char* menu_id, int selection, int items, int sel_size)
541{
542 if (selection < 0)
543 selection = 0;
544
545 rb->gui_synclist_init(&lists,list_get_name_cb,
546 menu_id, false, sel_size, NULL);
547
548 rb->gui_synclist_set_icon_callback(&lists,NULL);
549 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
550 rb->gui_synclist_set_nb_items(&lists,items);
551 rb->gui_synclist_limit_scroll(&lists,true);
552 rb->gui_synclist_select_item(&lists, selection);
553 list_voice_cb(selection, menu_id);
554}
555
556static int context_menu_cb(int action,
557 const struct menu_item_ex *this_item,
558 struct gui_synclist *this_list)
559{
560 (void)this_item;
561
562 int selection = rb->gui_synclist_get_sel_pos(this_list);
563
564 if(action == ACTION_ENTER_MENUITEM)
565 {
566 if (selection == 0 &&
567 op_entry.lang_id >= 0 && op_entry.lang_id != LANG_OPEN_PLUGIN)
568 {
569 rb->gui_synclist_set_title(this_list,
570 rb->str(op_entry.lang_id), 0);
571 }
572 }
573 else if ((action == ACTION_STD_OK))
574 {
575 /*Run, Edit, Remove, Export, Blank, Import, Add, Back*/
576 switch(selection)
577 {
578 case 0:case 1:case 2:case 3:case 5:
579 return ACTION_STD_OK;
580 case 4: /*blank*/
581 break;
582 default:
583 return ACTION_STD_CANCEL;
584 }
585 rb->gui_synclist_draw(this_list); /* redraw */
586 return 0;
587 }
588
589 return action;
590}
591
592static void edit_menu(int selection)
593{
594 int selected_item;
595 bool exit = false;
596 int action = 0;
597
598 if (!op_entry_read(fd_dat, selection, op_entry_sz))
599 return;
600
601 uint32_t crc = rb->crc_32(&op_entry, op_entry_sz, 0xffffffff);
602
603 synclist_set(MENU_ID_EDIT, 2, 8, 2);
604 rb->gui_synclist_draw(&lists);
605
606 while (!exit)
607 {
608 action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
609
610 if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
611 continue;
612 selected_item = rb->gui_synclist_get_sel_pos(&lists);
613 switch (action)
614 {
615 case ACTION_STD_OK:
616 if (selected_item == 0)
617 op_entry_set_name();
618 else if (selected_item == 2)
619 op_entry_set_path();
620 else if (selected_item == 4)
621 op_entry_set_param();
622 else
623 exit = true;
624
625 rb->gui_synclist_draw(&lists);
626 break;
627 case ACTION_STD_CANCEL:
628 exit = true;
629 break;
630 }
631 }
632
633 if (crc != rb->crc_32(&op_entry, op_entry_sz, 0xffffffff) &&
634 _yesno_pop(ID2P(LANG_SAVE)) == true)
635 {
636 char *param = op_entry.param;
637 if (param[0] == '\0')
638 param = NULL;
639
640 op_entry_add_path(NULL, op_entry.path, param);
641 fd_dat = rb->open(OPEN_PLUGIN_DAT, O_RDWR, 0666);
642 }
643}
644
645static int context_menu(int selection)
646{
647 int selected_item;
648 if (op_entry_read(fd_dat, selection, op_entry_sz))
649 {
650 MENUITEM_STRINGLIST(menu, op_entry.name, context_menu_cb,
651 ID2P(LANG_RUN), ID2P(LANG_EDIT), ID2P(LANG_REMOVE), ID2P(LANG_EXPORT),
652 ID2P(VOICE_BLANK), ID2P(LANG_ADD), ID2P(LANG_BACK));
653
654 selected_item = rb->do_menu(&menu, 0, NULL, false);
655 switch (selected_item)
656 {
657 case 0: /*run*/
658 return PLUGIN_GOTO_PLUGIN;
659 case 1: /*edit*/
660 edit_menu(selection);
661 break;
662 case 2: /*remove*/
663 op_entry_remove(selection);
664 break;
665 case 3: /*export*/
666 op_entry_export(selection);
667 break;
668 case 4: /*blank*/
669 break;
670 case 5: /*add*/
671 op_entry_browse_add(-1);
672 rb->plugin_open(rb->plugin_get_current_filename(), "\0");
673 return OP_PLUGIN_RESTART;
674 default:
675 break;
676
677 }
678 return PLUGIN_OK;
679 }
680 return PLUGIN_ERROR;
681}
682
683enum plugin_status plugin_start(const void* parameter)
684{
685 int ret = PLUGIN_OK;
686 uint32_t hash = 0;
687 int item = -1;
688 int selection = -1;
689 int action;
690 int items;
691 int len;
692 int fd_opx;
693 off_t filesize;
694 char *path;
695 bool exit = false;
696
697 const int creat_flags = O_RDWR | O_CREAT;
698 fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666);
699 if (!fd_dat)
700 exit = true;
701
702 items = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz;
703 if (items == 0 && !parameter)
704 {
705 rb->plugin_open(rb->plugin_get_current_filename(), NULL);
706 rb->close(fd_dat);
707 return PLUGIN_GOTO_PLUGIN;
708 }
709
710 if (parameter)
711 {
712 path = (char*)parameter;
713 len = rb->strlen(path);
714 if(len > 4 && rb->strcasecmp(&((path)[len-4]), "." OP_EXT) == 0)
715 {
716 fd_opx = rb->open(path, O_RDONLY, 0666);
717 if (fd_opx)
718 {
719 filesize = rb->filesize(fd_opx);
720 if (filesize == op_entry_sz)
721 {
722
723 if (op_entry_read(fd_opx, 0, op_entry_sz))
724 {
725 exit = true;
726 ret = op_entry_run();
727 }
728 else
729 rb->splashf(HZ, rb->str(LANG_READ_FAILED), path);
730 }
731 else if (filesize != 0)
732 rb->splashf(2 * HZ, rb->str(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), path);
733
734 rb->close(fd_opx);
735 }
736 }
737 else
738 {
739 open_plugin_get_hash(parameter, &hash);
740 rb->lseek(fd_dat, 0, SEEK_SET);
741 while (rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
742 {
743 item++;
744 if (op_entry.hash == hash)
745 {
746 selection = item;
747 break;
748 }
749 }
750
751 if (selection >= 0)
752 {
753 if (op_entry_read(fd_dat, selection, op_entry_sz))
754 {
755 if (op_entry_run() == PLUGIN_GOTO_PLUGIN)
756 return PLUGIN_GOTO_PLUGIN;
757 }
758 }
759 else
760 {
761 op_entry_read(fd_dat, selection, op_entry_sz);
762 op_entry_add_path(parameter, parameter, "\0");
763 selection = 0;
764 items++;
765 fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666);
766 if (!fd_dat)
767 exit = true;
768 }
769 }/* OP_EXT */
770
771 }
772
773 if (!exit)
774 {
775 synclist_set(MENU_ID_MAIN, selection, items, 1);
776 rb->gui_synclist_draw(&lists);
777
778 while (!exit)
779 {
780 action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
781
782 if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
783 continue;
784 selection = rb->gui_synclist_get_sel_pos(&lists);
785 switch (action)
786 {
787 case ACTION_STD_CONTEXT:
788 ret = context_menu(selection);
789 if (ret == OP_PLUGIN_RESTART)
790 {
791 ret = PLUGIN_GOTO_PLUGIN;
792 exit = true;
793 break;
794 }
795 else if (ret != PLUGIN_GOTO_PLUGIN)
796 {
797 synclist_set(MENU_ID_MAIN, selection, items, 1);
798 rb->gui_synclist_draw(&lists);
799 break;
800 }
801 case ACTION_STD_OK:
802 if (op_entry_read(fd_dat, selection, op_entry_sz))
803 {
804 ret = op_entry_run();
805 exit = true;
806 }
807 break;
808 case ACTION_STD_CANCEL:
809 {
810 selection = -2;
811 exit = true;
812 break;
813 }
814 }
815 }
816 op_entry_remove_empty();
817 }
818 rb->close(fd_dat);
819 if (ret != PLUGIN_OK)
820 return ret;
821 else
822 return PLUGIN_OK;
823}
diff --git a/apps/plugins/plugins.make b/apps/plugins/plugins.make
index d395c00e82..71eaeb52c0 100644
--- a/apps/plugins/plugins.make
+++ b/apps/plugins/plugins.make
@@ -86,6 +86,11 @@ $(OVERLAYREF_LDS): $(PLUGIN_LDS)
86$(BUILDDIR)/credits.raw credits.raw: $(DOCSDIR)/CREDITS 86$(BUILDDIR)/credits.raw credits.raw: $(DOCSDIR)/CREDITS
87 $(call PRINTS,Create credits.raw)perl $(APPSDIR)/plugins/credits.pl < $< > $(BUILDDIR)/$(@F) 87 $(call PRINTS,Create credits.raw)perl $(APPSDIR)/plugins/credits.pl < $< > $(BUILDDIR)/$(@F)
88 88
89$(BUILDDIR)/apps/plugins/open_plugins.opx:
90 $(call PRINTS,MK open_plugins.opx) touch $< $(BUILDDIR)/apps/plugins/open_plugins.opx
91
92$(BUILDDIR)/apps/plugins/open_plugins.rock: $(BUILDDIR)/apps/plugins/open_plugins.opx
93
89# special dependencies 94# special dependencies
90$(BUILDDIR)/apps/plugins/wav2wv.rock: $(RBCODEC_BLD)/codecs/libwavpack.a $(PLUGIN_LIBS) 95$(BUILDDIR)/apps/plugins/wav2wv.rock: $(RBCODEC_BLD)/codecs/libwavpack.a $(PLUGIN_LIBS)
91 96
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 84f096a409..2bf052bb20 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -77,6 +77,8 @@ mpv,viewers/mpegplayer,4
77m2v,viewers/mpegplayer,4 77m2v,viewers/mpegplayer,4
78iriver,viewers/iriver_flash,3 78iriver,viewers/iriver_flash,3
79tap,viewers/zxbox,12 79tap,viewers/zxbox,12
80opx,viewers/open_plugins,-
81rock,viewers/open_plugins,-
80sna,viewers/zxbox,12 82sna,viewers/zxbox,12
81tzx,viewers/zxbox,12 83tzx,viewers/zxbox,12
82z80,viewers/zxbox,12 84z80,viewers/zxbox,12
diff --git a/tools/buildzip.pl b/tools/buildzip.pl
index ce225139ef..4b15771333 100755
--- a/tools/buildzip.pl
+++ b/tools/buildzip.pl
@@ -518,6 +518,12 @@ sub buildzip {
518 copy("$ROOT/apps/tagnavi.config", "$temp_dir/"); 518 copy("$ROOT/apps/tagnavi.config", "$temp_dir/");
519 copy("$ROOT/apps/plugins/disktidy.config", "$temp_dir/rocks/apps/"); 519 copy("$ROOT/apps/plugins/disktidy.config", "$temp_dir/rocks/apps/");
520 520
521 if(-e "$temp_dir/rocks/viewers/open_plugins.rock") {
522 my $cwd = getcwd();
523 copy("$cwd/apps/plugins/open_plugins.opx", "$temp_dir/rocks/apps/open_plugins.opx") or
524 print STDERR "Copy failed: $cwd/apps/plugins/open_plugins.opx $!\n";
525 }
526
521 if($bitmap) { 527 if($bitmap) {
522 copy("$ROOT/apps/plugins/sokoban.levels", "$temp_dir/rocks/games/sokoban.levels"); # sokoban levels 528 copy("$ROOT/apps/plugins/sokoban.levels", "$temp_dir/rocks/games/sokoban.levels"); # sokoban levels
523 copy("$ROOT/apps/plugins/snake2.levels", "$temp_dir/rocks/games/snake2.levels"); # snake2 levels 529 copy("$ROOT/apps/plugins/snake2.levels", "$temp_dir/rocks/games/snake2.levels"); # snake2 levels