A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 870 lines 27 kB view raw
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, &param, 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}