diff options
Diffstat (limited to 'apps/plugins/open_plugins.c')
-rw-r--r-- | apps/plugins/open_plugins.c | 823 |
1 files changed, 823 insertions, 0 deletions
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 | |||
46 | static int fd_dat; | ||
47 | static struct gui_synclist lists; | ||
48 | struct open_plugin_entry_t op_entry; | ||
49 | const 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 */ | ||
52 | const off_t op_name_sz = OPEN_PLUGIN_NAMESZ + (op_entry.name - (char*)&op_entry); | ||
53 | |||
54 | static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter); | ||
55 | |||
56 | static 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 | |||
66 | static 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 | |||
87 | static 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 | |||
96 | static bool op_entry_read_name(int fd, int selected_item) | ||
97 | { | ||
98 | return op_entry_read(fd, selected_item, op_name_sz); | ||
99 | } | ||
100 | |||
101 | static 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 | |||
132 | failure: | ||
133 | rb->splashf( 2*HZ, "Save Failed (%s)", filename ); | ||
134 | |||
135 | } | ||
136 | |||
137 | static 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 | |||
145 | static 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 | |||
169 | static 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 | |||
195 | static 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 | |||
206 | static 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 | |||
222 | static 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 | |||
235 | static 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 | |||
248 | static 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 | |||
268 | static 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 | |||
346 | void 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 | |||
361 | static 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 | |||
386 | static 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 | |||
422 | static 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 | |||
440 | static 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 | |||
496 | static 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 | |||
540 | static 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 | |||
556 | static 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 | |||
592 | static 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 | |||
645 | static 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 | |||
683 | enum 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 | } | ||