A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 *
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 *
10 * Copyright (C) 2011 Jonathan Gordon
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#include <stdbool.h>
23#include <stdlib.h>
24#include <dir.h>
25#include "config.h"
26#include "system.h"
27#include "powermgmt.h"
28#include "power.h"
29#include "action.h"
30#include "ata_idle_notify.h"
31#include "debug_menu.h"
32#include "core_alloc.h"
33#include "list.h"
34#include "settings.h"
35#include "settings_list.h"
36#include "string-extra.h"
37#include "lang.h"
38#include "menu.h"
39#include "misc.h"
40#include "open_plugin.h"
41#include "tree.h"
42#include "splash.h"
43#include "pathfuncs.h"
44#include "filetypes.h"
45#include "shortcuts.h"
46#include "onplay.h"
47#include "screens.h"
48#include "talk.h"
49#include "yesno.h"
50
51#define MAX_SHORTCUT_NAME 64
52#define SHORTCUTS_HDR "[shortcut]"
53#define SHORTCUTS_FILENAME ROCKBOX_DIR "/shortcuts.txt"
54static const char * const type_strings[SHORTCUT_TYPE_COUNT] = {
55 [SHORTCUT_SETTING] = "setting",
56 [SHORTCUT_SETTING_APPLY] = "apply",
57 [SHORTCUT_DEBUGITEM] = "debug",
58 [SHORTCUT_BROWSER] = "browse",
59 [SHORTCUT_PLAYLISTMENU] = "playlist menu",
60 [SHORTCUT_SEPARATOR] = "separator",
61 [SHORTCUT_SHUTDOWN] = "shutdown",
62 [SHORTCUT_REBOOT] = "reboot",
63 [SHORTCUT_TIME] = "time",
64 [SHORTCUT_FILE] = "file",
65};
66
67struct shortcut {
68 enum shortcut_type type;
69 int8_t icon;
70 char name[MAX_SHORTCUT_NAME];
71 char talk_clip[MAX_PATH];
72 const struct settings_list *setting;
73 union {
74 char path[MAX_PATH];
75 struct {
76#if CONFIG_RTC
77 bool talktime;
78#endif
79 int sleep_timeout;
80 } timedata;
81 } u;
82};
83
84#define SHORTCUTS_PER_HANDLE 4
85struct shortcut_handle {
86 struct shortcut shortcuts[SHORTCUTS_PER_HANDLE];
87 int next_handle;
88};
89static int first_handle = 0;
90static int shortcut_count = 0;
91
92static int buflib_move_lock;
93static int move_callback(int handle, void *current, void *new)
94{
95 (void)handle;
96 (void)current;
97 (void)new;
98
99 if (buflib_move_lock > 0)
100 return BUFLIB_CB_CANNOT_MOVE;
101
102 return BUFLIB_CB_OK;
103}
104static struct buflib_callbacks shortcut_ops = {
105 .move_callback = move_callback,
106};
107
108static void reset_shortcuts(void)
109{
110 int current_handle = first_handle;
111 struct shortcut_handle *h = NULL;
112 while (current_handle > 0)
113 {
114 int next;
115 h = core_get_data(current_handle);
116 next = h->next_handle;
117 core_free(current_handle);
118 current_handle = next;
119 }
120 first_handle = 0;
121 shortcut_count = 0;
122}
123
124static struct shortcut_handle * alloc_first_sc_handle(void)
125{
126 struct shortcut_handle *h = NULL;
127#if 0 /* all paths are guarded, compiler doesn't recognize INIT_ATTR callers */
128 if (first_handle != 0)
129 return h; /* No re-init allowed */
130#endif
131 first_handle = core_alloc_ex(sizeof(struct shortcut_handle), &shortcut_ops);
132 if (first_handle > 0)
133 {
134 h = core_get_data(first_handle);
135 h->next_handle = 0;
136 }
137
138 return h;
139}
140
141static struct shortcut* get_shortcut(int index, struct shortcut *fail)
142{
143 int handle_count, handle_index;
144 int current_handle = first_handle;
145 struct shortcut_handle *h;
146
147 if (first_handle == 0)
148 {
149 h = alloc_first_sc_handle();
150 if (!h)
151 return fail;
152 current_handle = first_handle;
153 }
154
155 handle_count = index/SHORTCUTS_PER_HANDLE + 1;
156 handle_index = index%SHORTCUTS_PER_HANDLE;
157 do {
158 h = core_get_data(current_handle);
159 current_handle = h->next_handle;
160 handle_count--;
161 if(handle_count <= 0)
162 return &h->shortcuts[handle_index];
163 } while (current_handle > 0);
164
165 if (handle_index == 0)
166 {
167 /* prevent invalidation of 'h' during compaction */
168 ++buflib_move_lock;
169 h->next_handle = core_alloc_ex(sizeof(struct shortcut_handle), &shortcut_ops);
170 --buflib_move_lock;
171 if (h->next_handle <= 0)
172 return fail;
173 h = core_get_data(h->next_handle);
174 h->next_handle = 0;
175 }
176 return &h->shortcuts[handle_index];
177}
178
179static void remove_shortcut(int index)
180{
181 int this = index, next = index + 1;
182 struct shortcut *prev = get_shortcut(this, NULL);
183
184 while (next <= shortcut_count)
185 {
186 struct shortcut *sc = get_shortcut(next, NULL);
187 memcpy(prev, sc, sizeof(struct shortcut));
188 next++;
189 prev = sc;
190 }
191 shortcut_count--;
192}
193
194static bool verify_shortcut(struct shortcut* sc)
195{
196 switch (sc->type)
197 {
198 case SHORTCUT_UNDEFINED:
199 return false;
200 case SHORTCUT_BROWSER:
201 case SHORTCUT_FILE:
202 case SHORTCUT_PLAYLISTMENU:
203 return sc->u.path[0] != '\0';
204 case SHORTCUT_SETTING_APPLY:
205 case SHORTCUT_SETTING:
206 return sc->setting != NULL;
207 case SHORTCUT_TIME:
208 case SHORTCUT_DEBUGITEM:
209 case SHORTCUT_SEPARATOR:
210 case SHORTCUT_SHUTDOWN:
211 case SHORTCUT_REBOOT:
212 default:
213 break;
214 }
215 return true;
216}
217
218static void init_shortcut(struct shortcut* sc)
219{
220 memset(sc, 0, sizeof(*sc));
221 sc->type = SHORTCUT_UNDEFINED;
222 sc->icon = Icon_NOICON;
223}
224
225static int first_idx_to_writeback = -1;
226static bool overwrite_shortcuts = false;
227static void shortcuts_ata_idle_callback(void)
228{
229 int fd;
230
231 int current_idx = first_idx_to_writeback;
232 int append = overwrite_shortcuts ? O_TRUNC : O_APPEND;
233
234 if (first_idx_to_writeback < 0)
235 return;
236 fd = open(SHORTCUTS_FILENAME, append|O_RDWR|O_CREAT, 0644);
237 if (fd < 0)
238 return;
239
240 /* ensure pointer returned by get_shortcut()
241 survives the yield() of write() */
242 ++buflib_move_lock;
243
244 while (current_idx < shortcut_count)
245 {
246 struct shortcut* sc = get_shortcut(current_idx++, NULL);
247 const char *type;
248
249 if (!sc)
250 break;
251 type = type_strings[sc->type];
252
253 fdprintf(fd, SHORTCUTS_HDR "\ntype: %s\ndata: ", type);
254
255 if (sc->type == SHORTCUT_TIME)
256 {
257#if CONFIG_RTC
258 if (sc->u.timedata.talktime)
259 write(fd, "talk", 4);
260 else
261#endif
262 {
263 write(fd, "sleep", 5);
264 if(sc->u.timedata.sleep_timeout >= 0)
265 fdprintf(fd, " %d", sc->u.timedata.sleep_timeout);
266 }
267 }
268 else if (sc->type == SHORTCUT_SETTING_APPLY)
269 {
270 fdprintf(fd, "%s: %s", sc->setting->cfg_name, sc->u.path);
271 }
272 else if (sc->type == SHORTCUT_SETTING)
273 {
274 write(fd, sc->setting->cfg_name, strlen(sc->setting->cfg_name));
275 }
276 else
277 write(fd, sc->u.path, strlen(sc->u.path));
278
279 /* write name:, icon:, talkclip: */
280 fdprintf(fd, "\nname: %s\nicon: %d\ntalkclip: %s\n\n",
281 sc->name, sc->icon, sc->talk_clip);
282 }
283 close(fd);
284 if (first_idx_to_writeback == 0)
285 {
286 /* reload all shortcuts because we appended to the shortcuts file which
287 * has not been read yet.
288 */
289 reset_shortcuts();
290 shortcuts_init();
291 }
292 --buflib_move_lock;
293 first_idx_to_writeback = -1;
294}
295
296void shortcuts_add(enum shortcut_type type, const char* value)
297{
298 struct shortcut* sc = get_shortcut(shortcut_count++, NULL);
299 if (!sc)
300 return;
301 init_shortcut(sc);
302 sc->type = type;
303 if (type == SHORTCUT_SETTING || type == SHORTCUT_SETTING_APPLY)
304 {
305 sc->setting = (void*)value;
306 /* write the current value, will be ignored for SHORTCUT_SETTING */
307 cfg_to_string(sc->setting, sc->u.path, sizeof(sc->u.path));
308 }
309 else
310 strmemccpy(sc->u.path, value, MAX_PATH);
311
312 if (first_idx_to_writeback < 0)
313 first_idx_to_writeback = shortcut_count - 1;
314 overwrite_shortcuts = false;
315 register_storage_idle_func(shortcuts_ata_idle_callback);
316}
317
318static int readline_cb(int n, char *buf, void *parameters)
319{
320 (void)n;
321 (void)parameters;
322 struct shortcut **param = (struct shortcut**)parameters;
323 struct shortcut* sc = *param;
324 char *name, *value;
325
326 buf = skip_whitespace(buf);
327
328 if (!strcasecmp(buf, SHORTCUTS_HDR))
329 {
330 if (sc && verify_shortcut(sc))
331 shortcut_count++;
332 sc = get_shortcut(shortcut_count, NULL);
333 if (!sc)
334 return 1;
335 init_shortcut(sc);
336 *param = sc;
337 }
338 else if (sc && settings_parseline(buf, &name, &value))
339 {
340 static const char * const nm_options[] = {"type", "name", "data",
341 "icon", "talkclip", NULL};
342 int nm_op = string_option(name, nm_options, false);
343
344 if (nm_op == 0) /*type*/
345 {
346 int t = 0;
347 for (t=0; t<SHORTCUT_TYPE_COUNT && sc->type == SHORTCUT_UNDEFINED; t++)
348 if (!strcmp(value, type_strings[t]))
349 sc->type = t;
350 }
351 else if (nm_op == 1) /*name*/
352 {
353 strmemccpy(sc->name, value, MAX_SHORTCUT_NAME);
354 }
355 else if (nm_op == 2) /*data*/
356 {
357 switch (sc->type)
358 {
359 case SHORTCUT_UNDEFINED:
360 case SHORTCUT_TYPE_COUNT:
361 *param = NULL;
362 break;
363 case SHORTCUT_BROWSER:
364 {
365 char *p = strmemccpy(sc->u.path, value, MAX_PATH);
366 if (p && dir_exists(value))
367 {
368 /* ensure ending slash */
369 *p = '\0';
370 if (*(p-2) != '/')
371 *(p-1) = '/';
372 }
373 break;
374 }
375 case SHORTCUT_FILE:
376 case SHORTCUT_DEBUGITEM:
377 case SHORTCUT_PLAYLISTMENU:
378 {
379 strmemccpy(sc->u.path, value, MAX_PATH);
380 break;
381 }
382 case SHORTCUT_SETTING_APPLY:
383 case SHORTCUT_SETTING:
384 /* can handle 'name: value' pair for either type */
385 if (settings_parseline(value, &name, &value))
386 {
387 sc->setting = find_setting_by_cfgname(name);
388 strmemccpy(sc->u.path, value, MAX_PATH);
389 }
390 else /* force SHORTCUT_SETTING, no 'name: value' pair */
391 {
392 sc->type = SHORTCUT_SETTING;
393 sc->setting = find_setting_by_cfgname(value);
394 }
395 break;
396 case SHORTCUT_TIME:
397#if CONFIG_RTC
398 sc->u.timedata.talktime = false;
399 if (!strcasecmp(value, "talk"))
400 sc->u.timedata.talktime = true;
401 else
402#endif
403 if (!strncasecmp(value, "sleep", sizeof("sleep")-1))
404 {
405 /* 'sleep' may appear alone or followed by number after a space */
406 if (strlen(value) > sizeof("sleep")) /* sizeof 1 larger (+space chr..) */
407 sc->u.timedata.sleep_timeout = atoi(&value[sizeof("sleep")-1]);
408 else
409 sc->u.timedata.sleep_timeout = -1;
410 }
411 else
412 sc->type = SHORTCUT_UNDEFINED; /* error */
413 break;
414 case SHORTCUT_SEPARATOR:
415 case SHORTCUT_SHUTDOWN:
416 case SHORTCUT_REBOOT:
417 break;
418 }
419 }
420 else if (nm_op == 3) /*icon*/
421 {
422 if (!strcmp(value, "filetype") && sc->type != SHORTCUT_SETTING
423 && sc->type != SHORTCUT_SETTING_APPLY && sc->u.path[0])
424 {
425 sc->icon = filetype_get_icon(filetype_get_attr(sc->u.path));
426 }
427 else
428 {
429 sc->icon = atoi(value);
430 }
431 }
432 else if (nm_op == 4) /*talkclip*/
433 {
434 strmemccpy(sc->talk_clip, value, MAX_PATH);
435 }
436 }
437 return 0;
438}
439
440void shortcuts_init(void)
441{
442 int fd;
443 char buf[512];
444 struct shortcut *param = NULL;
445 struct shortcut_handle *h;
446 shortcut_count = 0;
447 fd = open_utf8(SHORTCUTS_FILENAME, O_RDONLY);
448 if (fd < 0)
449 return;
450 h = alloc_first_sc_handle();
451 if (!h) {
452 close(fd);
453 return;
454 }
455
456 /* we enter readline_cb() multiple times with a buffer
457 obtained when we encounter a SHORTCUTS_HDR ("[shortcut]") section.
458 fast_readline() might yield() -> protect buffer */
459 ++buflib_move_lock;
460
461 fast_readline(fd, buf, sizeof buf, ¶m, readline_cb);
462 close(fd);
463 if (param && verify_shortcut(param))
464 shortcut_count++;
465
466 /* invalidate at scope end since "param" contains
467 the shortcut pointer */
468 --buflib_move_lock;
469}
470
471static const char * shortcut_menu_get_name(int selected_item, void * data,
472 char * buffer, size_t buffer_len)
473{
474 (void)data;
475 struct shortcut *sc = get_shortcut(selected_item, NULL);
476 if (!sc)
477 return "";
478 const char *ret = sc->u.path;
479
480 if (sc->type == SHORTCUT_SETTING && sc->name[0] == '\0')
481 return P2STR(ID2P(sc->setting->lang_id));
482 else if (sc->type == SHORTCUT_SETTING_APPLY && sc->name[0] == '\0')
483 {
484 snprintf(buffer, buffer_len, "%s (%s)",
485 P2STR(ID2P(sc->setting->lang_id)), sc->u.path);
486 return buffer;
487 }
488 else if (sc->type == SHORTCUT_SEPARATOR)
489 return sc->name;
490 else if (sc->type == SHORTCUT_TIME)
491 {
492 if (sc->u.timedata.sleep_timeout < 0
493#if CONFIG_RTC
494 && !sc->u.timedata.talktime
495#endif
496 ) /* String representation for toggling sleep timer */
497 return string_sleeptimer(buffer, buffer_len);
498
499 if (sc->name[0])
500 return sc->name;
501
502#if CONFIG_RTC
503 if (sc->u.timedata.talktime)
504 return P2STR(ID2P(LANG_CURRENT_TIME));
505 else
506#endif
507 {
508 format_sleeptimer(sc->name, sizeof(sc->name),
509 sc->u.timedata.sleep_timeout, NULL);
510 snprintf(buffer, buffer_len, "%s (%s)",
511 P2STR(ID2P(LANG_SLEEP_TIMER)),
512 sc->name[0] ? sc->name : P2STR(ID2P(LANG_OFF)));
513 sc->name[0] = '\0';
514 return buffer;
515 }
516
517 }
518 else if ((sc->type == SHORTCUT_SHUTDOWN || sc->type == SHORTCUT_REBOOT) &&
519 sc->name[0] == '\0')
520 {
521 /* No translation support as only soft_shutdown has LANG_SHUTDOWN defined */
522 return type_strings[sc->type];
523 }
524 else if (sc->type == SHORTCUT_BROWSER && sc->name[0] == '\0' && (sc->u.path)[0] != '\0')
525 {
526 char* pos;
527 if (path_basename(sc->u.path, (const char **)&pos) > 0)
528 {
529 if (snprintf(buffer, buffer_len, "%s (%s)", pos, sc->u.path) < (int)buffer_len)
530 return buffer;
531 }
532 }
533
534 return sc->name[0] ? sc->name : ret;
535}
536
537static int shortcut_menu_speak_item(int selected_item, void * data)
538{
539 (void)data;
540 struct shortcut *sc = get_shortcut(selected_item, NULL);
541 if (sc)
542 {
543 if (sc->talk_clip[0])
544 {
545 talk_file(NULL, NULL, sc->talk_clip, NULL, NULL, false);
546 }
547 else
548 {
549 switch (sc->type)
550 {
551 case SHORTCUT_BROWSER:
552 {
553 DIR* dir;
554 struct dirent* entry;
555 char* slash = strrchr(sc->u.path, PATH_SEPCH);
556 char* filename = slash + 1;
557 if (slash && *filename != '\0')
558 {
559 *slash = '\0'; /* terminate the path to open the directory */
560 dir = opendir(sc->u.path);
561 *slash = PATH_SEPCH; /* restore fullpath */
562 if (dir)
563 {
564 while (0 != (entry = readdir(dir)))
565 {
566 if (!strcmp(entry->d_name, filename))
567 {
568 struct dirinfo info = dir_get_info(dir, entry);
569
570 if (info.attribute & ATTR_DIRECTORY)
571 talk_dir_or_spell(sc->u.path, NULL, false);
572 else
573 talk_file_or_spell(NULL, sc->u.path, NULL, false);
574
575 closedir(dir);
576 return 0;
577 }
578 }
579 closedir(dir);
580 }
581 }
582 else
583 {
584 talk_dir_or_spell(sc->u.path, NULL, false);
585 break;
586 }
587 talk_spell(sc->u.path, false);
588 }
589 break;
590 case SHORTCUT_FILE:
591 case SHORTCUT_PLAYLISTMENU:
592 talk_file_or_spell(NULL, sc->u.path, NULL, false);
593 break;
594 case SHORTCUT_SETTING_APPLY:
595 case SHORTCUT_SETTING:
596 talk_id(sc->setting->lang_id, false);
597 if (sc->type == SHORTCUT_SETTING_APPLY)
598 talk_spell(sc->u.path, true);
599 break;
600
601 case SHORTCUT_TIME:
602#if CONFIG_RTC
603 if (sc->u.timedata.talktime)
604 talk_timedate();
605 else
606#endif
607 if (sc->name[0] && sc->u.timedata.sleep_timeout >= 0)
608 talk_spell(sc->name, false);
609 else
610 talk_sleeptimer(sc->u.timedata.sleep_timeout);
611 break;
612 case SHORTCUT_SHUTDOWN:
613 case SHORTCUT_REBOOT:
614 if (!sc->name[0])
615 {
616 talk_spell(type_strings[sc->type], false);
617 break;
618 }
619 /* fall-through */
620 default:
621 talk_spell(sc->name[0] ? sc->name : sc->u.path, false);
622 break;
623 }
624 }
625 }
626 return 0;
627}
628
629static int shortcut_menu_get_action(int action, struct gui_synclist *lists)
630{
631 (void)lists;
632 if (action == ACTION_STD_CONTEXT)
633 {
634 int selection = gui_synclist_get_sel_pos(lists);
635
636 if (confirm_delete_yesno("") != YESNO_YES)
637 {
638 gui_synclist_set_title(lists, lists->title, lists->title_icon);
639 if (global_settings.talk_menu)
640 shortcut_menu_speak_item(selection, NULL);
641 return ACTION_REDRAW;
642 }
643
644 remove_shortcut(selection);
645 gui_synclist_set_nb_items(lists, shortcut_count);
646 gui_synclist_set_title(lists, lists->title, lists->title_icon);
647 if (selection >= shortcut_count)
648 gui_synclist_select_item(lists, shortcut_count - 1);
649 first_idx_to_writeback = 0;
650 overwrite_shortcuts = true;
651 shortcuts_ata_idle_callback();
652
653 if (shortcut_count > 0)
654 {
655 if (global_settings.talk_menu)
656 shortcut_menu_speak_item(gui_synclist_get_sel_pos(lists), NULL);
657 return ACTION_REDRAW;
658 }
659
660 return ACTION_STD_CANCEL;
661 }
662
663 if (action == ACTION_STD_OK
664 || action == ACTION_STD_MENU
665 || action == ACTION_STD_QUICKSCREEN)
666 {
667 return ACTION_STD_CANCEL;
668 }
669
670 return action;
671}
672
673static enum themable_icons shortcut_menu_get_icon(int selected_item, void * data)
674{
675 static const int8_t type_icons[SHORTCUT_TYPE_COUNT] = {
676 [SHORTCUT_SETTING] = Icon_Menu_setting,
677 [SHORTCUT_SETTING_APPLY] = Icon_Queued,
678 [SHORTCUT_DEBUGITEM] = Icon_Menu_functioncall,
679 [SHORTCUT_BROWSER] = Icon_Folder,
680 [SHORTCUT_PLAYLISTMENU] = Icon_Playlist,
681 [SHORTCUT_SEPARATOR] = Icon_NOICON,
682 [SHORTCUT_SHUTDOWN] = Icon_System_menu,
683 [SHORTCUT_REBOOT] = Icon_System_menu,
684 [SHORTCUT_TIME] = Icon_Menu_functioncall,
685 [SHORTCUT_FILE] = Icon_NOICON,
686 };
687
688 (void)data;
689 int icon;
690 struct shortcut *sc = get_shortcut(selected_item, NULL);
691 if (!sc)
692 return Icon_NOICON;
693 if (sc->icon == Icon_NOICON)
694 {
695 if (sc->type == SHORTCUT_BROWSER || sc->type == SHORTCUT_FILE)
696 {
697 icon = filetype_get_icon(filetype_get_attr(sc->u.path));
698 if (icon > 0)
699 return icon;
700 }
701 /* excluding SHORTCUT_UNDEFINED (-1) */
702 if (sc->type >= 0 && sc->type < SHORTCUT_TYPE_COUNT)
703 return type_icons[sc->type];
704 }
705 return sc->icon;
706}
707
708static void apply_new_setting(const struct settings_list *setting)
709{
710 settings_apply(false);
711 if ((setting->flags & (F_THEMESETTING|F_NEEDAPPLY)) == (F_THEMESETTING|F_NEEDAPPLY))
712 {
713 settings_apply_skins();
714 }
715 if (setting->setting == &global_settings.sleeptimer_duration && get_sleep_timer())
716 {
717 set_sleeptimer_duration(global_settings.sleeptimer_duration);
718 }
719}
720
721int do_shortcut_menu(void *ignored)
722{
723 (void)ignored;
724 struct simplelist_info list;
725 struct shortcut *sc;
726 int done = GO_TO_PREVIOUS;
727 if (first_handle == 0)
728 shortcuts_init();
729 simplelist_info_init(&list, P2STR(ID2P(LANG_SHORTCUTS)), shortcut_count, NULL);
730 list.get_name = shortcut_menu_get_name;
731 list.action_callback = shortcut_menu_get_action;
732 list.title_icon = Icon_Bookmark;
733
734 if (shortcut_count == 0)
735 {
736 splash(HZ, ID2P(LANG_NO_FILES));
737 return GO_TO_PREVIOUS;
738 }
739 push_current_activity(ACTIVITY_SHORTCUTSMENU);
740
741 /* prevent buffer moves while the menu is active.
742 Playing talk files or other I/O actions call yield() */
743 ++buflib_move_lock;
744
745 while (done == GO_TO_PREVIOUS)
746 {
747 list.count = shortcut_count; /* in case shortcut was deleted */
748 list.title = P2STR(ID2P(LANG_SHORTCUTS)); /* in case language changed */
749 list.get_icon = global_settings.show_icons ? shortcut_menu_get_icon : NULL;
750 list.get_talk = global_settings.talk_menu ? shortcut_menu_speak_item : NULL;
751
752 if (simplelist_show_list(&list))
753 break; /* some error happened?! */
754
755 if (list.selection == -1)
756 break;
757 else
758 {
759 sc = get_shortcut(list.selection, NULL);
760
761 if (!sc)
762 continue;
763
764 switch (sc->type)
765 {
766 case SHORTCUT_PLAYLISTMENU:
767 if (!file_exists(sc->u.path))
768 {
769 splash(HZ, ID2P(LANG_NO_FILES));
770 break;
771 }
772 else
773 {
774 onplay_show_playlist_menu(sc->u.path,
775 dir_exists(sc->u.path) ? ATTR_DIRECTORY :
776 filetype_get_attr(sc->u.path),
777 NULL);
778 }
779 break;
780 case SHORTCUT_FILE:
781 if (!file_exists(sc->u.path))
782 {
783 splash(HZ, ID2P(LANG_NO_FILES));
784 break;
785 }
786 /* else fall through */
787 case SHORTCUT_BROWSER:
788 {
789 if(open_plugin_add_path(ID2P(LANG_SHORTCUTS), sc->u.path, NULL) != 0)
790 {
791 done = GO_TO_PLUGIN;
792 break;
793 }
794 struct browse_context browse = {
795 .dirfilter = global_settings.dirfilter,
796 .icon = Icon_NOICON,
797 .root = sc->u.path,
798 };
799 if (sc->type == SHORTCUT_FILE)
800 browse.flags |= BROWSE_RUNFILE;
801 done = rockbox_browse(&browse);
802
803 }
804 break;
805 case SHORTCUT_SETTING_APPLY:
806 {
807 bool theme_changed;
808 string_to_cfg(sc->setting->cfg_name, sc->u.path, &theme_changed);
809 settings_save();
810 apply_new_setting(sc->setting);
811 break;
812 }
813 case SHORTCUT_SETTING:
814 {
815 do_setting_screen(sc->setting,
816 sc->name[0] ? sc->name : P2STR(ID2P(sc->setting->lang_id)),NULL);
817 apply_new_setting(sc->setting);
818 break;
819 }
820 case SHORTCUT_DEBUGITEM:
821 run_debug_screen(sc->u.path);
822 break;
823 case SHORTCUT_SHUTDOWN:
824#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
825 if (charger_inserted())
826 charging_splash();
827 else
828#endif
829 sys_poweroff();
830 break;
831 case SHORTCUT_REBOOT:
832#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
833 if (charger_inserted())
834 charging_splash();
835 else
836#endif
837 sys_reboot();
838 break;
839 case SHORTCUT_TIME:
840#if CONFIG_RTC
841 if (!sc->u.timedata.talktime)
842#endif
843 {
844 char timer_buf[10];
845 if (sc->u.timedata.sleep_timeout >= 0)
846 {
847 set_sleeptimer_duration(sc->u.timedata.sleep_timeout);
848 splashf(HZ, "%s (%s)", str(LANG_SLEEP_TIMER),
849 format_sleeptimer(timer_buf, sizeof(timer_buf),
850 sc->u.timedata.sleep_timeout,
851 NULL));
852 }
853 else
854 toggle_sleeptimer();
855 }
856 break;
857 case SHORTCUT_UNDEFINED:
858 default:
859 break;
860 }
861 }
862 }
863 if (GO_TO_PLUGIN == done)
864 pop_current_activity_without_refresh();
865 else
866 pop_current_activity();
867 --buflib_move_lock;
868
869 return done;
870}