blob: 092d23406b4373fb8a62ac0126abce987e075e98 [file] [log] [blame]
Björn Stenbergd7a55e12003-03-12 20:21:30 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
Nicolas Pennequin357ffb32008-05-05 10:32:46 +000010 * Copyright (C) 2002 Björn Stenberg
Björn Stenbergd7a55e12003-03-12 20:21:30 +000011 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Björn Stenbergd7a55e12003-03-12 20:21:30 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
Dan Everton1429c9b2006-03-20 11:46:06 +000021#include <errno.h>
Björn Stenbergd7a55e12003-03-12 20:21:30 +000022#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25#include <stdbool.h>
26
Linus Nielsen Feltzing2bf4a052003-04-01 20:58:31 +000027#include "debug.h"
Björn Stenbergd7a55e12003-03-12 20:21:30 +000028#include "lcd.h"
Björn Stenbergd7a55e12003-03-12 20:21:30 +000029#include "file.h"
Linus Nielsen Feltzing8a237a82005-04-04 12:06:29 +000030#include "audio.h"
Björn Stenbergd7a55e12003-03-12 20:21:30 +000031#include "menu.h"
32#include "lang.h"
33#include "playlist.h"
34#include "button.h"
35#include "kernel.h"
36#include "keyboard.h"
Linus Nielsen Feltzing2bf4a052003-04-01 20:58:31 +000037#include "mp3data.h"
Björn Stenberg51b45d52008-10-15 06:38:51 +000038#include "metadata.h"
Linus Nielsen Feltzing2bf4a052003-04-01 20:58:31 +000039#include "screens.h"
Björn Stenberg276b9e32003-03-24 13:17:43 +000040#include "tree.h"
Hardeep Sidhu9e426202003-07-01 21:05:43 +000041#include "settings.h"
Hardeep Sidhu107ebc52004-01-26 17:05:21 +000042#include "playlist_viewer.h"
Jörg Hohensohn4f36ea82004-03-14 21:33:53 +000043#include "talk.h"
Hardeep Sidhu9e426202003-07-01 21:05:43 +000044#include "onplay.h"
Björn Stenbergfb00c212004-05-21 20:08:24 +000045#include "filetypes.h"
Linus Nielsen Feltzingae9b3192004-07-13 13:57:14 +000046#include "plugin.h"
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +000047#include "bookmark.h"
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +000048#include "action.h"
Kevin Ferraree991bee2005-11-16 15:12:15 +000049#include "splash.h"
Kevin Ferrare40cc43a2005-11-22 22:19:08 +000050#include "yesno.h"
Jonathan Gordon3a7760c2007-04-30 13:41:33 +000051#include "menus/exported_menus.h"
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +000052#ifdef HAVE_LCD_BITMAP
53#include "icons.h"
54#endif
Linus Nielsen Feltzing22c1a8e2005-06-24 22:33:42 +000055#include "sound_menu.h"
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +000056#include "playlist_menu.h"
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +000057#include "playlist_catalog.h"
Jonathan Gordon710ccb72006-10-25 10:17:57 +000058#ifdef HAVE_TAGCACHE
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +000059#include "tagtree.h"
Jonathan Gordon710ccb72006-10-25 10:17:57 +000060#endif
Nicolas Pennequin6190a0d2007-05-14 17:34:52 +000061#include "cuesheet.h"
Jonathan Gordoneee54232010-01-29 07:52:13 +000062#include "statusbar-skinned.h"
Jonathan Gordon1da2f012008-10-05 13:01:54 +000063#include "pitchscreen.h"
Thomas Martitz595a8282009-08-23 15:06:11 +000064#include "viewport.h"
Michael Sevakis7d1a47c2013-08-05 22:02:45 -040065#include "pathfuncs.h"
Jonathan Gordon101693f2011-11-15 13:22:02 +000066#include "shortcuts.h"
Nicolas Pennequin1cf2ec32007-04-25 22:08:00 +000067
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +000068static int context;
Michael Sevakis7d1a47c2013-08-05 22:02:45 -040069static const char *selected_file = NULL;
Hardeep Sidhu9e426202003-07-01 21:05:43 +000070static int selected_file_attr = 0;
71static int onplay_result = ONPLAY_OK;
William Wilgus0c06e5f2017-09-07 12:27:54 +020072extern struct menu_item_ex file_menu; /* settings_menu.c */
Björn Stenbergd7a55e12003-03-12 20:21:30 +000073
Jonathan Gordon3a7760c2007-04-30 13:41:33 +000074/* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */
75#define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \
76 static const struct menu_item_ex *name##_[] = {__VA_ARGS__}; \
77 static const struct menu_callback_with_desc name##__ = {callback,str,icon};\
Jonathan Gordona6f2b822007-11-04 12:40:18 +000078 static const struct menu_item_ex name = \
Jonathan Gordon3a7760c2007-04-30 13:41:33 +000079 {MT_MENU|MENU_HAS_DESC|MENU_EXITAFTERTHISMENU| \
80 MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
81 { (void*)name##_},{.callback_and_desc = & name##__}};
Teruaki Kawashimaf8840682010-02-14 12:42:32 +000082
Michael Sevakis7d1a47c2013-08-05 22:02:45 -040083/* Used for directory move, copy and delete */
84struct dirrecurse_params
85{
86 char path[MAX_PATH]; /* Buffer for full path */
87 size_t append; /* Append position in 'path' for stack push */
88};
89
90enum clipboard_op_flags
91{
92 PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */
93 PASTE_COPY = 0x01, /* Is a copy operation */
94 PASTE_OVERWRITE = 0x02, /* Overwrite destination */
95 PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */
96};
97
98/* result codec of various onplay operations */
99enum onplay_result_code
100{
101 /* Anything < 0 is failure */
102 OPRC_SUCCESS = 0, /* All operations completed successfully */
103 OPRC_NOOP = 1, /* Operation didn't need to do anything */
104 OPRC_CANCELLED = 2, /* Operation was cancelled by user */
105 OPRC_NOOVERWRT = 3,
106};
107
108static struct clipboard
109{
110 char path[MAX_PATH]; /* Clipped file's path */
111 unsigned int attr; /* Clipped file's attributes */
112 unsigned int flags; /* Operation type flags */
113} clipboard;
114
115/* Empty the clipboard */
116static void clipboard_clear_selection(struct clipboard *clip)
117{
118 clip->path[0] = '\0';
119 clip->attr = 0;
120 clip->flags = 0;
121}
122
123/* Store the selection in the clipboard */
124static bool clipboard_clip(struct clipboard *clip, const char *path,
125 unsigned int attr, unsigned int flags)
126{
127 /* if it fits it clips */
128 if (strlcpy(clip->path, path, sizeof (clip->path))
129 < sizeof (clip->path)) {
130 clip->attr = attr;
131 clip->flags = flags;
132 return true;
133 }
134 else {
135 clipboard_clear_selection(clip);
136 return false;
137 }
138}
139
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +0000140/* ----------------------------------------------------------------------- */
141/* Displays the bookmark menu options for the user to decide. This is an */
142/* interface function. */
143/* ----------------------------------------------------------------------- */
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000144
Hardeep Sidhu8c2bcf12007-05-15 23:30:30 +0000145static int bookmark_menu_callback(int action,
146 const struct menu_item_ex *this_item);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000147MENUITEM_FUNCTION(bookmark_create_menu_item, 0,
148 ID2P(LANG_BOOKMARK_MENU_CREATE),
Osborne Jacobsfb30d012012-03-11 00:47:28 -0500149 bookmark_create_menu, NULL,
150 bookmark_menu_callback, Icon_Bookmark);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000151MENUITEM_FUNCTION(bookmark_load_menu_item, 0,
152 ID2P(LANG_BOOKMARK_MENU_LIST),
153 bookmark_load_menu, NULL,
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000154 bookmark_menu_callback, Icon_Bookmark);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000155MAKE_ONPLAYMENU(bookmark_menu, ID2P(LANG_BOOKMARK_MENU),
156 bookmark_menu_callback, Icon_Bookmark,
157 &bookmark_create_menu_item, &bookmark_load_menu_item);
Hardeep Sidhu8c2bcf12007-05-15 23:30:30 +0000158static int bookmark_menu_callback(int action,
159 const struct menu_item_ex *this_item)
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +0000160{
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000161 switch (action)
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +0000162 {
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000163 case ACTION_REQUEST_MENUITEM:
Osborne Jacobsfb30d012012-03-11 00:47:28 -0500164 /* hide create bookmark option if bookmarking isn't currently possible (no track playing, queued tracks...) */
165 if (this_item == &bookmark_create_menu_item)
166 {
167 if (!bookmark_is_bookmarkable_state())
168 return ACTION_EXIT_MENUITEM;
169 }
170 /* hide loading bookmarks menu if no bookmarks exist */
171 else if (this_item == &bookmark_load_menu_item)
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000172 {
Alexander Levin7d4c0c52010-07-06 16:53:52 +0000173 if (!bookmark_exists())
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000174 return ACTION_EXIT_MENUITEM;
175 }
Osborne Jacobsfb30d012012-03-11 00:47:28 -0500176 /* hide the bookmark menu if bookmarks can't be loaded or created */
177 else if (!bookmark_is_bookmarkable_state() && !bookmark_exists())
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000178 return ACTION_EXIT_MENUITEM;
179 break;
180#ifdef HAVE_LCD_CHARCELLS
181 case ACTION_ENTER_MENUITEM:
182 status_set_param(true);
183 break;
184#endif
185 case ACTION_EXIT_MENUITEM:
186#ifdef HAVE_LCD_CHARCELLS
187 status_set_param(false);
188#endif
189 settings_save();
190 break;
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +0000191 }
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000192 return action;
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +0000193}
194
Solomon Peachyfe951272018-12-14 08:20:25 -0500195/* playing_time screen context */
196struct playing_time_info {
197 int curr_playing; /* index of currently playing track in playlist */
198 int nb_tracks; /* how many tracks in playlist */
199 /* seconds before and after current position, and total. Datatype
200 allows for values up to 68years. If I had kept it in ms
201 though, it would have overflowed at 24days, which takes
202 something like 8.5GB at 32kbps, and so we could conceivably
203 have playlists lasting longer than that. */
204 long secs_bef, secs_aft, secs_ttl;
205 long trk_secs_bef, trk_secs_aft, trk_secs_ttl;
206 /* kilobytes played before and after current pos, and total.
207 Kilobytes because bytes would overflow. Data type range is up
208 to 2TB. */
209 long kbs_bef, kbs_aft, kbs_ttl;
210};
211
212/* list callback for playing_time screen */
213static const char * playing_time_get_or_speak_info(int selected_item, void * data,
214 char *buf, size_t buffer_len,
215 bool say_it)
216{
217 struct playing_time_info *pti = (struct playing_time_info *)data;
218 switch(selected_item) {
219 case 0: { /* elapsed and total time */
220 char timestr1[25], timestr2[25];
221 format_time_auto(timestr1, sizeof(timestr1), pti->secs_bef,
222 UNIT_SEC, false);
223 format_time_auto(timestr2, sizeof(timestr2), pti->secs_ttl,
224 UNIT_SEC, false);
225 long elapsed_perc; /* percentage of duration elapsed */
226 if (pti->secs_ttl == 0)
227 elapsed_perc = 0;
228 else if (pti->secs_ttl <= 0xFFFFFF)
229 elapsed_perc = pti->secs_bef *100 / pti->secs_ttl;
230 else /* sacrifice some precision to avoid overflow */
231 elapsed_perc = (pti->secs_bef>>7) *100 /(pti->secs_ttl>>7);
232 snprintf(buf, buffer_len, str(LANG_PLAYTIME_ELAPSED),
233 timestr1, timestr2, elapsed_perc);
234 if (say_it)
235 talk_ids(false, LANG_PLAYTIME_ELAPSED,
236 TALK_ID(pti->secs_bef, UNIT_TIME),
237 VOICE_OF,
238 TALK_ID(pti->secs_ttl, UNIT_TIME),
239 VOICE_PAUSE,
240 TALK_ID(elapsed_perc, UNIT_PERCENT));
241 break;
242 }
243 case 1: { /* playlist remaining time */
244 char timestr[25];
245 format_time_auto(timestr, sizeof(timestr), pti->secs_aft,
246 UNIT_SEC, false);
247 snprintf(buf, buffer_len, str(LANG_PLAYTIME_REMAINING),
248 timestr);
249 if (say_it)
250 talk_ids(false, LANG_PLAYTIME_REMAINING,
251 TALK_ID(pti->secs_aft, UNIT_TIME));
252 break;
253 }
254 case 2: { /* track elapsed and duration */
255 char timestr1[25], timestr2[25];
256 format_time_auto(timestr1, sizeof(timestr1), pti->trk_secs_bef,
257 UNIT_SEC, false);
258 format_time_auto(timestr2, sizeof(timestr2), pti->trk_secs_ttl,
259 UNIT_SEC, false);
260 long elapsed_perc; /* percentage of duration elapsed */
261 if (pti->trk_secs_ttl == 0)
262 elapsed_perc = 0;
263 else if (pti->trk_secs_ttl <= 0xFFFFFF)
264 elapsed_perc = pti->trk_secs_bef *100 / pti->trk_secs_ttl;
265 else /* sacrifice some precision to avoid overflow */
266 elapsed_perc = (pti->trk_secs_bef>>7) *100 /(pti->trk_secs_ttl>>7);
267 snprintf(buf, buffer_len, str(LANG_PLAYTIME_TRK_ELAPSED),
268 timestr1, timestr2, elapsed_perc);
269 if (say_it)
270 talk_ids(false, LANG_PLAYTIME_TRK_ELAPSED,
271 TALK_ID(pti->trk_secs_bef, UNIT_TIME),
272 VOICE_OF,
273 TALK_ID(pti->trk_secs_ttl, UNIT_TIME),
274 VOICE_PAUSE,
275 TALK_ID(elapsed_perc, UNIT_PERCENT));
276 break;
277 }
278 case 3: { /* track remaining time */
279 char timestr[25];
280 format_time_auto(timestr, sizeof(timestr), pti->trk_secs_aft,
281 UNIT_SEC, false);
282 snprintf(buf, buffer_len, str(LANG_PLAYTIME_TRK_REMAINING),
283 timestr);
284 if (say_it)
285 talk_ids(false, LANG_PLAYTIME_TRK_REMAINING,
286 TALK_ID(pti->trk_secs_aft, UNIT_TIME));
287 break;
288 }
289 case 4: { /* track index */
290 int track_perc = (pti->curr_playing+1) *100 / pti->nb_tracks;
291 snprintf(buf, buffer_len, str(LANG_PLAYTIME_TRACK),
292 pti->curr_playing, pti->nb_tracks, track_perc);
293 if (say_it)
294 talk_ids(false, LANG_PLAYTIME_TRACK,
295 TALK_ID(pti->curr_playing+1, UNIT_INT),
296 VOICE_OF,
297 TALK_ID(pti->nb_tracks, UNIT_INT),
298 VOICE_PAUSE,
299 TALK_ID(track_perc, UNIT_PERCENT));
300 break;
301 }
302 case 5: { /* storage size */
303 char str1[10], str2[10], str3[10];
304 output_dyn_value(str1, sizeof(str1), pti->kbs_ttl, kibyte_units, 3, true);
305 output_dyn_value(str2, sizeof(str2), pti->kbs_bef, kibyte_units, 3, true);
306 output_dyn_value(str3, sizeof(str3), pti->kbs_aft, kibyte_units, 3, true);
307 snprintf(buf, buffer_len, str(LANG_PLAYTIME_STORAGE),
308 str1,str2,str3);
309 if (say_it) {
310 talk_id(LANG_PLAYTIME_STORAGE, false);
311 output_dyn_value(NULL, 0, pti->kbs_ttl, kibyte_units, 3, true);
312 talk_ids(true, VOICE_PAUSE, VOICE_PLAYTIME_DONE);
313 output_dyn_value(NULL, 0, pti->kbs_bef, kibyte_units, 3, true);
314 talk_id(LANG_PLAYTIME_REMAINING, true);
315 output_dyn_value(NULL, 0, pti->kbs_aft, kibyte_units, 3, true);
316 }
317 break;
318 }
319 case 6: { /* Average track file size */
320 char str[10];
321 long avg_track_size = pti->kbs_ttl /pti->nb_tracks;
322 output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true);
323 snprintf(buf, buffer_len, str(LANG_PLAYTIME_AVG_TRACK_SIZE),
324 str);
325 if (say_it) {
326 talk_id(LANG_PLAYTIME_AVG_TRACK_SIZE, false);
327 output_dyn_value(NULL, 0, avg_track_size, kibyte_units, 3, true);
328 }
329 break;
330 }
331 case 7: { /* Average bitrate */
332 /* Convert power of 2 kilobytes to power of 10 kilobits */
333 long avg_bitrate = pti->kbs_ttl / pti->secs_ttl *1024 *8 /1000;
334 snprintf(buf, buffer_len, str(LANG_PLAYTIME_AVG_BITRATE),
335 avg_bitrate);
336 if (say_it)
337 talk_ids(false, LANG_PLAYTIME_AVG_BITRATE,
338 TALK_ID(avg_bitrate, UNIT_KBIT));
339 break;
340 }
341 }
342 return buf;
343}
344
345static const char * playing_time_get_info(int selected_item, void * data,
346 char *buffer, size_t buffer_len)
347{
348 return playing_time_get_or_speak_info(selected_item, data,
349 buffer, buffer_len, false);
350}
351
352static int playing_time_speak_info(int selected_item, void * data)
353{
354 static char buffer[MAX_PATH];
355 playing_time_get_or_speak_info(selected_item, data,
356 buffer, MAX_PATH, true);
357 return 0;
358}
359
360/* playing time screen: shows total and elapsed playlist duration and
361 other stats */
362static bool playing_time(void)
363{
364 unsigned long talked_tick = current_tick;
365 struct playing_time_info pti;
366 struct playlist_track_info pltrack;
367 struct mp3entry id3;
368 int i, fd, ret;
369
370 pti.nb_tracks = playlist_amount();
371 playlist_get_resume_info(&pti.curr_playing);
372 struct mp3entry *curr_id3 = audio_current_track();
373 if (pti.curr_playing == -1 || !curr_id3)
374 return false;
375 pti.secs_bef = pti.trk_secs_bef = curr_id3->elapsed/1000;
376 pti.secs_aft = pti.trk_secs_aft
377 = (curr_id3->length -curr_id3->elapsed)/1000;
378 pti.kbs_bef = curr_id3->offset/1024;
379 pti.kbs_aft = (curr_id3->filesize -curr_id3->offset)/1024;
380
381 splash(0, ID2P(LANG_WAIT));
382
383 /* Go through each file in the playlist and get its stats. For
384 huge playlists this can take a while... The reference position
385 is the position at the moment this function was invoked,
386 although playback continues forward. */
387 for (i = 0; i < pti.nb_tracks; i++) {
388 /* Show a splash while we are loading. */
389 splashf(0, str(LANG_LOADING_PERCENT),
390 i*100/pti.nb_tracks, str(LANG_OFF_ABORT));
391 /* Voice equivalent */
392 if (TIME_AFTER(current_tick, talked_tick+5*HZ)) {
393 talked_tick = current_tick;
394 talk_ids(false, LANG_LOADING_PERCENT,
395 TALK_ID(i*100/pti.nb_tracks, UNIT_PERCENT));
396 }
397 if (action_userabort(TIMEOUT_NOBLOCK))
398 goto exit;
399
400 if (i == pti.curr_playing)
401 continue;
402
403 if (playlist_get_track_info(NULL, i, &pltrack) < 0)
404 goto error;
405 if ((fd = open(pltrack.filename, O_RDONLY)) < 0)
406 goto error;
407 ret = get_metadata(&id3, fd, pltrack.filename);
408 close(fd);
409 if (!ret)
410 goto error;
411
412 if (i < pti.curr_playing) {
413 pti.secs_bef += id3.length/1000;
414 pti.kbs_bef += id3.filesize/1024;
415 } else {
416 pti.secs_aft += id3.length/1000;
417 pti.kbs_aft += id3.filesize/1024;
418 }
419 }
420
421 pti.secs_ttl = pti.secs_bef +pti.secs_aft;
422 pti.trk_secs_ttl = pti.trk_secs_bef +pti.trk_secs_aft;
423 pti.kbs_ttl = pti.kbs_bef +pti.kbs_aft;
424
425 struct gui_synclist pt_lists;
426 int key;
427
428 gui_synclist_init(&pt_lists, &playing_time_get_info, &pti, true, 1, NULL);
429 if (global_settings.talk_menu)
430 gui_synclist_set_voice_callback(&pt_lists, playing_time_speak_info);
431 gui_synclist_set_nb_items(&pt_lists, 8);
432 gui_synclist_draw(&pt_lists);
433 gui_syncstatusbar_draw(&statusbars, true);
434 gui_synclist_speak_item(&pt_lists);
435 while (true) {
436 gui_syncstatusbar_draw(&statusbars, false);
437 if (list_do_action(CONTEXT_LIST, HZ/2,
438 &pt_lists, &key, LIST_WRAP_UNLESS_HELD) == 0
439 && key!=ACTION_NONE && key!=ACTION_UNKNOWN)
440 {
441 talk_force_shutup();
442 return(default_event_handler(key) == SYS_USB_CONNECTED);
443 }
444 }
445 error:
446 splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
447 exit:
448 return false;
449}
450
451
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000452/* CONTEXT_WPS playlist options */
Tomas Salfischberger8fd03782005-06-23 15:49:36 +0000453static bool shuffle_playlist(void)
454{
Tomas Salfischbergerbec1afa2005-06-23 16:27:15 +0000455 playlist_sort(NULL, true);
Zakk Roberts5b93eb62006-03-22 02:18:44 +0000456 playlist_randomise(NULL, current_tick, true);
457
Tomas Salfischberger8fd03782005-06-23 15:49:36 +0000458 return false;
459}
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +0000460static bool save_playlist(void)
461{
Hardeep Sidhu0cca6ca2006-02-09 09:09:32 +0000462 save_playlist_screen(NULL);
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +0000463 return false;
464}
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000465
Thomas Martitzc19e5362010-02-20 19:06:39 +0000466extern struct menu_item_ex view_cur_playlist; /* from playlist_menu.c */
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000467MENUITEM_FUNCTION(search_playlist_item, 0, ID2P(LANG_SEARCH_IN_PLAYLIST),
468 search_playlist, NULL, NULL, Icon_Playlist);
469MENUITEM_FUNCTION(playlist_save_item, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST),
470 save_playlist, NULL, NULL, Icon_Playlist);
471MENUITEM_FUNCTION(reshuffle_item, 0, ID2P(LANG_SHUFFLE_PLAYLIST),
472 shuffle_playlist, NULL, NULL, Icon_Playlist);
Solomon Peachyfe951272018-12-14 08:20:25 -0500473MENUITEM_FUNCTION(playing_time_item, 0, ID2P(LANG_PLAYING_TIME),
474 playing_time, NULL, NULL, Icon_Playlist);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000475MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_PLAYLIST),
476 NULL, Icon_Playlist,
Thomas Martitzc19e5362010-02-20 19:06:39 +0000477 &view_cur_playlist, &search_playlist_item,
Solomon Peachyfe951272018-12-14 08:20:25 -0500478 &playlist_save_item, &reshuffle_item, &playing_time_item
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000479 );
480
481/* CONTEXT_[TREE|ID3DB] playlist options */
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000482static bool add_to_playlist(int position, bool queue)
Björn Stenbergd7a55e12003-03-12 20:21:30 +0000483{
Linus Nielsen Feltzing8a237a82005-04-04 12:06:29 +0000484 bool new_playlist = !(audio_status() & AUDIO_STATUS_PLAY);
Nils Wallménius33c44462008-04-26 09:30:24 +0000485 const char *lines[] = {
Nils Wallménius5b769362007-08-06 13:08:36 +0000486 ID2P(LANG_RECURSE_DIRECTORY_QUESTION),
Hristo Kovachevd694a212006-03-21 11:11:31 +0000487 selected_file
488 };
Nils Wallménius33c44462008-04-26 09:30:24 +0000489 const struct text_message message={lines, 2};
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000490
Nils Wallménius01729e72008-08-15 08:27:39 +0000491 splash(0, ID2P(LANG_WAIT));
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000492
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000493 if (new_playlist)
494 playlist_create(NULL, NULL);
495
Hardeep Sidhu8fe45072006-09-15 23:51:21 +0000496 /* always set seed before inserting shuffled */
Teruaki Kawashima30faf032009-11-01 14:25:38 +0000497 if (position == PLAYLIST_INSERT_SHUFFLED ||
498 position == PLAYLIST_INSERT_LAST_SHUFFLED)
Dave Hooper494fd962009-10-28 22:27:38 +0000499 {
Hardeep Sidhu8fe45072006-09-15 23:51:21 +0000500 srand(current_tick);
Teruaki Kawashima30faf032009-11-01 14:25:38 +0000501 if (position == PLAYLIST_INSERT_LAST_SHUFFLED)
502 playlist_set_last_shuffled_start();
Dave Hooper494fd962009-10-28 22:27:38 +0000503 }
Hardeep Sidhu8fe45072006-09-15 23:51:21 +0000504
Jonathan Gordon710ccb72006-10-25 10:17:57 +0000505#ifdef HAVE_TAGCACHE
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000506 if (context == CONTEXT_ID3DB)
Hardeep Sidhu11e7ad52003-07-02 15:54:44 +0000507 {
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000508 tagtree_insert_selection_playlist(position, queue);
Hardeep Sidhu11e7ad52003-07-02 15:54:44 +0000509 }
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000510 else
Jonathan Gordon710ccb72006-10-25 10:17:57 +0000511#endif
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000512 {
Jonathan Gordon36a2e302007-04-18 13:03:01 +0000513 if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
Miika Pekkarinen887cfeb2006-08-10 09:50:45 +0000514 playlist_insert_track(NULL, selected_file, position, queue, true);
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000515 else if (selected_file_attr & ATTR_DIRECTORY)
516 {
517 bool recurse = false;
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000518
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000519 if (global_settings.recursive_dir_insert != RECURSE_ASK)
520 recurse = (bool)global_settings.recursive_dir_insert;
521 else
522 {
523 /* Ask if user wants to recurse directory */
524 recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES);
525 }
Thomas Martitz87d17442009-03-08 17:18:18 +0000526
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000527 playlist_insert_directory(NULL, selected_file, position, queue,
528 recurse);
529 }
Jonathan Gordon36a2e302007-04-18 13:03:01 +0000530 else if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000531 playlist_insert_playlist(NULL, selected_file, position, queue);
532 }
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000533
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000534 if (new_playlist && (playlist_amount() > 0))
535 {
536 /* nothing is currently playing so begin playing what we just
537 inserted */
538 if (global_settings.playlist_shuffle)
539 playlist_shuffle(current_tick, -1);
Michael Sevakis31b71222013-07-14 07:59:39 -0400540 playlist_start(0, 0, 0);
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000541 onplay_result = ONPLAY_START_PLAY;
542 }
543
544 return false;
545}
546
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000547static bool view_playlist(void)
548{
Linus Nielsen Feltzing8a237a82005-04-04 12:06:29 +0000549 bool was_playing = audio_status() & AUDIO_STATUS_PLAY;
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000550 bool result;
551
552 result = playlist_viewer_ex(selected_file);
553
Linus Nielsen Feltzing8a237a82005-04-04 12:06:29 +0000554 if (!was_playing && (audio_status() & AUDIO_STATUS_PLAY) &&
Hardeep Sidhu107ebc52004-01-26 17:05:21 +0000555 onplay_result == ONPLAY_OK)
556 /* playlist was started from viewer */
557 onplay_result = ONPLAY_START_PLAY;
558
559 return result;
560}
561
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000562static int playlist_insert_func(void *param)
563{
564 if (((intptr_t)param == PLAYLIST_REPLACE) && !warn_on_pl_erase())
565 return 0;
566 add_to_playlist((intptr_t)param, false);
567 return 0;
568}
569
570static int playlist_queue_func(void *param)
571{
572 add_to_playlist((intptr_t)param, true);
573 return 0;
574}
575
576static int treeplaylist_wplayback_callback(int action,
577 const struct menu_item_ex* this_item)
578{
579 (void)this_item;
580 switch (action)
581 {
582 case ACTION_REQUEST_MENUITEM:
583 if (audio_status() & AUDIO_STATUS_PLAY)
584 return action;
585 else
586 return ACTION_EXIT_MENUITEM;
587 break;
588 }
589 return action;
590}
591
592static int treeplaylist_callback(int action,
593 const struct menu_item_ex *this_item);
594
595/* insert items */
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +0000596MENUITEM_FUNCTION(i_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT),
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000597 playlist_insert_func, (intptr_t*)PLAYLIST_INSERT,
Teruaki Kawashima9bea3492010-02-15 13:04:09 +0000598 NULL, Icon_Playlist);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000599MENUITEM_FUNCTION(i_first_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT_FIRST),
600 playlist_insert_func, (intptr_t*)PLAYLIST_INSERT_FIRST,
601 treeplaylist_wplayback_callback, Icon_Playlist);
602MENUITEM_FUNCTION(i_last_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT_LAST),
603 playlist_insert_func, (intptr_t*)PLAYLIST_INSERT_LAST,
604 treeplaylist_wplayback_callback, Icon_Playlist);
605MENUITEM_FUNCTION(i_shuf_pl_item, MENU_FUNC_USEPARAM,
606 ID2P(LANG_INSERT_SHUFFLED), playlist_insert_func,
607 (intptr_t*)PLAYLIST_INSERT_SHUFFLED,
608 treeplaylist_callback, Icon_Playlist);
609MENUITEM_FUNCTION(i_last_shuf_pl_item, MENU_FUNC_USEPARAM,
610 ID2P(LANG_INSERT_LAST_SHUFFLED), playlist_insert_func,
611 (intptr_t*)PLAYLIST_INSERT_LAST_SHUFFLED,
612 treeplaylist_callback, Icon_Playlist);
613/* queue items */
614MENUITEM_FUNCTION(q_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE),
615 playlist_queue_func, (intptr_t*)PLAYLIST_INSERT,
616 treeplaylist_wplayback_callback, Icon_Playlist);
617MENUITEM_FUNCTION(q_first_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE_FIRST),
618 playlist_queue_func, (intptr_t*)PLAYLIST_INSERT_FIRST,
619 treeplaylist_wplayback_callback, Icon_Playlist);
620MENUITEM_FUNCTION(q_last_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE_LAST),
621 playlist_queue_func, (intptr_t*)PLAYLIST_INSERT_LAST,
622 treeplaylist_wplayback_callback, Icon_Playlist);
623MENUITEM_FUNCTION(q_shuf_pl_item, MENU_FUNC_USEPARAM,
624 ID2P(LANG_QUEUE_SHUFFLED), playlist_queue_func,
625 (intptr_t*)PLAYLIST_INSERT_SHUFFLED,
626 treeplaylist_wplayback_callback, Icon_Playlist);
627MENUITEM_FUNCTION(q_last_shuf_pl_item, MENU_FUNC_USEPARAM,
628 ID2P(LANG_QUEUE_LAST_SHUFFLED), playlist_queue_func,
629 (intptr_t*)PLAYLIST_INSERT_LAST_SHUFFLED,
630 treeplaylist_callback, Icon_Playlist);
631/* replace playlist */
632MENUITEM_FUNCTION(replace_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_REPLACE),
633 playlist_insert_func, (intptr_t*)PLAYLIST_REPLACE,
634 treeplaylist_wplayback_callback, Icon_Playlist);
635
636/* others */
637MENUITEM_FUNCTION(view_playlist_item, 0, ID2P(LANG_VIEW),
638 view_playlist, NULL,
639 treeplaylist_callback, Icon_Playlist);
640
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +0000641MAKE_ONPLAYMENU( tree_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST),
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000642 treeplaylist_callback, Icon_Playlist,
Solomon Peachyfe951272018-12-14 08:20:25 -0500643
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000644 /* view */
645 &view_playlist_item,
Solomon Peachyfe951272018-12-14 08:20:25 -0500646
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000647 /* insert */
648 &i_pl_item, &i_first_pl_item, &i_last_pl_item,
649 &i_shuf_pl_item, &i_last_shuf_pl_item,
650 /* queue */
Solomon Peachyfe951272018-12-14 08:20:25 -0500651
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000652 &q_pl_item, &q_first_pl_item, &q_last_pl_item,
653 &q_shuf_pl_item, &q_last_shuf_pl_item,
Solomon Peachyfe951272018-12-14 08:20:25 -0500654
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000655 /* replace */
656 &replace_pl_item
657 );
658static int treeplaylist_callback(int action,
659 const struct menu_item_ex *this_item)
660{
661 switch (action)
662 {
663 case ACTION_REQUEST_MENUITEM:
664 if (this_item == &tree_playlist_menu)
665 {
666 if (((selected_file_attr & FILE_ATTR_MASK) ==
667 FILE_ATTR_AUDIO) ||
668 ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)||
669 (selected_file_attr & ATTR_DIRECTORY))
670 {
671 return action;
672 }
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000673 }
674 else if (this_item == &view_playlist_item)
675 {
676 if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U &&
677 context == CONTEXT_TREE)
678 {
679 return action;
680 }
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000681 }
682 else if (this_item == &i_shuf_pl_item)
683 {
684 if ((audio_status() & AUDIO_STATUS_PLAY) ||
685 (selected_file_attr & ATTR_DIRECTORY) ||
686 ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U))
687 {
688 return action;
689 }
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000690 }
691 else if (this_item == &i_last_shuf_pl_item ||
692 this_item == &q_last_shuf_pl_item)
693 {
694 if ((playlist_amount() > 0) &&
695 (audio_status() & AUDIO_STATUS_PLAY) &&
696 ((selected_file_attr & ATTR_DIRECTORY) ||
697 ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)))
698 {
699 return action;
700 }
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000701 }
Teruaki Kawashima9bea3492010-02-15 13:04:09 +0000702 return ACTION_EXIT_MENUITEM;
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000703 break;
704 }
705 return action;
706}
707
Jonathan Gordon101693f2011-11-15 13:22:02 +0000708void onplay_show_playlist_menu(char* path)
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +0000709{
Jonathan Gordon101693f2011-11-15 13:22:02 +0000710 selected_file = path;
711 if (dir_exists(path))
712 selected_file_attr = ATTR_DIRECTORY;
713 else
714 selected_file_attr = filetype_get_attr(path);
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +0000715 do_menu(&tree_playlist_menu, NULL, NULL, false);
716}
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000717
718/* playlist catalog options */
Jens Arnold2597a132006-12-25 14:01:47 +0000719static bool cat_add_to_a_playlist(void)
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000720{
721 return catalog_add_to_a_playlist(selected_file, selected_file_attr,
Jonathan Gordon517aca82008-05-04 13:01:16 +0000722 false, NULL);
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000723}
724
Jens Arnold2597a132006-12-25 14:01:47 +0000725static bool cat_add_to_a_new_playlist(void)
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000726{
Jonathan Gordon517aca82008-05-04 13:01:16 +0000727 return catalog_add_to_a_playlist(selected_file, selected_file_attr,
728 true, NULL);
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000729}
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +0000730static int clipboard_callback(int action,const struct menu_item_ex *this_item);
731static bool set_catalogdir(void)
732{
733 catalog_set_directory(selected_file);
734 settings_save();
735 return false;
736}
737MENUITEM_FUNCTION(set_catalogdir_item, 0, ID2P(LANG_SET_AS_PLAYLISTCAT_DIR),
738 set_catalogdir, NULL, clipboard_callback, Icon_Playlist);
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000739
Hardeep Sidhu8c2bcf12007-05-15 23:30:30 +0000740static int cat_playlist_callback(int action,
741 const struct menu_item_ex *this_item);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000742MENUITEM_FUNCTION(cat_add_to_list, 0, ID2P(LANG_CATALOG_ADD_TO),
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000743 cat_add_to_a_playlist, 0, NULL, Icon_Playlist);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000744MENUITEM_FUNCTION(cat_add_to_new, 0, ID2P(LANG_CATALOG_ADD_TO_NEW),
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000745 cat_add_to_a_new_playlist, 0, NULL, Icon_Playlist);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000746MAKE_ONPLAYMENU(cat_playlist_menu, ID2P(LANG_CATALOG),
747 cat_playlist_callback, Icon_Playlist,
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +0000748 &cat_add_to_list, &cat_add_to_new, &set_catalogdir_item);
749
750void onplay_show_playlist_cat_menu(char* track_name)
751{
752 selected_file = track_name;
753 selected_file_attr = FILE_ATTR_AUDIO;
754 do_menu(&cat_playlist_menu, NULL, NULL, false);
755}
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000756
Hardeep Sidhu8c2bcf12007-05-15 23:30:30 +0000757static int cat_playlist_callback(int action,
758 const struct menu_item_ex *this_item)
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000759{
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +0000760 (void)this_item;
Teruaki Kawashima9bea3492010-02-15 13:04:09 +0000761 if (!selected_file ||
762 (((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) &&
763 ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) &&
764 ((selected_file_attr & ATTR_DIRECTORY) == 0)))
Jonathan Gordondada3242008-04-14 10:03:46 +0000765 {
766 return ACTION_EXIT_MENUITEM;
767 }
Teruaki Kawashima993376c2010-02-21 12:01:14 +0000768#ifdef HAVE_TAGCACHE
769 if (context == CONTEXT_ID3DB &&
770 ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO))
771 {
772 return ACTION_EXIT_MENUITEM;
773 }
774#endif
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000775
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000776 switch (action)
Linus Nielsen Feltzingda0525f2006-07-18 13:54:12 +0000777 {
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000778 case ACTION_REQUEST_MENUITEM:
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +0000779 if ((audio_status() & AUDIO_STATUS_PLAY) || context != CONTEXT_WPS)
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000780 {
781 return action;
782 }
783 else
784 return ACTION_EXIT_MENUITEM;
785 break;
Hardeep Sidhu9e426202003-07-01 21:05:43 +0000786 }
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000787 return action;
Björn Stenbergd7a55e12003-03-12 20:21:30 +0000788}
789
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000790#ifdef HAVE_LCD_BITMAP
791static void draw_slider(void)
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000792{
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000793 FOR_NB_SCREENS(i)
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000794 {
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000795 struct viewport vp;
796 int slider_height = 2*screens[i].getcharheight();
797 viewport_set_defaults(&vp, i);
798 screens[i].set_viewport(&vp);
799 show_busy_slider(&screens[i], 1, vp.height - slider_height,
800 vp.width-2, slider_height-1);
801 screens[i].update_viewport();
802 screens[i].set_viewport(NULL);
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000803 }
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000804}
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000805#else
806#define draw_slider()
807#endif
Jonathan Gordon3a7760c2007-04-30 13:41:33 +0000808
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400809static void clear_display(bool update)
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +0000810{
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400811 struct viewport vp;
Zakk Roberts5b93eb62006-03-22 02:18:44 +0000812
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400813 FOR_NB_SCREENS(i)
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +0000814 {
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400815 struct screen * screen = &screens[i];
816 viewport_set_defaults(&vp, screen->screen_type);
817 screen->set_viewport(&vp);
818 screen->clear_viewport();
819 if (update) {
820 screen->update_viewport();
821 }
822 screen->set_viewport(NULL);
823 }
824}
825
826static void splash_path(const char *path)
827{
828 clear_display(false);
829 path_basename(path, &path);
830 splash(0, path);
831 draw_slider();
832}
833
834/* Splashes the path and checks the keys */
835static bool poll_cancel_action(const char *path)
836{
837 splash_path(path);
838 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
839}
840
841static int confirm_overwrite(void)
842{
843 static const char *lines[] = { ID2P(LANG_REALLY_OVERWRITE) };
844 static const struct text_message message = { lines, 1 };
845 return gui_syncyesno_run(&message, NULL, NULL);
846}
847
848static int confirm_delete(const char *file)
849{
850 const char *lines[] = { ID2P(LANG_REALLY_DELETE), file };
851 const char *yes_lines[] = { ID2P(LANG_DELETING), file };
852 const struct text_message message = { lines, 2 };
853 const struct text_message yes_message = { yes_lines, 2 };
854 return gui_syncyesno_run(&message, &yes_message, NULL);
855}
856
857static bool check_new_name(const char *basename)
858{
859 /* at least prevent escapes out of the base directory from keyboard-
860 entered filenames; the file code should reject other invalidities */
861 return *basename != '\0' && !strchr(basename, PATH_SEPCH) &&
862 !is_dotdir_name(basename);
863}
864
865static void splash_cancelled(void)
866{
867 clear_display(true);
868 splash(HZ, ID2P(LANG_CANCEL));
869}
870
871static void splash_failed(int lang_what)
872{
873 cond_talk_ids_fq(lang_what, LANG_FAILED);
874 clear_display(true);
875 splashf(HZ*2, "%s %s", str(lang_what), str(LANG_FAILED));
876}
877
878/* helper function to remove a non-empty directory */
879static int remove_dir(struct dirrecurse_params *parm)
880{
881 DIR *dir = opendir(parm->path);
882 if (!dir) {
883 return -1; /* open error */
884 }
885
886 size_t append = parm->append;
887 int rc = OPRC_SUCCESS;
888
889 /* walk through the directory content */
890 while (rc == OPRC_SUCCESS) {
891 errno = 0; /* distinguish failure from eod */
892 struct dirent *entry = readdir(dir);
893 if (!entry) {
894 if (errno) {
895 rc = -1;
896 }
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +0000897 break;
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400898 }
899
Thomas Martitz6eaab4d2010-09-01 21:29:34 +0000900 struct dirinfo info = dir_get_info(dir, entry);
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400901 if ((info.attribute & ATTR_DIRECTORY) &&
902 is_dotdir_name(entry->d_name)) {
903 continue; /* skip these */
904 }
Teruaki Kawashimaf8840682010-02-14 12:42:32 +0000905
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +0000906 /* append name to current directory */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400907 parm->append = append + path_append(&parm->path[append],
908 PA_SEP_HARD, entry->d_name,
909 sizeof (parm->path) - append);
910 if (parm->append >= sizeof (parm->path)) {
911 rc = -1;
912 break; /* no space left in buffer */
913 }
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +0000914
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400915 if (info.attribute & ATTR_DIRECTORY) {
916 /* remove a subdirectory */
917 rc = remove_dir(parm);
918 } else {
919 /* remove a file */
920 if (poll_cancel_action(parm->path)) {
921 rc = OPRC_CANCELLED;
922 break;
923 }
924
925 rc = remove(parm->path);
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +0000926 }
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400927
928 /* Remove basename we added above */
929 parm->path[append] = '\0';
930 }
931
932 closedir(dir);
933
934 if (rc == 0) {
935 /* remove the now empty directory */
936 if (poll_cancel_action(parm->path)) {
937 rc = OPRC_CANCELLED;
938 } else {
939 rc = rmdir(parm->path);
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +0000940 }
Michael Sevakis7d1a47c2013-08-05 22:02:45 -0400941 }
942
943 return rc;
944}
945
946/* share code for file and directory deletion, saves space */
947static int delete_file_dir(void)
948{
949 if (confirm_delete(selected_file) != YESNO_YES) {
950 return 1;
951 }
952
953 clear_display(true);
954 splash(HZ/2, str(LANG_DELETING));
955
956 int rc = -1;
957
958 if (selected_file_attr & ATTR_DIRECTORY) { /* true if directory */
959 struct dirrecurse_params parm;
960 parm.append = strlcpy(parm.path, selected_file, sizeof (parm.path));
961
962 if (parm.append < sizeof (parm.path)) {
963 cpu_boost(true);
964 rc = remove_dir(&parm);
965 cpu_boost(false);
966 }
967 } else {
968 rc = remove(selected_file);
969 }
970
971 if (rc < OPRC_SUCCESS) {
972 splash_failed(LANG_DELETE);
973 } else if (rc == OPRC_CANCELLED) {
974 splash_cancelled();
975 }
976
977 if (rc != OPRC_NOOP) {
978 /* Could have failed after some but not all needed changes; reload */
979 onplay_result = ONPLAY_RELOAD_DIR;
980 }
981
982 return 1;
983}
984
985static int rename_file(void)
986{
987 int rc = -1;
988 char newname[MAX_PATH];
989 const char *oldbase, *selection = selected_file;
990
991 path_basename(selection, &oldbase);
992 size_t pathlen = oldbase - selection;
993 char *newbase = newname + pathlen;
994
995 if (strlcpy(newname, selection, sizeof (newname)) >= sizeof (newname)) {
996 /* Too long */
997 } else if (kbd_input(newbase, sizeof (newname) - pathlen) < 0) {
998 rc = OPRC_CANCELLED;
999 } else if (!strcmp(oldbase, newbase)) {
1000 rc = OPRC_NOOP; /* No change at all */
1001 } else if (check_new_name(newbase)) {
1002 switch (relate(selection, newname))
Peter D'Hoyee21e87a2006-12-14 22:43:16 +00001003 {
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001004 case RELATE_DIFFERENT:
1005 if (file_exists(newname)) {
1006 break; /* don't overwrite */
1007 }
1008 /* Fall-through */
1009 case RELATE_SAME:
1010 rc = rename(selection, newname);
1011 break;
1012 case RELATE_PREFIX:
1013 default:
Peter D'Hoyee21e87a2006-12-14 22:43:16 +00001014 break;
1015 }
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +00001016 }
Jörg Hohensohnbd1ff3e2004-04-08 06:38:56 +00001017
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001018 if (rc < OPRC_SUCCESS) {
1019 splash_failed(LANG_RENAME);
1020 } else if (rc == OPRC_CANCELLED) {
1021 /* splash_cancelled(); kbd_input() splashes it */
1022 } else if (rc == OPRC_SUCCESS) {
Kevin Ferrare40cc43a2005-11-22 22:19:08 +00001023 onplay_result = ONPLAY_RELOAD_DIR;
Björn Stenbergd7a55e12003-03-12 20:21:30 +00001024 }
1025
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001026 return 1;
Björn Stenbergd7a55e12003-03-12 20:21:30 +00001027}
1028
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001029static int create_dir(void)
Linus Nielsen Feltzing642cce22004-03-11 10:43:53 +00001030{
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001031 int rc = -1;
Linus Nielsen Feltzing642cce22004-03-11 10:43:53 +00001032 char dirname[MAX_PATH];
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001033 size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD,
1034 sizeof (dirname));
1035 char *basename = dirname + pathlen;
Linus Nielsen Feltzing642cce22004-03-11 10:43:53 +00001036
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001037 if (pathlen >= sizeof (dirname)) {
1038 /* Too long */
1039 } else if (kbd_input(basename, sizeof (dirname) - pathlen) < 0) {
1040 rc = OPRC_CANCELLED;
1041 } else if (check_new_name(basename)) {
1042 rc = mkdir(dirname);
1043 }
Zakk Roberts5b93eb62006-03-22 02:18:44 +00001044
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001045 if (rc < OPRC_SUCCESS) {
1046 splash_failed(LANG_CREATE_DIR);
1047 } else if (rc == OPRC_CANCELLED) {
1048 /* splash_cancelled(); kbd_input() splashes it */
1049 } else if (rc == OPRC_SUCCESS) {
Linus Nielsen Feltzing642cce22004-03-11 10:43:53 +00001050 onplay_result = ONPLAY_RELOAD_DIR;
1051 }
1052
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001053 return 1;
Linus Nielsen Feltzing642cce22004-03-11 10:43:53 +00001054}
1055
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001056/* Paste a file */
1057static int clipboard_pastefile(const char *src, const char *target,
1058 unsigned int flags)
Dan Everton1429c9b2006-03-20 11:46:06 +00001059{
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001060 int rc = -1;
Dan Everton1429c9b2006-03-20 11:46:06 +00001061
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001062 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
1063 if ((flags & PASTE_OVERWRITE) || !file_exists(target)) {
1064 /* Rename and possibly overwrite the file */
1065 if (poll_cancel_action(src)) {
1066 rc = OPRC_CANCELLED;
1067 } else {
1068 rc = rename(src, target);
1069 }
1070
1071 #ifdef HAVE_MULTIVOLUME
1072 if (rc < 0 && errno == EXDEV) {
1073 /* Failed because cross volume rename doesn't work; force
1074 a move instead */
1075 flags |= PASTE_EXDEV;
1076 break;
1077 }
1078 #endif /* HAVE_MULTIVOLUME */
1079 }
1080
1081 return rc;
1082 }
1083
1084 /* See if we can get the plugin buffer for the file copy buffer */
1085 size_t buffersize;
1086 char *buffer = (char *) plugin_get_buffer(&buffersize);
1087 if (buffer == NULL || buffersize < 512) {
1088 /* Not large enough, try for a disk sector worth of stack
1089 instead */
1090 buffersize = 512;
1091 buffer = (char *)alloca(buffersize);
1092 }
1093
1094 if (buffer == NULL) {
1095 return -1;
1096 }
1097
1098 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
1099 size */
1100
1101 int src_fd = open(src, O_RDONLY);
1102 if (src_fd >= 0) {
1103 int oflag = O_WRONLY|O_CREAT;
1104
1105 if (!(flags & PASTE_OVERWRITE)) {
1106 oflag |= O_EXCL;
1107 }
1108
1109 int target_fd = open(target, oflag, 0666);
1110 if (target_fd >= 0) {
1111 off_t total_size = 0;
1112 off_t next_cancel_test = 0; /* No excessive button polling */
1113
1114 rc = OPRC_SUCCESS;
1115
1116 while (rc == OPRC_SUCCESS) {
1117 if (total_size >= next_cancel_test) {
1118 next_cancel_test = total_size + 0x10000;
1119 if (poll_cancel_action(src)) {
1120 rc = OPRC_CANCELLED;
1121 break;
1122 }
1123 }
1124
1125 ssize_t bytesread = read(src_fd, buffer, buffersize);
1126 if (bytesread <= 0) {
1127 if (bytesread < 0) {
1128 rc = -1;
1129 }
1130 /* else eof on buffer boundary; nothing to write */
1131 break;
1132 }
1133
1134 ssize_t byteswritten = write(target_fd, buffer, bytesread);
1135 if (byteswritten < bytesread) {
1136 /* Some I/O error */
1137 rc = -1;
1138 break;
1139 }
1140
1141 total_size += byteswritten;
1142
1143 if (bytesread < (ssize_t)buffersize) {
1144 /* EOF with trailing bytes */
1145 break;
1146 }
1147 }
1148
1149 if (rc == OPRC_SUCCESS) {
1150 /* If overwriting, set the correct length if original was
1151 longer */
1152 rc = ftruncate(target_fd, total_size);
1153 }
1154
1155 close(target_fd);
1156
1157 if (rc != OPRC_SUCCESS) {
1158 /* Copy failed. Cleanup. */
1159 remove(target);
1160 }
1161 }
1162
1163 close(src_fd);
1164 }
1165
1166 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
1167 /* Remove the source file */
1168 rc = remove(src);
1169 }
1170
1171 return rc;
1172}
1173
1174/* Paste a directory */
1175static int clipboard_pastedirectory(struct dirrecurse_params *src,
1176 struct dirrecurse_params *target,
1177 unsigned int flags)
1178{
1179 int rc = -1;
1180
1181 while (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
1182 if ((flags & PASTE_OVERWRITE) || !file_exists(target->path)) {
1183 /* Just try to move the directory */
1184 if (poll_cancel_action(src->path)) {
1185 rc = OPRC_CANCELLED;
1186 } else {
1187 rc = rename(src->path, target->path);
1188 }
1189
1190 if (rc < 0) {
1191 int errnum = errno;
1192 if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) {
1193 /* Directory is not empty thus rename() will not do a quick
1194 overwrite */
1195 break;
1196 }
1197 #ifdef HAVE_MULTIVOLUME
1198 else if (errnum == EXDEV) {
1199 /* Failed because cross volume rename doesn't work; force
1200 a move instead */
1201 flags |= PASTE_EXDEV;
1202 break;
Solomon Peachyfe951272018-12-14 08:20:25 -05001203 }
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001204 #endif /* HAVE_MULTIVOLUME */
1205 }
1206 }
1207
1208 return rc;
1209 }
1210
1211 DIR *srcdir = opendir(src->path);
1212
1213 if (srcdir) {
1214 /* Make a directory to copy things to */
1215 rc = mkdir(target->path);
1216 if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) {
1217 /* Exists and overwrite was approved */
1218 rc = OPRC_SUCCESS;
1219 }
1220 }
1221
1222 size_t srcap = src->append, targetap = target->append;
1223
1224 /* Walk through the directory content; this loop will exit as soon as
1225 there's a problem */
1226 while (rc == OPRC_SUCCESS) {
1227 errno = 0; /* Distinguish failure from eod */
1228 struct dirent *entry = readdir(srcdir);
1229 if (!entry) {
1230 if (errno) {
1231 rc = -1;
1232 }
1233 break;
1234 }
1235
1236 struct dirinfo info = dir_get_info(srcdir, entry);
1237 if ((info.attribute & ATTR_DIRECTORY) &&
1238 is_dotdir_name(entry->d_name)) {
1239 continue; /* Skip these */
1240 }
1241
1242 /* Append names to current directories */
1243 src->append = srcap +
1244 path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name,
1245 sizeof(src->path) - srcap);
1246
1247 target->append = targetap +
1248 path_append(&target->path[targetap], PA_SEP_HARD, entry->d_name,
1249 sizeof (target->path) - targetap);
1250
1251 if (src->append >= sizeof (src->path) ||
1252 target->append >= sizeof (target->path)) {
1253 rc = -1; /* No space left in buffer */
1254 break;
1255 }
1256
1257 if (poll_cancel_action(src->path)) {
1258 rc = OPRC_CANCELLED;
1259 break;
1260 }
Solomon Peachyfe951272018-12-14 08:20:25 -05001261
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001262 DEBUGF("Copy %s to %s\n", src->path, target->path);
1263
1264 if (info.attribute & ATTR_DIRECTORY) {
1265 /* Copy/move a subdirectory */
1266 rc = clipboard_pastedirectory(src, target, flags); /* recursion */
1267 } else {
1268 /* Copy/move a file */
1269 rc = clipboard_pastefile(src->path, target->path, flags);
1270 }
1271
1272 /* Remove basenames we added above */
1273 src->path[srcap] = target->path[targetap] = '\0';
1274 }
1275
1276 if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) {
1277 /* Remove the now empty directory */
1278 rc = rmdir(src->path);
1279 }
1280
1281 closedir(srcdir);
1282 return rc;
Dan Everton1429c9b2006-03-20 11:46:06 +00001283}
1284
1285static bool clipboard_cut(void)
1286{
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001287 return clipboard_clip(&clipboard, selected_file, selected_file_attr,
1288 PASTE_CUT);
Dan Everton1429c9b2006-03-20 11:46:06 +00001289}
1290
1291static bool clipboard_copy(void)
1292{
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001293 return clipboard_clip(&clipboard, selected_file, selected_file_attr,
1294 PASTE_COPY);
Dan Everton1429c9b2006-03-20 11:46:06 +00001295}
1296
1297/* Paste the clipboard to the current directory */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001298static int clipboard_paste(void)
Dan Everton1429c9b2006-03-20 11:46:06 +00001299{
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001300 if (!clipboard.path[0])
1301 return 1;
Dan Everton1429c9b2006-03-20 11:46:06 +00001302
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001303 int rc = -1;
Dan Everton1429c9b2006-03-20 11:46:06 +00001304
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001305 struct dirrecurse_params src, target;
1306 unsigned int flags = clipboard.flags;
Dan Everton1429c9b2006-03-20 11:46:06 +00001307
1308 /* Figure out the name of the selection */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001309 const char *nameptr;
1310 path_basename(clipboard.path, &nameptr);
Dan Everton1429c9b2006-03-20 11:46:06 +00001311
Peter D'Hoye6be61562007-04-12 22:38:54 +00001312 /* Final target is current directory plus name of selection */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001313 target.append = path_append(target.path, getcwd(NULL, 0),
1314 nameptr, sizeof (target.path));
Dan Everton1429c9b2006-03-20 11:46:06 +00001315
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001316 switch (target.append < sizeof (target.path) ?
1317 relate(clipboard.path, target.path) : -1)
Dan Everton4c486ce2007-05-20 10:04:39 +00001318 {
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001319 case RELATE_SAME:
1320 rc = OPRC_NOOP;
1321 break;
Dan Everton4c486ce2007-05-20 10:04:39 +00001322
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001323 case RELATE_DIFFERENT:
1324 if (file_exists(target.path)) {
1325 /* If user chooses not to overwrite, cancel */
1326 if (confirm_overwrite() == YESNO_NO) {
1327 rc = OPRC_NOOVERWRT;
1328 break;
Nils Wallméniusd7fb4302008-12-01 11:31:22 +00001329 }
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001330
1331 flags |= PASTE_OVERWRITE;
Jonathan Gordon96016e42007-02-27 10:06:55 +00001332 }
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001333
1334 clear_display(true);
1335 splash(HZ/2, (flags & PASTE_COPY) ? ID2P(LANG_COPYING) :
1336 ID2P(LANG_MOVING));
1337
1338 /* Now figure out what we're doing */
1339 cpu_boost(true);
1340
1341 if (clipboard.attr & ATTR_DIRECTORY) {
1342 /* Copy or move a subdirectory */
1343 src.append = strlcpy(src.path, clipboard.path,
1344 sizeof (src.path));
1345 if (src.append < sizeof (src.path)) {
1346 rc = clipboard_pastedirectory(&src, &target, flags);
1347 }
1348 } else {
1349 /* Copy or move a file */
1350 rc = clipboard_pastefile(clipboard.path, target.path, flags);
1351 }
1352
1353 cpu_boost(false);
1354 break;
1355
1356 case RELATE_PREFIX:
1357 default: /* Some other relation / failure */
1358 break;
Dan Everton1429c9b2006-03-20 11:46:06 +00001359 }
1360
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001361 clear_display(true);
Dan Everton1429c9b2006-03-20 11:46:06 +00001362
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001363 switch (rc)
1364 {
1365 case OPRC_CANCELLED:
1366 splash_cancelled();
1367 case OPRC_SUCCESS:
Dan Everton1429c9b2006-03-20 11:46:06 +00001368 onplay_result = ONPLAY_RELOAD_DIR;
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001369 case OPRC_NOOP:
1370 clipboard_clear_selection(&clipboard);
1371 case OPRC_NOOVERWRT:
1372 break;
1373 default:
1374 if (rc < OPRC_SUCCESS) {
1375 splash_failed(LANG_PASTE);
1376 onplay_result = ONPLAY_RELOAD_DIR;
1377 }
Dan Everton1429c9b2006-03-20 11:46:06 +00001378 }
1379
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001380 return 1;
Dan Everton1429c9b2006-03-20 11:46:06 +00001381}
1382
Robert Kukla40595bd2007-03-26 23:10:40 +00001383#ifdef HAVE_TAGCACHE
Jonathan Gordon49ca6672007-11-08 14:00:36 +00001384static int set_rating_inline(void)
Robert Kukla226cb7b2007-03-26 15:08:59 +00001385{
1386 struct mp3entry* id3 = audio_current_track();
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001387 if (id3 && id3->tagcache_idx && global_settings.runtimedb)
Miika Pekkarinen9d756e22007-07-21 17:35:19 +00001388 {
Jonathan Gordon49ca6672007-11-08 14:00:36 +00001389 set_int_ex(str(LANG_MENU_SET_RATING), "", UNIT_INT, (void*)(&id3->rating),
1390 NULL, 1, 0, 10, NULL, NULL);
Robert Kuklaad5610d2008-02-12 18:51:10 +00001391 tagcache_update_numeric(id3->tagcache_idx-1, tag_rating, id3->rating);
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001392 }
Jonathan Gordon49ca6672007-11-08 14:00:36 +00001393 else
Nils Wallménius01729e72008-08-15 08:27:39 +00001394 splash(HZ*2, ID2P(LANG_ID3_NO_INFO));
Jonathan Gordon49ca6672007-11-08 14:00:36 +00001395 return 0;
Robert Kukla226cb7b2007-03-26 15:08:59 +00001396}
Robert Kukla987faea2007-11-08 16:31:44 +00001397static int ratingitem_callback(int action,const struct menu_item_ex *this_item)
1398{
1399 (void)this_item;
1400 switch (action)
1401 {
1402 case ACTION_REQUEST_MENUITEM:
Jonathan Gordonff2e9bd2008-12-21 09:58:11 +00001403 if (!selected_file || !global_settings.runtimedb ||
1404 !tagcache_is_usable())
Robert Kukla987faea2007-11-08 16:31:44 +00001405 return ACTION_EXIT_MENUITEM;
1406 break;
1407 }
1408 return action;
1409}
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001410MENUITEM_FUNCTION(rating_item, 0, ID2P(LANG_MENU_SET_RATING),
1411 set_rating_inline, NULL,
Robert Kukla987faea2007-11-08 16:31:44 +00001412 ratingitem_callback, Icon_Questionmark);
Solomon Peachyfe951272018-12-14 08:20:25 -05001413#endif
1414#ifdef HAVE_PICTUREFLOW_INTEGRATION
1415MENUITEM_RETURNVALUE(pictureflow_item, ID2P(LANG_ONPLAY_PICTUREFLOW),
1416 GO_TO_PICTUREFLOW, NULL, Icon_NOICON);
Robert Kukla40595bd2007-03-26 23:10:40 +00001417#endif
Robert Kukla226cb7b2007-03-26 15:08:59 +00001418
Nicolas Pennequin6190a0d2007-05-14 17:34:52 +00001419static bool view_cue(void)
1420{
1421 struct mp3entry* id3 = audio_current_track();
Solomon Peachyfe951272018-12-14 08:20:25 -05001422 if (id3 && id3->cuesheet)
Nicolas Pennequin6190a0d2007-05-14 17:34:52 +00001423 {
Jonathan Gordon24b136f2009-07-20 05:18:18 +00001424 browse_cuesheet(id3->cuesheet);
Nicolas Pennequin6190a0d2007-05-14 17:34:52 +00001425 }
1426 return false;
1427}
Hardeep Sidhu8c2bcf12007-05-15 23:30:30 +00001428static int view_cue_item_callback(int action,
1429 const struct menu_item_ex *this_item)
Nicolas Pennequin6190a0d2007-05-14 17:34:52 +00001430{
1431 (void)this_item;
1432 struct mp3entry* id3 = audio_current_track();
1433 switch (action)
1434 {
1435 case ACTION_REQUEST_MENUITEM:
Jonathan Gordon24b136f2009-07-20 05:18:18 +00001436 if (!selected_file
1437 || !id3 || !id3->cuesheet)
Nicolas Pennequin6190a0d2007-05-14 17:34:52 +00001438 return ACTION_EXIT_MENUITEM;
1439 break;
1440 }
1441 return action;
1442}
1443MENUITEM_FUNCTION(view_cue_item, 0, ID2P(LANG_BROWSE_CUESHEET),
1444 view_cue, NULL, view_cue_item_callback, Icon_NOICON);
1445
Thomas Martitzb779fcc2011-10-07 22:59:06 +00001446
1447static int browse_id3_wrapper(void)
1448{
1449 if (browse_id3())
1450 return GO_TO_ROOT;
1451 return GO_TO_PREVIOUS;
1452}
1453
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001454/* CONTEXT_WPS items */
Thomas Martitzb779fcc2011-10-07 22:59:06 +00001455MENUITEM_FUNCTION(browse_id3_item, MENU_FUNC_CHECK_RETVAL, ID2P(LANG_MENU_SHOW_ID3_INFO),
1456 browse_id3_wrapper, NULL, NULL, Icon_NOICON);
Nils Wallméniusd29a11b2012-05-08 16:34:26 +02001457#ifdef HAVE_PITCHCONTROL
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001458MENUITEM_FUNCTION(pitch_screen_item, 0, ID2P(LANG_PITCH),
Jonathan Gordon1da2f012008-10-05 13:01:54 +00001459 gui_syncpitchscreen_run, NULL, NULL, Icon_Audio);
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001460#endif
Stéphane Doyonab0f7e12008-05-04 13:47:58 +00001461
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001462/* CONTEXT_[TREE|ID3DB] items */
1463static int clipboard_callback(int action,const struct menu_item_ex *this_item);
1464MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME),
1465 rename_file, NULL, clipboard_callback, Icon_NOICON);
1466MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT),
1467 clipboard_cut, NULL, clipboard_callback, Icon_NOICON);
1468MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY),
1469 clipboard_copy, NULL, clipboard_callback, Icon_NOICON);
1470MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE),
1471 clipboard_paste, NULL, clipboard_callback, Icon_NOICON);
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001472MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE),
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001473 delete_file_dir, NULL, clipboard_callback, Icon_NOICON);
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001474MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR),
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001475 delete_file_dir, NULL, clipboard_callback, Icon_NOICON);
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001476MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR),
1477 create_dir, NULL, clipboard_callback, Icon_NOICON);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001478
1479/* other items */
1480static bool list_viewers(void)
1481{
1482 int ret = filetype_list_viewers(selected_file);
1483 if (ret == PLUGIN_USB_CONNECTED)
1484 onplay_result = ONPLAY_RELOAD_DIR;
1485 return false;
1486}
1487
1488static bool onplay_load_plugin(void *param)
1489{
1490 int ret = filetype_load_plugin((const char*)param, selected_file);
1491 if (ret == PLUGIN_USB_CONNECTED)
1492 onplay_result = ONPLAY_RELOAD_DIR;
1493 return false;
1494}
1495
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001496MENUITEM_FUNCTION(list_viewers_item, 0, ID2P(LANG_ONPLAY_OPEN_WITH),
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001497 list_viewers, NULL, clipboard_callback, Icon_NOICON);
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001498MENUITEM_FUNCTION(properties_item, MENU_FUNC_USEPARAM, ID2P(LANG_PROPERTIES),
1499 onplay_load_plugin, (void *)"properties",
1500 clipboard_callback, Icon_NOICON);
Jonathan Gordon101693f2011-11-15 13:22:02 +00001501static bool onplay_add_to_shortcuts(void)
1502{
1503 shortcuts_add(SHORTCUT_BROWSER, selected_file);
1504 return false;
1505}
1506MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES),
1507 onplay_add_to_shortcuts, NULL,
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001508 clipboard_callback, Icon_NOICON);
1509
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001510#if LCD_DEPTH > 1
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001511static bool set_backdrop(void)
1512{
Frank Gevaertsd09f9782011-04-10 13:25:47 +00001513 strlcpy(global_settings.backdrop_file, selected_file,
1514 sizeof(global_settings.backdrop_file));
1515 settings_save();
Jonathan Gordon9928e342010-09-14 11:56:50 +00001516 skin_backdrop_load_setting();
Thomas Martitze1261532011-10-28 17:09:38 +00001517 skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN));
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001518 return true;
1519}
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001520MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP),
1521 set_backdrop, NULL, clipboard_callback, Icon_NOICON);
1522#endif
Jonathan Gordon415e9d72007-07-10 07:41:37 +00001523#ifdef HAVE_RECORDING
1524static bool set_recdir(void)
1525{
Teruaki Kawashima21426282010-11-15 12:33:47 +00001526 strlcpy(global_settings.rec_directory, selected_file,
1527 sizeof(global_settings.rec_directory));
Peter D'Hoye80ad4592008-02-10 18:28:59 +00001528 settings_save();
Jonathan Gordon415e9d72007-07-10 07:41:37 +00001529 return false;
1530}
1531MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_SET_AS_REC_DIR),
1532 set_recdir, NULL, clipboard_callback, Icon_Recording);
1533#endif
Jonathan Gordonefbcece2010-10-04 10:34:38 +00001534static bool set_startdir(void)
1535{
Solomon Peachyfe951272018-12-14 08:20:25 -05001536 snprintf(global_settings.start_directory,
Jonathan Gordonefbcece2010-10-04 10:34:38 +00001537 sizeof(global_settings.start_directory),
1538 "%s/", selected_file);
1539 settings_save();
1540 return false;
1541}
1542MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_SET_AS_START_DIR),
1543 set_startdir, NULL, clipboard_callback, Icon_file_view_menu);
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001544
1545static int clipboard_callback(int action,const struct menu_item_ex *this_item)
1546{
1547 switch (action)
1548 {
1549 case ACTION_REQUEST_MENUITEM:
Jonathan Gordone8dbe2e2008-04-21 14:52:42 +00001550#ifdef HAVE_MULTIVOLUME
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001551 /* no rename+delete for volumes */
1552 if ((selected_file_attr & ATTR_VOLUME) &&
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001553 (this_item == &rename_file_item ||
1554 this_item == &delete_dir_item ||
1555 this_item == &clipboard_cut_item ||
1556 this_item == &list_viewers_item))
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001557 return ACTION_EXIT_MENUITEM;
Jonathan Gordone8dbe2e2008-04-21 14:52:42 +00001558#endif
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001559#ifdef HAVE_TAGCACHE
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001560 if (context == CONTEXT_ID3DB)
Teruaki Kawashima993376c2010-02-21 12:01:14 +00001561 {
1562 if (((selected_file_attr & FILE_ATTR_MASK) ==
1563 FILE_ATTR_AUDIO) &&
1564 this_item == &properties_item)
1565 return action;
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001566 return ACTION_EXIT_MENUITEM;
Teruaki Kawashima993376c2010-02-21 12:01:14 +00001567 }
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001568#endif
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001569 if (this_item == &clipboard_paste_item)
1570 { /* visible if there is something to paste */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001571 return (clipboard.path[0] != 0) ?
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001572 action : ACTION_EXIT_MENUITEM;
1573 }
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001574 else if (this_item == &create_dir_item)
Robert Kuklaf617ba42008-04-09 13:50:33 +00001575 {
1576 /* always visible */
1577 return action;
1578 }
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001579 else if (selected_file)
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001580 {
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001581 /* requires an actual file */
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001582 if (this_item == &rename_file_item ||
1583 this_item == &clipboard_cut_item ||
1584 this_item == &clipboard_copy_item ||
1585 this_item == &properties_item ||
1586 this_item == &add_to_faves_item)
1587 {
1588 return action;
1589 }
1590 else if ((selected_file_attr & ATTR_DIRECTORY))
1591 {
1592 /* only for directories */
Jonathan Gordonefbcece2010-10-04 10:34:38 +00001593 if (this_item == &delete_dir_item ||
Jonathan Gordon97a4c1e2011-07-20 14:11:15 +00001594 this_item == &set_startdir_item ||
1595 this_item == &set_catalogdir_item
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001596#ifdef HAVE_RECORDING
1597 || this_item == &set_recdir_item
1598#endif
1599 )
1600 return action;
1601 }
1602 else if (this_item == &delete_file_item ||
1603 this_item == &list_viewers_item)
1604 {
1605 /* only for files */
1606 return action;
1607 }
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001608#if LCD_DEPTH > 1
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001609 else if (this_item == &set_backdrop_item)
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001610 {
1611 char *suffix = strrchr(selected_file, '.');
1612 if (suffix)
1613 {
1614 if (strcasecmp(suffix, ".bmp") == 0)
1615 {
1616 return action;
1617 }
1618 }
1619 }
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001620#endif
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001621 }
1622 return ACTION_EXIT_MENUITEM;
1623 break;
1624 }
1625 return action;
1626}
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001627
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001628static int onplaymenu_callback(int action,const struct menu_item_ex *this_item);
1629/* used when onplay() is called in the CONTEXT_WPS context */
1630MAKE_ONPLAYMENU( wps_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE),
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001631 onplaymenu_callback, Icon_Audio,
Nils Wallméniusb2ee5bc2008-05-22 16:28:20 +00001632 &wps_playlist_menu, &cat_playlist_menu,
1633 &sound_settings, &playback_settings,
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001634#ifdef HAVE_TAGCACHE
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001635 &rating_item,
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001636#endif
Solomon Peachyfe951272018-12-14 08:20:25 -05001637 &bookmark_menu,
Jonathan Gordonedf06dc2010-06-09 09:00:42 +00001638#ifdef HAVE_PICTUREFLOW_INTEGRATION
Jonathan Gordond871ff82010-06-09 04:25:41 +00001639 &pictureflow_item,
Solomon Peachyfe951272018-12-14 08:20:25 -05001640#endif
Jonathan Gordond871ff82010-06-09 04:25:41 +00001641 &browse_id3_item, &list_viewers_item,
Nils Wallméniusb2ee5bc2008-05-22 16:28:20 +00001642 &delete_file_item, &view_cue_item,
Nils Wallméniusd29a11b2012-05-08 16:34:26 +02001643#ifdef HAVE_PITCHCONTROL
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001644 &pitch_screen_item,
1645#endif
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001646 );
1647/* used when onplay() is not called in the CONTEXT_WPS context */
Teruaki Kawashimaf8840682010-02-14 12:42:32 +00001648MAKE_ONPLAYMENU( tree_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE),
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001649 onplaymenu_callback, Icon_file_view_menu,
1650 &tree_playlist_menu, &cat_playlist_menu,
1651 &rename_file_item, &clipboard_cut_item, &clipboard_copy_item,
1652 &clipboard_paste_item, &delete_file_item, &delete_dir_item,
1653#if LCD_DEPTH > 1
1654 &set_backdrop_item,
1655#endif
Jonathan Gordon415e9d72007-07-10 07:41:37 +00001656 &list_viewers_item, &create_dir_item, &properties_item,
1657#ifdef HAVE_RECORDING
1658 &set_recdir_item,
1659#endif
William Wilgus0c06e5f2017-09-07 12:27:54 +02001660 &set_startdir_item, &add_to_faves_item, &file_menu,
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001661 );
Jonathan Gordona6f2b822007-11-04 12:40:18 +00001662static int onplaymenu_callback(int action,const struct menu_item_ex *this_item)
1663{
Jonathan Gordona6f2b822007-11-04 12:40:18 +00001664 switch (action)
1665 {
1666 case ACTION_TREE_STOP:
1667 if (this_item == &wps_onplay_menu)
1668 {
1669 list_stop_handler();
1670 return ACTION_STD_CANCEL;
1671 }
1672 break;
1673 case ACTION_EXIT_MENUITEM:
1674 return ACTION_EXIT_AFTER_THIS_MENUITEM;
1675 break;
1676 }
1677 return action;
1678}
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001679
1680#ifdef HAVE_HOTKEY
1681/* direct function calls, no need for menu callbacks */
1682static bool delete_item(void)
1683{
1684#ifdef HAVE_MULTIVOLUME
1685 /* no delete for volumes */
Michael Sevakis7d1a47c2013-08-05 22:02:45 -04001686 if (selected_file_attr & ATTR_VOLUME)
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001687 return false;
1688#endif
1689 return delete_file_dir();
1690}
1691
1692static bool open_with(void)
1693{
1694 /* only open files */
1695 if (selected_file_attr & ATTR_DIRECTORY)
1696 return false;
1697#ifdef HAVE_MULTIVOLUME
1698 if (selected_file_attr & ATTR_VOLUME)
1699 return false;
1700#endif
1701 return list_viewers();
1702}
1703
Jeffrey Goodee141f802010-05-11 13:40:25 +00001704static int playlist_insert_shuffled(void)
1705{
1706 if ((audio_status() & AUDIO_STATUS_PLAY) ||
1707 (selected_file_attr & ATTR_DIRECTORY) ||
1708 ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U))
1709 {
1710 playlist_insert_func((intptr_t*)PLAYLIST_INSERT_SHUFFLED);
1711 return ONPLAY_START_PLAY;
1712 }
Solomon Peachyfe951272018-12-14 08:20:25 -05001713
Jeffrey Goodee141f802010-05-11 13:40:25 +00001714 return ONPLAY_RELOAD_DIR;
1715}
1716
Alexander Levin31959e72010-04-10 19:43:47 +00001717struct hotkey_assignment {
Jeffrey Goode156272f2010-05-11 04:41:15 +00001718 int action; /* hotkey_action */
1719 int lang_id; /* Language ID */
Alexander Levin47794562010-04-10 20:31:01 +00001720 struct menu_func func; /* Function to run if this entry is selected */
1721 int return_code; /* What to return after the function is run */
Alexander Levin31959e72010-04-10 19:43:47 +00001722};
1723
1724#define HOTKEY_FUNC(func, param) {{(void *)func}, param}
1725
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001726/* Any desired hotkey functions go here, in the enum in onplay.h,
Solomon Peachyfe951272018-12-14 08:20:25 -05001727 and in the settings menu in settings_list.c. The order here
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001728 is not important. */
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001729static struct hotkey_assignment hotkey_items[] = {
Jeffrey Goode156272f2010-05-11 04:41:15 +00001730 { HOTKEY_VIEW_PLAYLIST, LANG_VIEW_DYNAMIC_PLAYLIST,
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001731 HOTKEY_FUNC(NULL, NULL),
Jeffrey Goode156272f2010-05-11 04:41:15 +00001732 ONPLAY_PLAYLIST },
1733 { HOTKEY_SHOW_TRACK_INFO, LANG_MENU_SHOW_ID3_INFO,
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001734 HOTKEY_FUNC(browse_id3, NULL),
Jeffrey Goode156272f2010-05-11 04:41:15 +00001735 ONPLAY_RELOAD_DIR },
Nils Wallméniusd29a11b2012-05-08 16:34:26 +02001736#ifdef HAVE_PITCHCONTROL
Jeffrey Goode156272f2010-05-11 04:41:15 +00001737 { HOTKEY_PITCHSCREEN, LANG_PITCH,
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001738 HOTKEY_FUNC(gui_syncpitchscreen_run, NULL),
Jeffrey Goode156272f2010-05-11 04:41:15 +00001739 ONPLAY_RELOAD_DIR },
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001740#endif
Jeffrey Goode156272f2010-05-11 04:41:15 +00001741 { HOTKEY_OPEN_WITH, LANG_ONPLAY_OPEN_WITH,
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001742 HOTKEY_FUNC(open_with, NULL),
Jeffrey Goode156272f2010-05-11 04:41:15 +00001743 ONPLAY_RELOAD_DIR },
1744 { HOTKEY_DELETE, LANG_DELETE,
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001745 HOTKEY_FUNC(delete_item, NULL),
Jeffrey Goode156272f2010-05-11 04:41:15 +00001746 ONPLAY_RELOAD_DIR },
1747 { HOTKEY_INSERT, LANG_INSERT,
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001748 HOTKEY_FUNC(playlist_insert_func, (intptr_t*)PLAYLIST_INSERT),
Alexander Levin482b45b2010-12-17 09:54:18 +00001749 ONPLAY_RELOAD_DIR },
Jeffrey Goodee141f802010-05-11 13:40:25 +00001750 { HOTKEY_INSERT_SHUFFLED, LANG_INSERT_SHUFFLED,
1751 HOTKEY_FUNC(playlist_insert_shuffled, NULL),
Alexander Levin482b45b2010-12-17 09:54:18 +00001752 ONPLAY_RELOAD_DIR },
Jonathan Gordon10e24d22010-06-09 08:51:29 +00001753#ifdef HAVE_PICTUREFLOW_INTEGRATION
Jonathan Gordond871ff82010-06-09 04:25:41 +00001754 { HOTKEY_PICTUREFLOW, LANG_ONPLAY_PICTUREFLOW,
1755 HOTKEY_FUNC(NULL, NULL),
1756 ONPLAY_PICTUREFLOW },
1757#endif
Solomon Peachy5e8db162019-01-01 19:37:13 -05001758 { HOTKEY_BOOKMARK, LANG_BOOKMARK_MENU_CREATE,
1759 HOTKEY_FUNC(bookmark_create_menu, NULL),
1760 ONPLAY_OK },
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001761};
1762
Jeffrey Goode156272f2010-05-11 04:41:15 +00001763/* Return the language ID for this action */
1764int get_hotkey_lang_id(int action)
Jeffrey Goodeaccc0462010-04-08 01:43:50 +00001765{
Alexander Levinc79bc802010-05-11 17:10:07 +00001766 int i = ARRAYLEN(hotkey_items);
Jeffrey Goode156272f2010-05-11 04:41:15 +00001767 while (i--)
Jeffrey Goodeaccc0462010-04-08 01:43:50 +00001768 {
Jeffrey Goode156272f2010-05-11 04:41:15 +00001769 if (hotkey_items[i].action == action)
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001770 return hotkey_items[i].lang_id;
Jeffrey Goodeaccc0462010-04-08 01:43:50 +00001771 }
Solomon Peachyfe951272018-12-14 08:20:25 -05001772
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001773 return LANG_OFF;
Jeffrey Goodeaccc0462010-04-08 01:43:50 +00001774}
1775
Jeffrey Goode156272f2010-05-11 04:41:15 +00001776/* Execute the hotkey function, if listed */
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001777static int execute_hotkey(bool is_wps)
1778{
Alexander Levinc79bc802010-05-11 17:10:07 +00001779 int i = ARRAYLEN(hotkey_items);
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001780 struct hotkey_assignment *this_item;
Jeffrey Goode156272f2010-05-11 04:41:15 +00001781 const int action = (is_wps ? global_settings.hotkey_wps :
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001782 global_settings.hotkey_tree);
Solomon Peachyfe951272018-12-14 08:20:25 -05001783
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001784 /* search assignment struct for a match for the hotkey setting */
Jeffrey Goode156272f2010-05-11 04:41:15 +00001785 while (i--)
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001786 {
1787 this_item = &hotkey_items[i];
Jeffrey Goode156272f2010-05-11 04:41:15 +00001788 if (this_item->action == action)
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001789 {
1790 /* run the associated function (with optional param), if any */
1791 const struct menu_func func = this_item->func;
Jeffrey Goodeaaa07972010-05-11 13:50:39 +00001792 int func_return = ONPLAY_RELOAD_DIR;
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001793 if (func.function != NULL)
1794 {
1795 if (func.param != NULL)
Jeffrey Goodee141f802010-05-11 13:40:25 +00001796 func_return = (*func.function_w_param)(func.param);
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001797 else
Jeffrey Goodee141f802010-05-11 13:40:25 +00001798 func_return = (*func.function)();
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001799 }
1800 /* return with the associated code */
Jeffrey Goodee141f802010-05-11 13:40:25 +00001801 const int return_code = this_item->return_code;
1802 /* ONPLAY_OK here means to use the function return code */
1803 if (return_code == ONPLAY_OK)
1804 return func_return;
1805 return return_code;
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001806 }
1807 }
Solomon Peachyfe951272018-12-14 08:20:25 -05001808
Jeffrey Goode1ad76ff2010-05-09 02:02:51 +00001809 /* no valid hotkey set, ignore hotkey */
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001810 return ONPLAY_RELOAD_DIR;
1811}
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001812#endif /* HOTKEY */
1813
1814int onplay(char* file, int attr, int from, bool hotkey)
Björn Stenbergd7a55e12003-03-12 20:21:30 +00001815{
Jonathan Gordon49ca6672007-11-08 14:00:36 +00001816 const struct menu_item_ex *menu;
Hardeep Sidhu9e426202003-07-01 21:05:43 +00001817 onplay_result = ONPLAY_OK;
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001818 context = from;
Linus Nielsen Feltzing6e0436f2005-06-23 01:31:26 +00001819 selected_file = file;
1820 selected_file_attr = attr;
Jeffrey Goode7be582b2010-04-08 14:34:14 +00001821 int menu_selection;
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001822#ifdef HAVE_HOTKEY
1823 if (hotkey)
1824 return execute_hotkey(context == CONTEXT_WPS);
Jeffrey Gooded5e6bc72010-04-01 03:14:44 +00001825#else
1826 (void)hotkey;
1827#endif
Jonathan Gordone918b7b2011-08-05 00:47:11 +00001828
1829 push_current_activity(ACTIVITY_CONTEXTMENU);
Linus Nielsen Feltzing22c1a8e2005-06-24 22:33:42 +00001830 if (context == CONTEXT_WPS)
Jonathan Gordon49ca6672007-11-08 14:00:36 +00001831 menu = &wps_onplay_menu;
Dan Everton1429c9b2006-03-20 11:46:06 +00001832 else
Jonathan Gordon49ca6672007-11-08 14:00:36 +00001833 menu = &tree_onplay_menu;
Jeffrey Goode7be582b2010-04-08 14:34:14 +00001834 menu_selection = do_menu(menu, NULL, NULL, false);
Jonathan Gordone918b7b2011-08-05 00:47:11 +00001835 pop_current_activity();
1836
Jeffrey Goode7be582b2010-04-08 14:34:14 +00001837 switch (menu_selection)
1838 {
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001839 case GO_TO_WPS:
1840 return ONPLAY_START_PLAY;
1841 case GO_TO_ROOT:
1842 case GO_TO_MAINMENU:
1843 return ONPLAY_MAINMENU;
Thomas Martitzc19e5362010-02-20 19:06:39 +00001844 case GO_TO_PLAYLIST_VIEWER:
1845 return ONPLAY_PLAYLIST;
Jonathan Gordon10e24d22010-06-09 08:51:29 +00001846#ifdef HAVE_PICTUREFLOW_INTEGRATION
Jonathan Gordond871ff82010-06-09 04:25:41 +00001847 case GO_TO_PICTUREFLOW:
1848 return ONPLAY_PICTUREFLOW;
1849#endif
Jonathan Gordon3a7760c2007-04-30 13:41:33 +00001850 default:
Teruaki Kawashima9bea3492010-02-15 13:04:09 +00001851 return onplay_result;
Dan Everton1429c9b2006-03-20 11:46:06 +00001852 }
Björn Stenbergd7a55e12003-03-12 20:21:30 +00001853}