Merge tag 'v3.13-final' into maemo-port
[maemo-rb.git] / apps / playlist_viewer.c
blob5479ba43bad68847dafa81e9597d2b43f875f155
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
11 * Copyright (C) 2003 Hardeep Sidhu
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 * Kevin Ferrare 2005/10/16
24 * multi-screen support, rewrote a lot of code
26 #include <string.h>
27 #include "playlist.h"
28 #include "audio.h"
29 #include "screens.h"
30 #include "settings.h"
31 #include "icons.h"
32 #include "menu.h"
33 #include "plugin.h"
34 #include "keyboard.h"
35 #include "filetypes.h"
36 #include "onplay.h"
37 #include "talk.h"
38 #include "misc.h"
39 #include "action.h"
40 #include "debug.h"
41 #include "backlight.h"
43 #include "lang.h"
45 #include "playlist_viewer.h"
46 #include "playlist_catalog.h"
47 #include "icon.h"
48 #include "list.h"
49 #include "splash.h"
50 #include "playlist_menu.h"
51 #include "yesno.h"
53 /* Maximum number of tracks we can have loaded at one time */
54 #define MAX_PLAYLIST_ENTRIES 200
56 /* The number of items between the selected one and the end/start of
57 * the buffer under which the buffer must reload */
58 #define MIN_BUFFER_MARGIN (screens[0].getnblines()+1)
60 /* Information about a specific track */
61 struct playlist_entry {
62 char *name; /* Formatted track name */
63 int index; /* Playlist index */
64 int display_index; /* Display index */
65 bool queued; /* Is track queued? */
66 bool skipped; /* Is track marked as bad? */
69 enum direction
71 FORWARD,
72 BACKWARD
75 struct playlist_buffer
77 char *name_buffer; /* Buffer used to store track names */
78 int buffer_size; /* Size of name buffer */
80 int first_index; /* Real index of first track loaded inside
81 the buffer */
83 enum direction direction; /* Direction of the buffer (if the buffer
84 was loaded BACKWARD, the last track in
85 the buffer has a real index < to the
86 real index of the the first track)*/
88 struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES];
89 int num_loaded; /* Number of track entries loaded in buffer */
92 /* Global playlist viewer settings */
93 struct playlist_viewer {
94 struct playlist_info* playlist; /* playlist being viewed */
95 int num_tracks; /* Number of tracks in playlist */
96 int current_playing_track; /* Index of current playing track */
97 int selected_track; /* The selected track, relative (first is 0) */
98 int moving_track; /* The track to move, relative (first is 0)
99 or -1 if nothing is currently being moved */
100 int moving_playlist_index; /* Playlist-relative index (as opposed to
101 viewer-relative index) of moving track */
102 struct playlist_buffer buffer;
105 static struct playlist_viewer viewer;
107 /* Used when viewing playlists on disk */
108 static struct playlist_info temp_playlist;
110 static void playlist_buffer_init(struct playlist_buffer *pb, char *names_buffer,
111 int names_buffer_size);
112 static void playlist_buffer_load_entries(struct playlist_buffer * pb, int index,
113 enum direction direction);
114 static int playlist_entry_load(struct playlist_entry *entry, int index,
115 char* name_buffer, int remaining_size);
117 static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer *pb,
118 int index);
120 static bool playlist_viewer_init(struct playlist_viewer * viewer,
121 const char* filename, bool reload);
123 static void format_name(char* dest, const char* src);
124 static void format_line(const struct playlist_entry* track, char* str,
125 int len);
127 static bool update_playlist(bool force);
128 static int onplay_menu(int index);
130 static void playlist_buffer_init(struct playlist_buffer *pb, char *names_buffer,
131 int names_buffer_size)
133 pb->name_buffer = names_buffer;
134 pb->buffer_size = names_buffer_size;
135 pb->first_index = 0;
136 pb->num_loaded = 0;
140 * Loads the entries following 'index' in the playlist buffer
142 static void playlist_buffer_load_entries(struct playlist_buffer *pb, int index,
143 enum direction direction)
145 int num_entries = viewer.num_tracks;
146 char* p = pb->name_buffer;
147 int remaining = pb->buffer_size;
148 int i;
150 pb->first_index = index;
151 if (num_entries > MAX_PLAYLIST_ENTRIES)
152 num_entries = MAX_PLAYLIST_ENTRIES;
154 for (i = 0; i < num_entries; i++)
156 int len = playlist_entry_load(&(pb->tracks[i]), index, p, remaining);
157 if (len < 0)
159 /* Out of name buffer space */
160 num_entries = i;
161 break;
164 p += len;
165 remaining -= len;
167 if(direction == FORWARD)
168 index++;
169 else
170 index--;
171 index += viewer.num_tracks;
172 index %= viewer.num_tracks;
174 pb->direction = direction;
175 pb->num_loaded = i;
178 /* playlist_buffer_load_entries_screen()
179 * This function is called when the currently selected item gets too close
180 * to the start or the end of the loaded part of the playlis, or when
181 * the list callback requests a playlist item that has not been loaded yet
183 * reference_track is either the currently selected track, or the track that
184 * has been requested by the callback, and has not been loaded yet.
186 static void playlist_buffer_load_entries_screen(struct playlist_buffer * pb,
187 enum direction direction,
188 int reference_track)
190 if (direction == FORWARD)
192 int min_start = reference_track-2*screens[0].getnblines();
193 while (min_start < 0)
194 min_start += viewer.num_tracks;
195 min_start %= viewer.num_tracks;
196 playlist_buffer_load_entries(pb, min_start, FORWARD);
198 else
200 int max_start = reference_track+2*screens[0].getnblines();
201 max_start %= viewer.num_tracks;
202 playlist_buffer_load_entries(pb, max_start, BACKWARD);
206 static int playlist_entry_load(struct playlist_entry *entry, int index,
207 char* name_buffer, int remaining_size)
209 struct playlist_track_info info;
210 int len;
212 /* Playlist viewer orders songs based on display index. We need to
213 convert to real playlist index to access track */
214 index = (index + playlist_get_first_index(viewer.playlist)) %
215 viewer.num_tracks;
216 if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
217 return -1;
219 len = strlen(info.filename) + 1;
221 if (len <= remaining_size)
223 strcpy(name_buffer, info.filename);
225 entry->name = name_buffer;
226 entry->index = info.index;
227 entry->display_index = info.display_index;
228 entry->queued = info.attr & PLAYLIST_ATTR_QUEUED;
229 entry->skipped = info.attr & PLAYLIST_ATTR_SKIPPED;
230 return len;
232 return -1;
235 static int playlist_buffer_get_index(struct playlist_buffer *pb, int index)
237 int buffer_index;
238 if (pb->direction == FORWARD)
240 if (index >= pb->first_index)
241 buffer_index = index-pb->first_index;
242 else /* rotation : track0 in buffer + requested track */
243 buffer_index = viewer.num_tracks-pb->first_index+index;
245 else
247 if (index <= pb->first_index)
248 buffer_index = pb->first_index-index;
249 else /* rotation : track0 in buffer + dist from the last track
250 to the requested track (num_tracks-requested track) */
251 buffer_index = pb->first_index+viewer.num_tracks-index;
253 return buffer_index;
256 #define distance(a, b) \
257 a>b? (a) - (b) : (b) - (a)
258 static bool playlist_buffer_needs_reload(struct playlist_buffer* pb,
259 int track_index)
261 if (pb->num_loaded == viewer.num_tracks)
262 return false;
263 int selected_index = playlist_buffer_get_index(pb, track_index);
264 int first_buffer_index = playlist_buffer_get_index(pb, pb->first_index);
265 int distance_beginning = distance(selected_index, first_buffer_index);
266 if (distance_beginning < MIN_BUFFER_MARGIN)
267 return true;
269 if (pb->num_loaded - distance_beginning < MIN_BUFFER_MARGIN)
270 return true;
271 return false;
274 static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer *pb,
275 int index)
277 int buffer_index = playlist_buffer_get_index(pb, index);
278 /* Make sure that we are not returning an invalid pointer.
279 In some cases, when scrolling really fast, it could happen that a reqested track
280 has not been pre-loaded */
281 if (buffer_index < 0) {
282 playlist_buffer_load_entries_screen(&viewer.buffer,
283 pb->direction == FORWARD ? BACKWARD : FORWARD,
284 index);
286 } else if (buffer_index >= pb->num_loaded) {
287 playlist_buffer_load_entries_screen(&viewer.buffer,
288 pb->direction,
289 index);
291 buffer_index = playlist_buffer_get_index(pb, index);
292 if (buffer_index < 0 || buffer_index >= pb->num_loaded) {
293 /* This really shouldn't happen. If this happens, then
294 the name_buffer is probably too small to store enough
295 titles to fill the screen, and preload data in the short
296 direction.
298 If this happens then scrolling performance will probably
299 be quite low, but it's better then having Data Abort errors */
300 playlist_buffer_load_entries(pb, index, FORWARD);
301 buffer_index = playlist_buffer_get_index(pb, index);
303 return &(pb->tracks[buffer_index]);
306 /* Initialize the playlist viewer. */
307 static bool playlist_viewer_init(struct playlist_viewer * viewer,
308 const char* filename, bool reload)
310 char* buffer;
311 size_t buffer_size;
312 bool is_playing = audio_status() & (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE);
313 bool have_list = filename || is_playing;
314 if (!have_list && (global_status.resume_index != -1))
316 /* Try to restore the list from control file */
317 have_list = (playlist_resume() != -1);
319 if (!have_list && (playlist_amount() > 0))
321 /*If dynamic playlist still exists, view it anyway even
322 if playback has reached the end of the playlist */
323 have_list = true;
325 if (!have_list)
327 /* Nothing to view, exit */
328 splash(HZ, str(LANG_CATALOG_NO_PLAYLISTS));
329 return false;
332 buffer = plugin_get_buffer(&buffer_size);
333 if (!buffer)
334 return false;
336 if (!filename)
337 viewer->playlist = NULL;
338 else
340 /* Viewing playlist on disk */
341 const char *dir, *file;
342 char *temp_ptr;
343 char *index_buffer = NULL;
344 ssize_t index_buffer_size = 0;
346 viewer->playlist = &temp_playlist;
348 /* Separate directory from filename */
349 temp_ptr = strrchr(filename+1,'/');
350 if (temp_ptr)
352 *temp_ptr = 0;
353 dir = filename;
354 file = temp_ptr + 1;
356 else
358 dir = "/";
359 file = filename+1;
362 if (is_playing)
364 /* Something is playing, use half the plugin buffer for playlist
365 indices */
366 index_buffer_size = buffer_size / 2;
367 index_buffer = buffer;
370 playlist_create_ex(viewer->playlist, dir, file, index_buffer,
371 index_buffer_size, buffer+index_buffer_size,
372 buffer_size-index_buffer_size);
374 if (temp_ptr)
375 *temp_ptr = '/';
377 buffer += index_buffer_size;
378 buffer_size -= index_buffer_size;
380 playlist_buffer_init(&viewer->buffer, buffer, buffer_size);
382 viewer->moving_track = -1;
383 viewer->moving_playlist_index = -1;
385 if (!reload)
387 if (viewer->playlist)
388 viewer->selected_track = 0;
389 else
390 viewer->selected_track = playlist_get_display_index() - 1;
393 if (!update_playlist(true))
394 return false;
395 return true;
398 /* Format trackname for display purposes */
399 static void format_name(char* dest, const char* src)
401 switch (global_settings.playlist_viewer_track_display)
403 case 0:
404 default:
406 /* Only display the filename */
407 char* p = strrchr(src, '/');
409 strcpy(dest, p+1);
411 /* Remove the extension */
412 strrsplt(dest, '.');
414 break;
416 case 1:
417 /* Full path */
418 strcpy(dest, src);
419 break;
423 /* Format display line */
424 static void format_line(const struct playlist_entry* track, char* str,
425 int len)
427 char name[MAX_PATH];
428 char *skipped = "";
430 format_name(name, track->name);
432 if (track->skipped)
433 skipped = "(ERR) ";
435 if (global_settings.playlist_viewer_indices)
436 /* Display playlist index */
437 snprintf(str, len, "%d. %s%s", track->display_index, skipped, name);
438 else
439 snprintf(str, len, "%s%s", skipped, name);
443 /* Update playlist in case something has changed or forced */
444 static bool update_playlist(bool force)
446 if (!viewer.playlist)
447 playlist_get_resume_info(&viewer.current_playing_track);
448 else
449 viewer.current_playing_track = -1;
450 int nb_tracks = playlist_amount_ex(viewer.playlist);
451 force = force || nb_tracks != viewer.num_tracks;
452 if (force)
454 /* Reload tracks */
455 viewer.num_tracks = nb_tracks;
456 if (viewer.num_tracks <= 0)
458 global_status.resume_index = -1;
459 global_status.resume_offset = -1;
460 return false;
462 playlist_buffer_load_entries_screen(&viewer.buffer, FORWARD,
463 viewer.selected_track);
464 if (viewer.buffer.num_loaded <= 0)
466 global_status.resume_index = -1;
467 global_status.resume_offset = -1;
468 return false;
471 return true;
474 /* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
475 Returns -1 if USB attached, 0 if no playlist change, and 1 if playlist
476 changed. */
477 static int onplay_menu(int index)
479 int result, ret = 0;
480 struct playlist_entry * current_track =
481 playlist_buffer_get_track(&viewer.buffer, index);
482 MENUITEM_STRINGLIST(menu_items, ID2P(LANG_PLAYLIST), NULL,
483 ID2P(LANG_CURRENT_PLAYLIST), ID2P(LANG_CATALOG),
484 ID2P(LANG_REMOVE), ID2P(LANG_MOVE), ID2P(LANG_SHUFFLE),
485 ID2P(LANG_SAVE_DYNAMIC_PLAYLIST));
486 bool current = (current_track->index == viewer.current_playing_track);
488 result = do_menu(&menu_items, NULL, NULL, false);
489 if (result == MENU_ATTACHED_USB)
491 ret = -1;
493 else if (result >= 0)
495 /* Abort current move */
496 viewer.moving_track = -1;
497 viewer.moving_playlist_index = -1;
499 switch (result)
501 case 0:
502 /* playlist */
503 onplay_show_playlist_menu(current_track->name);
504 ret = 0;
505 break;
506 case 1:
507 /* add to catalog */
508 onplay_show_playlist_cat_menu(current_track->name);
509 ret = 0;
510 break;
511 case 2:
512 /* delete track */
513 playlist_delete(viewer.playlist, current_track->index);
514 if (current)
516 if (playlist_amount_ex(viewer.playlist) <= 0)
517 audio_stop();
518 else
520 /* Start playing new track except if it's the lasttrack
521 track in the playlist and repeat mode is disabled */
522 current_track =
523 playlist_buffer_get_track(&viewer.buffer, index);
524 if (current_track->display_index!=viewer.num_tracks ||
525 global_settings.repeat_mode == REPEAT_ALL)
527 audio_play(0);
528 viewer.current_playing_track = -1;
532 ret = 1;
533 break;
534 case 3:
535 /* move track */
536 viewer.moving_track = index;
537 viewer.moving_playlist_index = current_track->index;
538 ret = 0;
539 break;
540 case 4:
541 /* shuffle */
542 playlist_randomise(viewer.playlist, current_tick, false);
543 ret = 1;
544 break;
545 case 5:
546 /* save playlist */
547 save_playlist_screen(viewer.playlist);
548 ret = 0;
549 break;
552 return ret;
555 /* View current playlist */
556 enum playlist_viewer_result playlist_viewer(void)
558 return playlist_viewer_ex(NULL);
561 static int get_track_num(struct playlist_viewer *local_viewer,
562 int selected_item)
564 if (local_viewer->moving_track >= 0)
566 if (local_viewer->selected_track == selected_item)
568 return local_viewer->moving_track;
570 else if (local_viewer->selected_track > selected_item
571 && selected_item >= local_viewer->moving_track)
573 return selected_item+1; /* move down */
575 else if (local_viewer->selected_track < selected_item
576 && selected_item <= local_viewer->moving_track)
578 return selected_item-1; /* move up */
581 return selected_item;
584 static const char* playlist_callback_name(int selected_item,
585 void *data,
586 char *buffer,
587 size_t buffer_len)
589 struct playlist_viewer *local_viewer = (struct playlist_viewer *)data;
591 int track_num = get_track_num(local_viewer, selected_item);
592 struct playlist_entry *track =
593 playlist_buffer_get_track(&(local_viewer->buffer), track_num);
595 format_line(track, buffer, buffer_len);
597 return(buffer);
601 static enum themable_icons playlist_callback_icons(int selected_item,
602 void *data)
604 struct playlist_viewer *local_viewer = (struct playlist_viewer *)data;
606 int track_num = get_track_num(local_viewer, selected_item);
607 struct playlist_entry *track =
608 playlist_buffer_get_track(&(local_viewer->buffer), track_num);
610 if (track->index == local_viewer->current_playing_track)
612 /* Current playing track */
613 return Icon_Audio;
615 else if (track->index == local_viewer->moving_playlist_index)
617 /* Track we are moving */
618 return Icon_Moving;
620 else if (track->queued)
622 /* Queued track */
623 return Icon_Queued;
625 else
626 return Icon_NOICON;
629 static int playlist_callback_voice(int selected_item, void *data)
631 struct playlist_viewer *local_viewer = (struct playlist_viewer *)data;
633 int track_num = get_track_num(local_viewer, selected_item);
634 struct playlist_entry *track =
635 playlist_buffer_get_track(&(local_viewer->buffer), track_num);
637 bool enqueue = false;
639 if (global_settings.talk_file_clip || global_settings.talk_file == 2)
641 if (global_settings.playlist_viewer_indices)
643 talk_number(track->display_index, false);
644 enqueue = true;
646 talk_file_or_spell(NULL, track->name, NULL, enqueue);
648 else if (global_settings.talk_file == 1) /* as numbers */
650 talk_id(VOICE_FILE, false);
651 talk_number(track->display_index, true);
654 return 0;
657 /* Main viewer function. Filename identifies playlist to be viewed. If NULL,
658 view current playlist. */
659 enum playlist_viewer_result playlist_viewer_ex(const char* filename)
661 enum playlist_viewer_result ret = PLAYLIST_VIEWER_OK;
662 bool exit = false; /* exit viewer */
663 int button;
664 bool dirty = false;
665 struct gui_synclist playlist_lists;
666 if (!playlist_viewer_init(&viewer, filename, false))
667 goto exit;
669 push_current_activity(ACTIVITY_PLAYLISTVIEWER);
670 gui_synclist_init(&playlist_lists, playlist_callback_name,
671 &viewer, false, 1, NULL);
672 gui_synclist_set_voice_callback(&playlist_lists, playlist_callback_voice);
673 gui_synclist_set_icon_callback(&playlist_lists,
674 global_settings.playlist_viewer_icons?
675 &playlist_callback_icons:NULL);
676 gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks);
677 gui_synclist_set_title(&playlist_lists, str(LANG_PLAYLIST), Icon_Playlist);
678 gui_synclist_select_item(&playlist_lists, viewer.selected_track);
679 gui_synclist_draw(&playlist_lists);
680 gui_synclist_speak_item(&playlist_lists);
681 while (!exit)
683 int track;
685 if (global_status.resume_index != -1 && !viewer.playlist)
686 playlist_get_resume_info(&track);
687 else
688 track = -1;
690 if (track != viewer.current_playing_track ||
691 playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
693 /* Playlist has changed (new track started?) */
694 if (!update_playlist(false))
695 goto exit;
696 /*Needed because update_playlist gives wrong value when
697 playing is stopped*/
698 viewer.current_playing_track = track;
699 gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks);
701 gui_synclist_draw(&playlist_lists);
704 /* Timeout so we can determine if play status has changed */
705 bool res = list_do_action(CONTEXT_TREE, HZ/2,
706 &playlist_lists, &button, LIST_WRAP_UNLESS_HELD);
707 /* during moving, another redraw is going to be needed,
708 * since viewer.selected_track is updated too late (after the first draw)
709 * drawing the moving item needs it */
710 viewer.selected_track=gui_synclist_get_sel_pos(&playlist_lists);
711 if (res)
713 bool reload = playlist_buffer_needs_reload(&viewer.buffer,
714 viewer.selected_track);
715 if (reload)
716 playlist_buffer_load_entries_screen(&viewer.buffer,
717 button == ACTION_STD_NEXT ? FORWARD : BACKWARD,
718 viewer.selected_track);
719 if (reload || viewer.moving_track >= 0)
720 gui_synclist_draw(&playlist_lists);
722 switch (button)
724 case ACTION_TREE_WPS:
725 case ACTION_STD_CANCEL:
727 if (viewer.moving_track >= 0)
729 viewer.selected_track = viewer.moving_track;
730 gui_synclist_select_item(&playlist_lists, viewer.moving_track);
731 viewer.moving_track = -1;
732 viewer.moving_playlist_index = -1;
733 gui_synclist_draw(&playlist_lists);
735 else
737 exit = true;
738 ret = PLAYLIST_VIEWER_CANCEL;
740 break;
742 case ACTION_STD_OK:
744 struct playlist_entry * current_track =
745 playlist_buffer_get_track(&viewer.buffer,
746 viewer.selected_track);
748 if (viewer.moving_track >= 0)
750 /* Move track */
751 int ret_val;
753 ret_val = playlist_move(viewer.playlist,
754 viewer.moving_playlist_index,
755 current_track->index);
756 if (ret_val < 0)
757 splashf(HZ, (unsigned char *)"%s %s", str(LANG_MOVE),
758 str(LANG_FAILED));
759 update_playlist(true);
760 viewer.moving_track = -1;
761 viewer.moving_playlist_index = -1;
762 dirty = true;
764 else if (!viewer.playlist)
766 /* play new track */
767 if (!global_settings.party_mode)
769 playlist_start(current_track->index, 0);
770 update_playlist(false);
773 else if (!global_settings.party_mode)
775 int start_index = current_track->index;
776 if (!warn_on_pl_erase())
778 gui_synclist_draw(&playlist_lists);
779 break;
781 /* New playlist */
782 if (playlist_set_current(viewer.playlist) < 0)
783 goto exit;
784 if (global_settings.playlist_shuffle)
785 start_index = playlist_shuffle(current_tick, start_index);
786 playlist_start(start_index, 0);
788 /* Our playlist is now the current list */
789 if (!playlist_viewer_init(&viewer, NULL, true))
790 goto exit;
791 exit = true;
793 gui_synclist_draw(&playlist_lists);
795 break;
797 case ACTION_STD_CONTEXT:
799 /* ON+PLAY menu */
800 int ret_val;
802 ret_val = onplay_menu(viewer.selected_track);
804 if (ret_val < 0)
806 ret = PLAYLIST_VIEWER_USB;
807 goto exit;
809 else if (ret_val > 0)
811 /* Playlist changed */
812 gui_synclist_del_item(&playlist_lists);
813 update_playlist(true);
814 if (viewer.num_tracks <= 0)
815 exit = true;
816 if (viewer.selected_track >= viewer.num_tracks)
817 viewer.selected_track = viewer.num_tracks-1;
818 dirty = true;
820 gui_synclist_draw(&playlist_lists);
821 break;
823 case ACTION_STD_MENU:
824 ret = PLAYLIST_VIEWER_MAINMENU;
825 goto exit;
826 default:
827 if(default_event_handler(button) == SYS_USB_CONNECTED)
829 ret = PLAYLIST_VIEWER_USB;
830 goto exit;
832 break;
836 exit:
837 pop_current_activity();
838 if (viewer.playlist)
840 if(dirty && yesno_pop(ID2P(LANG_SAVE_CHANGES)))
841 save_playlist_screen(viewer.playlist);
842 playlist_close(viewer.playlist);
844 return ret;
847 static const char* playlist_search_callback_name(int selected_item, void * data,
848 char *buffer, size_t buffer_len)
850 (void)buffer_len; /* this should probably be used */
851 int *found_indicies = (int*)data;
852 static struct playlist_track_info track;
853 playlist_get_track_info(viewer.playlist, found_indicies[selected_item], &track);
854 format_name(buffer, track.filename);
855 return buffer;
858 bool search_playlist(void)
860 char search_str[32] = "";
861 bool ret = false, exit = false;
862 int i, playlist_count;
863 int found_indicies[MAX_PLAYLIST_ENTRIES];
864 int found_indicies_count = 0, last_found_count = -1;
865 int button;
866 struct gui_synclist playlist_lists;
867 struct playlist_track_info track;
869 if (!playlist_viewer_init(&viewer, 0, false))
870 return ret;
871 if (kbd_input(search_str, sizeof(search_str)) < 0)
872 return ret;
873 lcd_clear_display();
874 playlist_count = playlist_amount_ex(viewer.playlist);
876 cpu_boost(true);
878 for (i = 0; i < playlist_count &&
879 found_indicies_count < MAX_PLAYLIST_ENTRIES; i++)
881 if (found_indicies_count != last_found_count)
883 splashf(0, str(LANG_PLAYLIST_SEARCH_MSG), found_indicies_count,
884 str(LANG_OFF_ABORT));
885 last_found_count = found_indicies_count;
888 if (action_userabort(TIMEOUT_NOBLOCK))
889 break;
891 playlist_get_track_info(viewer.playlist, i, &track);
893 if (strcasestr(track.filename,search_str))
894 found_indicies[found_indicies_count++] = track.index;
896 yield();
899 cpu_boost(false);
901 if (!found_indicies_count)
903 return ret;
905 backlight_on();
907 gui_synclist_init(&playlist_lists, playlist_search_callback_name,
908 found_indicies, false, 1, NULL);
909 gui_synclist_set_title(&playlist_lists, str(LANG_SEARCH_RESULTS), NOICON);
910 gui_synclist_set_icon_callback(&playlist_lists, NULL);
911 gui_synclist_set_nb_items(&playlist_lists, found_indicies_count);
912 gui_synclist_select_item(&playlist_lists, 0);
913 gui_synclist_draw(&playlist_lists);
914 while (!exit)
916 if (list_do_action(CONTEXT_LIST, HZ/4,
917 &playlist_lists, &button, LIST_WRAP_UNLESS_HELD))
918 continue;
919 switch (button)
921 case ACTION_STD_CANCEL:
922 exit = true;
923 break;
925 case ACTION_STD_OK:
927 int sel = gui_synclist_get_sel_pos(&playlist_lists);
928 playlist_start(found_indicies[sel], 0);
929 exit = 1;
931 break;
933 default:
934 if (default_event_handler(button) == SYS_USB_CONNECTED)
936 ret = true;
937 exit = true;
939 break;
942 return ret;