diff options
author | William Wilgus <wilgus.william@gmail.com> | 2024-06-21 10:38:50 -0400 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2024-06-30 02:09:40 -0400 |
commit | dc7486c7de3018b78fcfeafe7a1cc5c9da168494 (patch) | |
tree | ca5326e18d88f650e8132c2b9760199701448df2 /apps | |
parent | c87c09658a7d65249affe3e8a814bd278998bb42 (diff) | |
download | rockbox-dc7486c7de3018b78fcfeafe7a1cc5c9da168494.tar.gz rockbox-dc7486c7de3018b78fcfeafe7a1cc5c9da168494.zip |
[Feature] onplay.c show file progress
first some clean-up of onplay.c
extend/move fileobject copy, move, delete routines in prep for other users
add error checking, better error codes
pre scan to make sure the operation doesn't exceed system resources
show progress for file and directory copies
Change-Id: Ife2e62df554892dab651bab40433bf70b27e73ff
Diffstat (limited to 'apps')
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/fileop.c | 606 | ||||
-rw-r--r-- | apps/fileop.h | 70 | ||||
-rw-r--r-- | apps/misc.c | 18 | ||||
-rw-r--r-- | apps/misc.h | 4 | ||||
-rw-r--r-- | apps/onplay.c | 784 |
6 files changed, 844 insertions, 639 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index 444951bbcb..2c002b51cd 100644 --- a/apps/SOURCES +++ b/apps/SOURCES | |||
@@ -10,6 +10,7 @@ bookmark.c | |||
10 | core_keymap.c | 10 | core_keymap.c |
11 | debug_menu.c | 11 | debug_menu.c |
12 | filetypes.c | 12 | filetypes.c |
13 | fileop.c | ||
13 | language.c | 14 | language.c |
14 | main.c | 15 | main.c |
15 | menu.c | 16 | menu.c |
diff --git a/apps/fileop.c b/apps/fileop.c new file mode 100644 index 0000000000..0d2dc774b9 --- /dev/null +++ b/apps/fileop.c | |||
@@ -0,0 +1,606 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
11 | * Copyright (C) 2024 William Wilgus | ||
12 | * | ||
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. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include <stdbool.h> | ||
23 | #include <errno.h> | ||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include "string-extra.h" | ||
28 | #include "debug.h" | ||
29 | |||
30 | #include "misc.h" | ||
31 | #include "plugin.h" | ||
32 | #include "dir.h" | ||
33 | #include "tree.h" | ||
34 | #include "fileop.h" | ||
35 | #include "pathfuncs.h" | ||
36 | |||
37 | #include "settings.h" | ||
38 | #include "lang.h" | ||
39 | #include "yesno.h" | ||
40 | #include "splash.h" | ||
41 | #include "keyboard.h" | ||
42 | |||
43 | /* Used for directory move, copy and delete */ | ||
44 | struct file_op_params | ||
45 | { | ||
46 | char path[MAX_PATH]; /* Buffer for full path */ | ||
47 | bool is_dir; | ||
48 | int objects; /* how many files and subdirectories*/ | ||
49 | int processed; | ||
50 | size_t append; /* Append position in 'path' for stack push */ | ||
51 | }; | ||
52 | |||
53 | static bool poll_cancel_action(const char *path, int operation, int current, int total) | ||
54 | { | ||
55 | const char *op_str = ""; | ||
56 | |||
57 | clear_screen_buffer(false); | ||
58 | |||
59 | if (operation == FOC_COPY) | ||
60 | op_str = str(LANG_COPYING); | ||
61 | else if (operation == FOC_MOVE) | ||
62 | op_str = str(LANG_MOVING); | ||
63 | else if (operation == FOC_COUNT) | ||
64 | op_str = str(LANG_SCANNING_DISK); | ||
65 | else if (operation == FOC_DELETE) | ||
66 | op_str = str(LANG_DELETING); | ||
67 | |||
68 | path_basename(path, &path); | ||
69 | |||
70 | if (total <= 0) | ||
71 | splashf(0, "%s (%d) %s", op_str, current, path); | ||
72 | else | ||
73 | splash_progress(current, total, "%s %s", op_str, path); | ||
74 | |||
75 | return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); | ||
76 | } | ||
77 | |||
78 | static struct file_op_params* init_file_op(struct file_op_params *param, | ||
79 | const char *basename, | ||
80 | const char *selected_file) | ||
81 | { | ||
82 | /* Assumes: basename will never be NULL */ | ||
83 | if (selected_file == NULL) | ||
84 | { | ||
85 | param->append = strlcpy(param->path, basename, sizeof (param->path)); | ||
86 | } | ||
87 | else | ||
88 | param->append = path_append(param->path, basename, | ||
89 | selected_file, sizeof (param->path)); | ||
90 | param->is_dir = dir_exists(param->path); | ||
91 | param->objects = 0; /* how many files and subdirectories*/ | ||
92 | param->processed = 0; | ||
93 | |||
94 | return param; | ||
95 | } | ||
96 | |||
97 | /* counts file objects, deletes file objects */ | ||
98 | static int directory_fileop(struct file_op_params *param, enum file_op_current fileop) | ||
99 | { | ||
100 | errno = 0; | ||
101 | DIR *dir = opendir(param->path); | ||
102 | if (!dir) { | ||
103 | if (errno == EMFILE) { | ||
104 | return FORC_TOO_MANY_SUBDIRS; | ||
105 | } | ||
106 | return FORC_PATH_NOT_EXIST; /* open error */ | ||
107 | } | ||
108 | int rc = FORC_SUCCESS; | ||
109 | size_t append = param->append; | ||
110 | |||
111 | /* walk through the directory content */ | ||
112 | while (rc == FORC_SUCCESS) { | ||
113 | errno = 0; /* distinguish failure from eod */ | ||
114 | struct dirent *entry = readdir(dir); | ||
115 | if (!entry) { | ||
116 | if (errno) { | ||
117 | rc = FORC_PATH_NOT_EXIST; | ||
118 | } | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | struct dirinfo info = dir_get_info(dir, entry); | ||
123 | if ((info.attribute & ATTR_DIRECTORY) && | ||
124 | is_dotdir_name(entry->d_name)) { | ||
125 | continue; /* skip these */ | ||
126 | } | ||
127 | |||
128 | /* append name to current directory */ | ||
129 | param->append = append + path_append(¶m->path[append], | ||
130 | PA_SEP_HARD, entry->d_name, | ||
131 | sizeof (param->path) - append); | ||
132 | |||
133 | if (fileop == FOC_DELETE) | ||
134 | { | ||
135 | param->processed++; | ||
136 | /* at this point we've already counted and never had a path too long | ||
137 | in the other branch so we 'should not' encounter one here either */ | ||
138 | |||
139 | if (param->processed > param->objects) | ||
140 | { | ||
141 | rc = FORC_UNKNOWN_FAILURE; | ||
142 | break; | ||
143 | } | ||
144 | |||
145 | if (info.attribute & ATTR_DIRECTORY) { | ||
146 | /* remove a subdirectory */ | ||
147 | rc = directory_fileop(param, fileop); | ||
148 | } else { | ||
149 | /* remove a file */ | ||
150 | if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) | ||
151 | { | ||
152 | rc = FORC_CANCELLED; | ||
153 | break; | ||
154 | } | ||
155 | rc = remove(param->path); | ||
156 | } | ||
157 | } | ||
158 | else /* count objects */ | ||
159 | { | ||
160 | param->objects++; | ||
161 | |||
162 | if (param->append >= sizeof (param->path)) { | ||
163 | rc = FORC_PATH_TOO_LONG; | ||
164 | break; /* no space left in buffer */ | ||
165 | } | ||
166 | |||
167 | if (info.attribute & ATTR_DIRECTORY) { | ||
168 | /* remove a subdirectory */ | ||
169 | rc = directory_fileop(param, FOC_COUNT); | ||
170 | } else { | ||
171 | if (poll_cancel_action(param->path, FOC_COUNT, param->objects, 0)) | ||
172 | { | ||
173 | rc = FORC_CANCELLED; | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | param->append = append; /* other functions may use param, reset append */ | ||
179 | /* Remove basename we added above */ | ||
180 | param->path[append] = '\0'; | ||
181 | } | ||
182 | |||
183 | closedir(dir); | ||
184 | |||
185 | if (fileop == FOC_DELETE && rc == FORC_SUCCESS) { | ||
186 | /* remove the now empty directory */ | ||
187 | if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) | ||
188 | { | ||
189 | rc = FORC_CANCELLED; | ||
190 | } else { | ||
191 | rc = rmdir(param->path); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | return rc; | ||
196 | } | ||
197 | |||
198 | static int check_count_fileobjects(struct file_op_params *param) | ||
199 | { | ||
200 | int rc = directory_fileop(param, FOC_COUNT); | ||
201 | DEBUGF("%s res:(%d) objects %d \n", __func__, rc, param->objects); | ||
202 | return rc; | ||
203 | } | ||
204 | |||
205 | static bool check_new_name(const char *basename) | ||
206 | { | ||
207 | /* at least prevent escapes out of the base directory from keyboard- | ||
208 | entered filenames; the file code should reject other invalidities */ | ||
209 | return *basename != '\0' && !strchr(basename, PATH_SEPCH) && | ||
210 | !is_dotdir_name(basename); | ||
211 | } | ||
212 | |||
213 | int create_dir(void) | ||
214 | { | ||
215 | int rc = FORC_UNKNOWN_FAILURE; | ||
216 | char dirname[MAX_PATH]; | ||
217 | size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD, | ||
218 | sizeof (dirname)); | ||
219 | char *basename = dirname + pathlen; | ||
220 | |||
221 | if (pathlen >= sizeof (dirname)) { | ||
222 | /* Too long */ | ||
223 | } else if (kbd_input(basename, sizeof (dirname) - pathlen, NULL) < 0) { | ||
224 | rc = FORC_CANCELLED; | ||
225 | } else if (check_new_name(basename)) { | ||
226 | rc = mkdir(dirname); | ||
227 | } | ||
228 | |||
229 | return rc; | ||
230 | } | ||
231 | |||
232 | /************************************************************************************/ | ||
233 | /* share code for file and directory deletion, saves space */ | ||
234 | static int delete_file_dir(struct file_op_params *param) | ||
235 | { | ||
236 | /* Note: delete_file_dir() will happily delete whatever | ||
237 | * path is passed (after confirmation) */ | ||
238 | if (confirm_delete_yesno(param->path) != YESNO_YES) { | ||
239 | return FORC_CANCELLED; | ||
240 | } | ||
241 | |||
242 | clear_screen_buffer(true); | ||
243 | poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects); | ||
244 | |||
245 | int rc = FORC_UNKNOWN_FAILURE; | ||
246 | |||
247 | if (param->is_dir) { /* if directory */ | ||
248 | cpu_boost(true); | ||
249 | rc = directory_fileop(param, FOC_DELETE); | ||
250 | cpu_boost(false); | ||
251 | } else { | ||
252 | rc = remove(param->path); | ||
253 | } | ||
254 | |||
255 | return rc; | ||
256 | } | ||
257 | |||
258 | int delete_fileobject(const char *selected_file) | ||
259 | { | ||
260 | struct file_op_params param; | ||
261 | if (init_file_op(¶m, selected_file, NULL)->is_dir == true) | ||
262 | { | ||
263 | int rc = check_count_fileobjects(¶m); | ||
264 | DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path); | ||
265 | if (rc != FORC_SUCCESS) | ||
266 | return rc; | ||
267 | } | ||
268 | |||
269 | return delete_file_dir(¶m); | ||
270 | } | ||
271 | |||
272 | int rename_file(const char *selected_file) | ||
273 | { | ||
274 | int rc = FORC_UNKNOWN_FAILURE; | ||
275 | char newname[MAX_PATH]; | ||
276 | const char *oldbase, *selection = selected_file; | ||
277 | |||
278 | path_basename(selection, &oldbase); | ||
279 | size_t pathlen = oldbase - selection; | ||
280 | char *newbase = newname + pathlen; | ||
281 | |||
282 | if (strmemccpy(newname, selection, sizeof (newname)) == NULL) { | ||
283 | /* Too long */ | ||
284 | } else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) { | ||
285 | rc = FORC_CANCELLED; | ||
286 | } else if (!strcmp(oldbase, newbase)) { | ||
287 | rc = FORC_NOOP; /* No change at all */ | ||
288 | } else if (check_new_name(newbase)) { | ||
289 | switch (relate(selection, newname)) | ||
290 | { | ||
291 | case RELATE_DIFFERENT: | ||
292 | if (file_exists(newname)) { | ||
293 | break; /* don't overwrite */ | ||
294 | } | ||
295 | /* Fall-through */ | ||
296 | case RELATE_SAME: | ||
297 | rc = rename(selection, newname); | ||
298 | break; | ||
299 | case RELATE_PREFIX: | ||
300 | default: | ||
301 | break; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | return rc; | ||
306 | } | ||
307 | |||
308 | static int move_by_rename(const char *src_path, | ||
309 | const char *dst_path, | ||
310 | unsigned int *pflags) | ||
311 | { | ||
312 | unsigned int flags = *pflags; | ||
313 | int rc = FORC_UNKNOWN_FAILURE; | ||
314 | while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { | ||
315 | if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) { | ||
316 | /* Just try to move the directory / file */ | ||
317 | if (poll_cancel_action(src_path, FOC_MOVE, 0 , 0)) { | ||
318 | rc = FORC_CANCELLED; | ||
319 | } else { | ||
320 | rc = rename(src_path, dst_path); | ||
321 | } | ||
322 | |||
323 | if (rc < 0) { | ||
324 | int errnum = errno; | ||
325 | if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { | ||
326 | /* Directory is not empty thus rename() will not do a quick | ||
327 | overwrite */ | ||
328 | break; | ||
329 | } | ||
330 | #ifdef HAVE_MULTIVOLUME | ||
331 | else if (errnum == EXDEV) { | ||
332 | /* Failed because cross volume rename doesn't work; force | ||
333 | a move instead */ | ||
334 | *pflags |= PASTE_EXDEV; | ||
335 | break; | ||
336 | } | ||
337 | #endif /* HAVE_MULTIVOLUME */ | ||
338 | } | ||
339 | } | ||
340 | |||
341 | break; | ||
342 | } | ||
343 | return rc; | ||
344 | } | ||
345 | |||
346 | /* Paste a file */ | ||
347 | static int copy_move_file(const char *src_path, const char *dst_path, unsigned int flags) | ||
348 | { | ||
349 | /* Try renaming first */ | ||
350 | int rc = move_by_rename(src_path, dst_path, &flags); | ||
351 | if (rc == FORC_SUCCESS) | ||
352 | return rc; | ||
353 | |||
354 | /* See if we can get the plugin buffer for the file copy buffer */ | ||
355 | size_t buffersize; | ||
356 | char *buffer = (char *) plugin_get_buffer(&buffersize); | ||
357 | if (buffer == NULL || buffersize < 512) { | ||
358 | /* Not large enough, try for a disk sector worth of stack | ||
359 | instead */ | ||
360 | buffersize = 512; | ||
361 | buffer = (char *)alloca(buffersize); | ||
362 | } | ||
363 | |||
364 | if (buffer == NULL) { | ||
365 | return FORC_NO_BUFFER_AVAIL; | ||
366 | } | ||
367 | |||
368 | buffersize &= ~0x1ff; /* Round buffer size to multiple of sector | ||
369 | size */ | ||
370 | |||
371 | int src_fd = open(src_path, O_RDONLY); | ||
372 | if (src_fd >= 0) { | ||
373 | off_t src_sz = lseek(src_fd, 0, SEEK_END); | ||
374 | lseek(src_fd, 0, SEEK_SET); | ||
375 | |||
376 | int oflag = O_WRONLY|O_CREAT; | ||
377 | |||
378 | if (!(flags & PASTE_OVERWRITE)) { | ||
379 | oflag |= O_EXCL; | ||
380 | } | ||
381 | |||
382 | int dst_fd = open(dst_path, oflag, 0666); | ||
383 | if (dst_fd >= 0) { | ||
384 | off_t total_size = 0; | ||
385 | off_t next_cancel_test = 0; /* No excessive button polling */ | ||
386 | |||
387 | rc = FORC_SUCCESS; | ||
388 | |||
389 | while (rc == FORC_SUCCESS) { | ||
390 | if (total_size >= next_cancel_test) { | ||
391 | next_cancel_test = total_size + 0x10000; | ||
392 | if (poll_cancel_action(src_path, | ||
393 | !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, | ||
394 | total_size, src_sz)) | ||
395 | { | ||
396 | rc = FORC_CANCELLED; | ||
397 | break; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | ssize_t bytesread = read(src_fd, buffer, buffersize); | ||
402 | if (bytesread <= 0) { | ||
403 | if (bytesread < 0) { | ||
404 | rc = FORC_READ_FAILURE; | ||
405 | } | ||
406 | /* else eof on buffer boundary; nothing to write */ | ||
407 | break; | ||
408 | } | ||
409 | |||
410 | ssize_t byteswritten = write(dst_fd, buffer, bytesread); | ||
411 | if (byteswritten < bytesread) { | ||
412 | /* Some I/O error */ | ||
413 | rc = FORC_WRITE_FAILURE; | ||
414 | break; | ||
415 | } | ||
416 | |||
417 | total_size += byteswritten; | ||
418 | |||
419 | if (bytesread < (ssize_t)buffersize) { | ||
420 | /* EOF with trailing bytes */ | ||
421 | break; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | if (rc == FORC_SUCCESS) { | ||
426 | /* If overwriting, set the correct length if original was | ||
427 | longer */ | ||
428 | rc = ftruncate(dst_fd, total_size) * 10; | ||
429 | } | ||
430 | |||
431 | close(dst_fd); | ||
432 | |||
433 | if (rc != FORC_SUCCESS) { | ||
434 | /* Copy failed. Cleanup. */ | ||
435 | remove(dst_path); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | close(src_fd); | ||
440 | } | ||
441 | |||
442 | if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { | ||
443 | /* Remove the source file */ | ||
444 | rc = remove(src_path) * 10; | ||
445 | } | ||
446 | |||
447 | return rc; | ||
448 | } | ||
449 | |||
450 | /* Paste a directory */ | ||
451 | static int copy_move_directory(struct file_op_params *src, | ||
452 | struct file_op_params *dst, | ||
453 | unsigned int flags) | ||
454 | { | ||
455 | int rc = FORC_UNKNOWN_FAILURE; | ||
456 | |||
457 | DIR *srcdir = opendir(src->path); | ||
458 | |||
459 | if (srcdir) { | ||
460 | /* Make a directory to copy things to */ | ||
461 | rc = mkdir(dst->path) * 10; | ||
462 | if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) { | ||
463 | /* Exists and overwrite was approved */ | ||
464 | rc = FORC_SUCCESS; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | size_t srcap = src->append, dstap = dst->append; | ||
469 | |||
470 | /* Walk through the directory content; this loop will exit as soon as | ||
471 | there's a problem */ | ||
472 | while (rc == FORC_SUCCESS) { | ||
473 | errno = 0; /* Distinguish failure from eod */ | ||
474 | struct dirent *entry = readdir(srcdir); | ||
475 | if (!entry) { | ||
476 | if (errno) { | ||
477 | rc = FORC_PATH_NOT_EXIST; | ||
478 | } | ||
479 | break; | ||
480 | } | ||
481 | |||
482 | struct dirinfo info = dir_get_info(srcdir, entry); | ||
483 | if ((info.attribute & ATTR_DIRECTORY) && | ||
484 | is_dotdir_name(entry->d_name)) { | ||
485 | continue; /* Skip these */ | ||
486 | } | ||
487 | |||
488 | /* Append names to current directories */ | ||
489 | src->append = srcap + | ||
490 | path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name, | ||
491 | sizeof(src->path) - srcap); | ||
492 | |||
493 | dst->append = dstap + | ||
494 | path_append(&dst->path[dstap], PA_SEP_HARD, entry->d_name, | ||
495 | sizeof (dst->path) - dstap); | ||
496 | /* src length was already checked by check_count_fileobjects() */ | ||
497 | if (dst->append >= sizeof (dst->path)) { | ||
498 | rc = FORC_PATH_TOO_LONG; /* No space left in buffer */ | ||
499 | break; | ||
500 | } | ||
501 | |||
502 | src->processed++; | ||
503 | if (src->processed > src->objects) | ||
504 | { | ||
505 | rc = FORC_UNKNOWN_FAILURE; | ||
506 | break; | ||
507 | } | ||
508 | |||
509 | if (poll_cancel_action(src->path, | ||
510 | !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, | ||
511 | src->processed, src->objects)) | ||
512 | { | ||
513 | rc = FORC_CANCELLED; | ||
514 | break; | ||
515 | } | ||
516 | |||
517 | DEBUGF("Copy %s to %s\n", src->path, dst->path); | ||
518 | |||
519 | if (info.attribute & ATTR_DIRECTORY) { | ||
520 | /* Copy/move a subdirectory */ | ||
521 | rc = copy_move_directory(src, dst, flags); /* recursion */; | ||
522 | } else { | ||
523 | /* Copy/move a file */ | ||
524 | rc = copy_move_file(src->path, dst->path, flags); | ||
525 | } | ||
526 | |||
527 | /* Remove basenames we added above */ | ||
528 | src->path[srcap] = '\0'; | ||
529 | dst->path[dstap] = '\0'; | ||
530 | } | ||
531 | |||
532 | if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { | ||
533 | /* Remove the now empty directory */ | ||
534 | rc = rmdir(src->path) * 10; | ||
535 | } | ||
536 | |||
537 | closedir(srcdir); | ||
538 | return rc; | ||
539 | } | ||
540 | |||
541 | int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned int flags) | ||
542 | { | ||
543 | if (!src_path[0]) | ||
544 | return FORC_NOOP; | ||
545 | |||
546 | int rc = FORC_UNKNOWN_FAILURE; | ||
547 | |||
548 | struct file_op_params src, dst; | ||
549 | |||
550 | /* Figure out the name of the selection */ | ||
551 | const char *nameptr; | ||
552 | path_basename(src_path, &nameptr); | ||
553 | |||
554 | /* Final target is current directory plus name of selection */ | ||
555 | init_file_op(&dst, dst_path, nameptr); | ||
556 | |||
557 | switch (dst.append < sizeof (dst.path) ? | ||
558 | relate(src_path, dst.path) : FORC_PATH_TOO_LONG) | ||
559 | { | ||
560 | case RELATE_SAME: | ||
561 | rc = FORC_NOOP; | ||
562 | break; | ||
563 | |||
564 | case RELATE_DIFFERENT: | ||
565 | if (file_exists(dst.path)) { | ||
566 | /* If user chooses not to overwrite, cancel */ | ||
567 | if (confirm_overwrite_yesno() == YESNO_NO) { | ||
568 | rc = FORC_NOOVERWRT; | ||
569 | break; | ||
570 | } | ||
571 | |||
572 | flags |= PASTE_OVERWRITE; | ||
573 | } | ||
574 | |||
575 | /* Now figure out what we're doing */ | ||
576 | cpu_boost(true); | ||
577 | |||
578 | init_file_op(&src, src_path, NULL); | ||
579 | |||
580 | if (src.is_dir) { | ||
581 | /* Copy or move a subdirectory */ | ||
582 | |||
583 | if (src.append < sizeof (src.path)) { | ||
584 | /* Try renaming first */ | ||
585 | rc = move_by_rename(src.path, dst.path, &flags); | ||
586 | if (rc != FORC_SUCCESS && rc != FORC_CANCELLED) { | ||
587 | if (check_count_fileobjects(&src) == FORC_SUCCESS) { | ||
588 | rc = copy_move_directory(&src, &dst, flags); | ||
589 | } | ||
590 | } | ||
591 | } | ||
592 | } else { | ||
593 | /* Copy or move a file */ | ||
594 | rc = copy_move_file(src_path, dst.path, flags); | ||
595 | } | ||
596 | |||
597 | cpu_boost(false); | ||
598 | break; | ||
599 | |||
600 | case RELATE_PREFIX: | ||
601 | default: /* Some other relation / failure */ | ||
602 | break; | ||
603 | } | ||
604 | |||
605 | return rc; | ||
606 | } | ||
diff --git a/apps/fileop.h b/apps/fileop.h new file mode 100644 index 0000000000..f8237dc64f --- /dev/null +++ b/apps/fileop.h | |||
@@ -0,0 +1,70 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef FILEOP_H | ||
22 | #define FILEOP_H | ||
23 | |||
24 | #include "file.h" | ||
25 | |||
26 | /* result codes of various file operations */ | ||
27 | enum fileop_result_code | ||
28 | { | ||
29 | FORC_READ_FAILURE = -7, | ||
30 | FORC_WRITE_FAILURE = -6, | ||
31 | FORC_NO_BUFFER_AVAIL = -5, | ||
32 | FORC_TOO_MANY_SUBDIRS = -4, | ||
33 | FORC_PATH_TOO_LONG = -3, | ||
34 | FORC_PATH_NOT_EXIST = -2, | ||
35 | FORC_UNKNOWN_FAILURE = -1, | ||
36 | /* Anything < 0 is failure */ | ||
37 | FORC_SUCCESS = 0, /* All operations completed successfully */ | ||
38 | FORC_NOOP = 1, /* Operation didn't need to do anything */ | ||
39 | FORC_CANCELLED = 2, /* Operation was cancelled by user */ | ||
40 | FORC_NOOVERWRT = 3, | ||
41 | }; | ||
42 | |||
43 | enum file_op_flags | ||
44 | { | ||
45 | PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */ | ||
46 | PASTE_COPY = 0x01, /* Is a copy operation */ | ||
47 | PASTE_OVERWRITE = 0x02, /* Overwrite destination */ | ||
48 | PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */ | ||
49 | }; | ||
50 | |||
51 | enum file_op_current | ||
52 | { | ||
53 | FOC_NONE = 0, | ||
54 | FOC_COUNT, | ||
55 | FOC_MOVE, | ||
56 | FOC_COPY, | ||
57 | FOC_DELETE, | ||
58 | FOC_CREATE, | ||
59 | }; | ||
60 | |||
61 | int create_dir(void); | ||
62 | |||
63 | int rename_file(const char *selected_file); | ||
64 | |||
65 | int delete_fileobject(const char *selected_file); | ||
66 | |||
67 | int copy_move_fileobject(const char *src_path, const char *dst_path, | ||
68 | unsigned int flags); | ||
69 | |||
70 | #endif /* FILEOP_H */ | ||
diff --git a/apps/misc.c b/apps/misc.c index dd73c98a69..d8caabd397 100644 --- a/apps/misc.c +++ b/apps/misc.c | |||
@@ -1999,4 +1999,22 @@ long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm | |||
1999 | 1999 | ||
2000 | return vol >> NVOL_FRACBITS; | 2000 | return vol >> NVOL_FRACBITS; |
2001 | } | 2001 | } |
2002 | |||
2003 | void clear_screen_buffer(bool update) | ||
2004 | { | ||
2005 | struct viewport vp; | ||
2006 | struct viewport *last_vp; | ||
2007 | FOR_NB_SCREENS(i) | ||
2008 | { | ||
2009 | struct screen * screen = &screens[i]; | ||
2010 | viewport_set_defaults(&vp, screen->screen_type); | ||
2011 | last_vp = screen->set_viewport(&vp); | ||
2012 | screen->clear_viewport(); | ||
2013 | if (update) { | ||
2014 | screen->update_viewport(); | ||
2015 | } | ||
2016 | screen->set_viewport(last_vp); | ||
2017 | } | ||
2018 | } | ||
2019 | |||
2002 | #endif /* ndef __PCTOOL__ */ | 2020 | #endif /* ndef __PCTOOL__ */ |
diff --git a/apps/misc.h b/apps/misc.h index c6485db4ff..e5fb7a3d1f 100644 --- a/apps/misc.h +++ b/apps/misc.h | |||
@@ -277,4 +277,8 @@ long to_normalized_volume(long vol, long min_vol, long max_vol, long max_norm); | |||
277 | * for the given normalized volume. */ | 277 | * for the given normalized volume. */ |
278 | long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm); | 278 | long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm); |
279 | 279 | ||
280 | /* clear the lcd output buffer, if update is true the cleared buffer | ||
281 | * will be written to the lcd */ | ||
282 | void clear_screen_buffer(bool update); | ||
283 | |||
280 | #endif /* MISC_H */ | 284 | #endif /* MISC_H */ |
diff --git a/apps/onplay.c b/apps/onplay.c index 572138e583..4880af58f3 100644 --- a/apps/onplay.c +++ b/apps/onplay.c | |||
@@ -26,7 +26,6 @@ | |||
26 | 26 | ||
27 | #include "debug.h" | 27 | #include "debug.h" |
28 | #include "lcd.h" | 28 | #include "lcd.h" |
29 | #include "file.h" | ||
30 | #include "audio.h" | 29 | #include "audio.h" |
31 | #include "menu.h" | 30 | #include "menu.h" |
32 | #include "lang.h" | 31 | #include "lang.h" |
@@ -43,6 +42,7 @@ | |||
43 | #include "talk.h" | 42 | #include "talk.h" |
44 | #include "onplay.h" | 43 | #include "onplay.h" |
45 | #include "filetypes.h" | 44 | #include "filetypes.h" |
45 | #include "fileop.h" | ||
46 | #include "open_plugin.h" | 46 | #include "open_plugin.h" |
47 | #include "plugin.h" | 47 | #include "plugin.h" |
48 | #include "bookmark.h" | 48 | #include "bookmark.h" |
@@ -84,31 +84,6 @@ extern struct menu_item_ex file_menu; /* settings_menu.c */ | |||
84 | MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ | 84 | MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ |
85 | { (void*)name##_},{.callback_and_desc = & name##__}}; | 85 | { (void*)name##_},{.callback_and_desc = & name##__}}; |
86 | 86 | ||
87 | /* Used for directory move, copy and delete */ | ||
88 | struct dirrecurse_params | ||
89 | { | ||
90 | char path[MAX_PATH]; /* Buffer for full path */ | ||
91 | size_t append; /* Append position in 'path' for stack push */ | ||
92 | }; | ||
93 | |||
94 | enum clipboard_op_flags | ||
95 | { | ||
96 | PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */ | ||
97 | PASTE_COPY = 0x01, /* Is a copy operation */ | ||
98 | PASTE_OVERWRITE = 0x02, /* Overwrite destination */ | ||
99 | PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */ | ||
100 | }; | ||
101 | |||
102 | /* result codec of various onplay operations */ | ||
103 | enum onplay_result_code | ||
104 | { | ||
105 | /* Anything < 0 is failure */ | ||
106 | OPRC_SUCCESS = 0, /* All operations completed successfully */ | ||
107 | OPRC_NOOP = 1, /* Operation didn't need to do anything */ | ||
108 | OPRC_CANCELLED = 2, /* Operation was cancelled by user */ | ||
109 | OPRC_NOOVERWRT = 3, | ||
110 | }; | ||
111 | |||
112 | static struct clipboard | 87 | static struct clipboard |
113 | { | 88 | { |
114 | char path[MAX_PATH]; /* Clipped file's path */ | 89 | char path[MAX_PATH]; /* Clipped file's path */ |
@@ -191,8 +166,6 @@ static int bookmark_menu_callback(int action, | |||
191 | return action; | 166 | return action; |
192 | } | 167 | } |
193 | 168 | ||
194 | |||
195 | |||
196 | /* CONTEXT_WPS playlist options */ | 169 | /* CONTEXT_WPS playlist options */ |
197 | static bool shuffle_playlist(void) | 170 | static bool shuffle_playlist(void) |
198 | { | 171 | { |
@@ -244,46 +217,82 @@ MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST), | |||
244 | ); | 217 | ); |
245 | 218 | ||
246 | /* argument for add_to_playlist (for use by menu callbacks) */ | 219 | /* argument for add_to_playlist (for use by menu callbacks) */ |
220 | #define PL_NONE 0x00 | ||
221 | #define PL_QUEUE 0x01 | ||
222 | #define PL_REPLACE 0x02 | ||
247 | struct add_to_pl_param | 223 | struct add_to_pl_param |
248 | { | 224 | { |
249 | int8_t position; | 225 | int8_t position; |
250 | unsigned int queue: 1; | 226 | uint8_t flags; |
251 | unsigned int replace: 1; | ||
252 | }; | 227 | }; |
253 | 228 | ||
254 | static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, 0, 0}; | 229 | static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, PL_NONE}; |
255 | static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, 0, 0}; | 230 | static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, PL_NONE}; |
256 | static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, 0, 0}; | 231 | static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, PL_NONE}; |
257 | static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, 0, 0}; | 232 | static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_NONE}; |
258 | static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 0}; | 233 | static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_NONE}; |
259 | 234 | ||
260 | static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, 1, 0}; | 235 | static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, PL_QUEUE}; |
261 | static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, 1, 0}; | 236 | static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, PL_QUEUE}; |
262 | static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, 1, 0}; | 237 | static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, PL_QUEUE}; |
263 | static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, 1, 0}; | 238 | static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, PL_QUEUE}; |
264 | static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 1, 0}; | 239 | static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_QUEUE}; |
265 | 240 | ||
266 | static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, 0, 1}; | 241 | static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, PL_REPLACE}; |
267 | static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 1}; | 242 | static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, PL_REPLACE}; |
243 | |||
244 | static void op_playlist_insert_selected(int position, bool queue) | ||
245 | { | ||
246 | #ifdef HAVE_TAGCACHE | ||
247 | if (context == CONTEXT_STD && ctx_current_playlist_insert != NULL) | ||
248 | { | ||
249 | ctx_current_playlist_insert(position, queue, false); | ||
250 | return; | ||
251 | } | ||
252 | #endif | ||
253 | if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) | ||
254 | playlist_insert_track(NULL, selected_file, position, queue, true); | ||
255 | else if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U) | ||
256 | playlist_insert_playlist(NULL, selected_file, position, queue); | ||
257 | else if (selected_file_attr & ATTR_DIRECTORY) | ||
258 | { | ||
259 | #ifdef HAVE_TAGCACHE | ||
260 | if (context == CONTEXT_ID3DB) | ||
261 | { | ||
262 | tagtree_current_playlist_insert(position, queue); | ||
263 | return; | ||
264 | } | ||
265 | #endif | ||
266 | bool recurse = (global_settings.recursive_dir_insert == RECURSE_ON); | ||
267 | if (global_settings.recursive_dir_insert == RECURSE_ASK) | ||
268 | { | ||
269 | |||
270 | const char *lines[] = { | ||
271 | ID2P(LANG_RECURSE_DIRECTORY_QUESTION), | ||
272 | selected_file | ||
273 | }; | ||
274 | const struct text_message message={lines, 2}; | ||
275 | /* Ask if user wants to recurse directory */ | ||
276 | recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES); | ||
277 | } | ||
278 | |||
279 | playlist_insert_directory(NULL, selected_file, position, queue, | ||
280 | recurse == RECURSE_ON); | ||
281 | } | ||
282 | } | ||
268 | 283 | ||
269 | /* CONTEXT_[TREE|ID3DB|STD] playlist options */ | 284 | /* CONTEXT_[TREE|ID3DB|STD] playlist options */ |
270 | static int add_to_playlist(void* arg) | 285 | static int add_to_playlist(void* arg) |
271 | { | 286 | { |
272 | struct add_to_pl_param* param = arg; | 287 | struct add_to_pl_param* param = arg; |
273 | int position = param->position; | 288 | int position = param->position; |
274 | bool new_playlist = !!param->replace; | 289 | bool new_playlist = (param->flags & PL_REPLACE) == PL_REPLACE; |
275 | bool queue = !!param->queue; | 290 | bool queue = (param->flags & PL_QUEUE) == PL_QUEUE; |
276 | 291 | ||
277 | /* warn if replacing the playlist */ | 292 | /* warn if replacing the playlist */ |
278 | if (new_playlist && !warn_on_pl_erase()) | 293 | if (new_playlist && !warn_on_pl_erase()) |
279 | return 0; | 294 | return 0; |
280 | 295 | ||
281 | const char *lines[] = { | ||
282 | ID2P(LANG_RECURSE_DIRECTORY_QUESTION), | ||
283 | selected_file | ||
284 | }; | ||
285 | const struct text_message message={lines, 2}; | ||
286 | |||
287 | splash(0, ID2P(LANG_WAIT)); | 296 | splash(0, ID2P(LANG_WAIT)); |
288 | 297 | ||
289 | if (new_playlist && global_settings.keep_current_track_on_replace_playlist) | 298 | if (new_playlist && global_settings.keep_current_track_on_replace_playlist) |
@@ -307,38 +316,7 @@ static int add_to_playlist(void* arg) | |||
307 | playlist_set_last_shuffled_start(); | 316 | playlist_set_last_shuffled_start(); |
308 | } | 317 | } |
309 | 318 | ||
310 | #ifdef HAVE_TAGCACHE | 319 | op_playlist_insert_selected(position, queue); |
311 | if ((context == CONTEXT_ID3DB) && (selected_file_attr & ATTR_DIRECTORY)) | ||
312 | { | ||
313 | tagtree_current_playlist_insert(position, queue); | ||
314 | } | ||
315 | else if (context == CONTEXT_STD && ctx_current_playlist_insert != NULL) | ||
316 | { | ||
317 | ctx_current_playlist_insert(position, queue, false); | ||
318 | } | ||
319 | else | ||
320 | #endif | ||
321 | { | ||
322 | if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) | ||
323 | playlist_insert_track(NULL, selected_file, position, queue, true); | ||
324 | else if (selected_file_attr & ATTR_DIRECTORY) | ||
325 | { | ||
326 | bool recurse = false; | ||
327 | |||
328 | if (global_settings.recursive_dir_insert != RECURSE_ASK) | ||
329 | recurse = (bool)global_settings.recursive_dir_insert; | ||
330 | else | ||
331 | { | ||
332 | /* Ask if user wants to recurse directory */ | ||
333 | recurse = (gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES); | ||
334 | } | ||
335 | |||
336 | playlist_insert_directory(NULL, selected_file, position, queue, | ||
337 | recurse); | ||
338 | } | ||
339 | else if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U) | ||
340 | playlist_insert_playlist(NULL, selected_file, position, queue); | ||
341 | } | ||
342 | 320 | ||
343 | if (new_playlist && (playlist_amount() > 0)) | 321 | if (new_playlist && (playlist_amount() > 0)) |
344 | { | 322 | { |
@@ -447,18 +425,21 @@ MAKE_ONPLAYMENU(tree_playlist_menu, ID2P(LANG_PLAYING_NEXT), | |||
447 | &replace_pl_item, | 425 | &replace_pl_item, |
448 | &replace_shuf_pl_item | 426 | &replace_shuf_pl_item |
449 | ); | 427 | ); |
428 | |||
450 | static int treeplaylist_callback(int action, | 429 | static int treeplaylist_callback(int action, |
451 | const struct menu_item_ex *this_item, | 430 | const struct menu_item_ex *this_item, |
452 | struct gui_synclist *this_list) | 431 | struct gui_synclist *this_list) |
453 | { | 432 | { |
454 | (void)this_list; | 433 | (void)this_list; |
434 | int sel_file_attr = (selected_file_attr & FILE_ATTR_MASK); | ||
435 | |||
455 | switch (action) | 436 | switch (action) |
456 | { | 437 | { |
457 | case ACTION_REQUEST_MENUITEM: | 438 | case ACTION_REQUEST_MENUITEM: |
458 | if (this_item == &tree_playlist_menu) | 439 | if (this_item == &tree_playlist_menu) |
459 | { | 440 | { |
460 | if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO && | 441 | if (sel_file_attr != FILE_ATTR_AUDIO && |
461 | (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U && | 442 | sel_file_attr != FILE_ATTR_M3U && |
462 | (selected_file_attr & ATTR_DIRECTORY) == 0) | 443 | (selected_file_attr & ATTR_DIRECTORY) == 0) |
463 | return ACTION_EXIT_MENUITEM; | 444 | return ACTION_EXIT_MENUITEM; |
464 | } | 445 | } |
@@ -476,7 +457,7 @@ static int treeplaylist_callback(int action, | |||
476 | { | 457 | { |
477 | struct add_to_pl_param *param = this_item->function_param->param; | 458 | struct add_to_pl_param *param = this_item->function_param->param; |
478 | 459 | ||
479 | if (param->queue) | 460 | if ((param->flags & PL_QUEUE) == PL_QUEUE) |
480 | { | 461 | { |
481 | if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL && | 462 | if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL && |
482 | !in_queue_submenu) | 463 | !in_queue_submenu) |
@@ -489,12 +470,12 @@ static int treeplaylist_callback(int action, | |||
489 | if (!global_settings.show_shuffled_adding_options) | 470 | if (!global_settings.show_shuffled_adding_options) |
490 | return ACTION_EXIT_MENUITEM; | 471 | return ACTION_EXIT_MENUITEM; |
491 | 472 | ||
492 | if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U && | 473 | if (sel_file_attr != FILE_ATTR_M3U && |
493 | (selected_file_attr & ATTR_DIRECTORY) == 0) | 474 | (selected_file_attr & ATTR_DIRECTORY) == 0) |
494 | return ACTION_EXIT_MENUITEM; | 475 | return ACTION_EXIT_MENUITEM; |
495 | } | 476 | } |
496 | 477 | ||
497 | if (!param->replace) | 478 | if ((param->flags & PL_REPLACE) != PL_REPLACE) |
498 | { | 479 | { |
499 | if (!(audio_status() & AUDIO_STATUS_PLAY)) | 480 | if (!(audio_status() & AUDIO_STATUS_PLAY)) |
500 | return ACTION_EXIT_MENUITEM; | 481 | return ACTION_EXIT_MENUITEM; |
@@ -583,482 +564,17 @@ static int cat_playlist_callback(int action, | |||
583 | return action; | 564 | return action; |
584 | } | 565 | } |
585 | 566 | ||
586 | static void draw_slider(void) | ||
587 | { | ||
588 | struct viewport *last_vp; | ||
589 | FOR_NB_SCREENS(i) | ||
590 | { | ||
591 | struct viewport vp; | ||
592 | int slider_height = 2*screens[i].getcharheight(); | ||
593 | viewport_set_defaults(&vp, i); | ||
594 | last_vp = screens[i].set_viewport(&vp); | ||
595 | show_busy_slider(&screens[i], 1, vp.height - slider_height, | ||
596 | vp.width-2, slider_height-1); | ||
597 | screens[i].update_viewport(); | ||
598 | screens[i].set_viewport(last_vp); | ||
599 | } | ||
600 | } | ||
601 | |||
602 | static void clear_display(bool update) | ||
603 | { | ||
604 | struct viewport vp; | ||
605 | struct viewport *last_vp; | ||
606 | FOR_NB_SCREENS(i) | ||
607 | { | ||
608 | struct screen * screen = &screens[i]; | ||
609 | viewport_set_defaults(&vp, screen->screen_type); | ||
610 | last_vp = screen->set_viewport(&vp); | ||
611 | screen->clear_viewport(); | ||
612 | if (update) { | ||
613 | screen->update_viewport(); | ||
614 | } | ||
615 | screen->set_viewport(last_vp); | ||
616 | } | ||
617 | } | ||
618 | |||
619 | static void splash_path(const char *path) | ||
620 | { | ||
621 | clear_display(false); | ||
622 | path_basename(path, &path); | ||
623 | splash(0, path); | ||
624 | draw_slider(); | ||
625 | } | ||
626 | |||
627 | /* Splashes the path and checks the keys */ | ||
628 | static bool poll_cancel_action(const char *path) | ||
629 | { | ||
630 | splash_path(path); | ||
631 | return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); | ||
632 | } | ||
633 | |||
634 | static bool check_new_name(const char *basename) | ||
635 | { | ||
636 | /* at least prevent escapes out of the base directory from keyboard- | ||
637 | entered filenames; the file code should reject other invalidities */ | ||
638 | return *basename != '\0' && !strchr(basename, PATH_SEPCH) && | ||
639 | !is_dotdir_name(basename); | ||
640 | } | ||
641 | |||
642 | static void splash_cancelled(void) | 567 | static void splash_cancelled(void) |
643 | { | 568 | { |
644 | clear_display(true); | 569 | clear_screen_buffer(true); |
645 | splash(HZ, ID2P(LANG_CANCEL)); | 570 | splash(HZ, ID2P(LANG_CANCEL)); |
646 | } | 571 | } |
647 | 572 | ||
648 | static void splash_failed(int lang_what) | 573 | static void splash_failed(int lang_what, int err) |
649 | { | 574 | { |
650 | cond_talk_ids_fq(lang_what, LANG_FAILED); | 575 | cond_talk_ids_fq(lang_what, LANG_FAILED); |
651 | clear_display(true); | 576 | clear_screen_buffer(true); |
652 | splashf(HZ*2, "%s %s", str(lang_what), str(LANG_FAILED)); | 577 | splashf(HZ*2, "%s %s (%d)", str(lang_what), str(LANG_FAILED), err); |
653 | } | ||
654 | |||
655 | /* helper function to remove a non-empty directory */ | ||
656 | static int remove_dir(struct dirrecurse_params *parm) | ||
657 | { | ||
658 | DIR *dir = opendir(parm->path); | ||
659 | if (!dir) { | ||
660 | return -1; /* open error */ | ||
661 | } | ||
662 | |||
663 | size_t append = parm->append; | ||
664 | int rc = OPRC_SUCCESS; | ||
665 | |||
666 | /* walk through the directory content */ | ||
667 | while (rc == OPRC_SUCCESS) { | ||
668 | errno = 0; /* distinguish failure from eod */ | ||
669 | struct dirent *entry = readdir(dir); | ||
670 | if (!entry) { | ||
671 | if (errno) { | ||
672 | rc = -1; | ||
673 | } | ||
674 | break; | ||
675 | } | ||
676 | |||
677 | struct dirinfo info = dir_get_info(dir, entry); | ||
678 | if ((info.attribute & ATTR_DIRECTORY) && | ||
679 | is_dotdir_name(entry->d_name)) { | ||
680 | continue; /* skip these */ | ||
681 | } | ||
682 | |||
683 | /* append name to current directory */ | ||
684 | parm->append = append + path_append(&parm->path[append], | ||
685 | PA_SEP_HARD, entry->d_name, | ||
686 | sizeof (parm->path) - append); | ||
687 | if (parm->append >= sizeof (parm->path)) { | ||
688 | rc = -1; | ||
689 | break; /* no space left in buffer */ | ||
690 | } | ||
691 | |||
692 | if (info.attribute & ATTR_DIRECTORY) { | ||
693 | /* remove a subdirectory */ | ||
694 | rc = remove_dir(parm); | ||
695 | } else { | ||
696 | /* remove a file */ | ||
697 | if (poll_cancel_action(parm->path)) { | ||
698 | rc = OPRC_CANCELLED; | ||
699 | break; | ||
700 | } | ||
701 | |||
702 | rc = remove(parm->path); | ||
703 | } | ||
704 | |||
705 | /* Remove basename we added above */ | ||
706 | parm->path[append] = '\0'; | ||
707 | } | ||
708 | |||
709 | closedir(dir); | ||
710 | |||
711 | if (rc == 0) { | ||
712 | /* remove the now empty directory */ | ||
713 | if (poll_cancel_action(parm->path)) { | ||
714 | rc = OPRC_CANCELLED; | ||
715 | } else { | ||
716 | rc = rmdir(parm->path); | ||
717 | } | ||
718 | } | ||
719 | |||
720 | return rc; | ||
721 | } | ||
722 | |||
723 | /* share code for file and directory deletion, saves space */ | ||
724 | static int delete_file_dir(void) | ||
725 | { | ||
726 | const char *to_delete=selected_file; | ||
727 | const int to_delete_attr=selected_file_attr; | ||
728 | if (confirm_delete_yesno(to_delete) != YESNO_YES) { | ||
729 | return 1; | ||
730 | } | ||
731 | |||
732 | clear_display(true); | ||
733 | splash(HZ/2, ID2P(LANG_DELETING)); | ||
734 | |||
735 | int rc = -1; | ||
736 | |||
737 | if (to_delete_attr & ATTR_DIRECTORY) { /* true if directory */ | ||
738 | struct dirrecurse_params parm; | ||
739 | parm.append = strlcpy(parm.path, to_delete, sizeof (parm.path)); | ||
740 | |||
741 | if (parm.append < sizeof (parm.path)) { | ||
742 | cpu_boost(true); | ||
743 | rc = remove_dir(&parm); | ||
744 | cpu_boost(false); | ||
745 | } | ||
746 | } else { | ||
747 | rc = remove(to_delete); | ||
748 | } | ||
749 | |||
750 | if (rc < OPRC_SUCCESS) { | ||
751 | splash_failed(LANG_DELETE); | ||
752 | } else if (rc == OPRC_CANCELLED) { | ||
753 | splash_cancelled(); | ||
754 | } | ||
755 | |||
756 | if (rc != OPRC_NOOP) { | ||
757 | /* Could have failed after some but not all needed changes; reload */ | ||
758 | onplay_result = ONPLAY_RELOAD_DIR; | ||
759 | } | ||
760 | |||
761 | return 1; | ||
762 | } | ||
763 | |||
764 | static int rename_file(void) | ||
765 | { | ||
766 | int rc = -1; | ||
767 | char newname[MAX_PATH]; | ||
768 | const char *oldbase, *selection = selected_file; | ||
769 | |||
770 | path_basename(selection, &oldbase); | ||
771 | size_t pathlen = oldbase - selection; | ||
772 | char *newbase = newname + pathlen; | ||
773 | |||
774 | if (strmemccpy(newname, selection, sizeof (newname)) == NULL) { | ||
775 | /* Too long */ | ||
776 | } else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) { | ||
777 | rc = OPRC_CANCELLED; | ||
778 | } else if (!strcmp(oldbase, newbase)) { | ||
779 | rc = OPRC_NOOP; /* No change at all */ | ||
780 | } else if (check_new_name(newbase)) { | ||
781 | switch (relate(selection, newname)) | ||
782 | { | ||
783 | case RELATE_DIFFERENT: | ||
784 | if (file_exists(newname)) { | ||
785 | break; /* don't overwrite */ | ||
786 | } | ||
787 | /* Fall-through */ | ||
788 | case RELATE_SAME: | ||
789 | rc = rename(selection, newname); | ||
790 | break; | ||
791 | case RELATE_PREFIX: | ||
792 | default: | ||
793 | break; | ||
794 | } | ||
795 | } | ||
796 | |||
797 | if (rc < OPRC_SUCCESS) { | ||
798 | splash_failed(LANG_RENAME); | ||
799 | } else if (rc == OPRC_CANCELLED) { | ||
800 | /* splash_cancelled(); kbd_input() splashes it */ | ||
801 | } else if (rc == OPRC_SUCCESS) { | ||
802 | onplay_result = ONPLAY_RELOAD_DIR; | ||
803 | } | ||
804 | |||
805 | return 1; | ||
806 | } | ||
807 | |||
808 | static int create_dir(void) | ||
809 | { | ||
810 | int rc = -1; | ||
811 | char dirname[MAX_PATH]; | ||
812 | size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD, | ||
813 | sizeof (dirname)); | ||
814 | char *basename = dirname + pathlen; | ||
815 | |||
816 | if (pathlen >= sizeof (dirname)) { | ||
817 | /* Too long */ | ||
818 | } else if (kbd_input(basename, sizeof (dirname) - pathlen, NULL) < 0) { | ||
819 | rc = OPRC_CANCELLED; | ||
820 | } else if (check_new_name(basename)) { | ||
821 | rc = mkdir(dirname); | ||
822 | } | ||
823 | |||
824 | if (rc < OPRC_SUCCESS) { | ||
825 | splash_failed(LANG_CREATE_DIR); | ||
826 | } else if (rc == OPRC_CANCELLED) { | ||
827 | /* splash_cancelled(); kbd_input() splashes it */ | ||
828 | } else if (rc == OPRC_SUCCESS) { | ||
829 | onplay_result = ONPLAY_RELOAD_DIR; | ||
830 | } | ||
831 | |||
832 | return 1; | ||
833 | } | ||
834 | |||
835 | /* Paste a file */ | ||
836 | static int clipboard_pastefile(const char *src, const char *target, | ||
837 | unsigned int flags) | ||
838 | { | ||
839 | int rc = -1; | ||
840 | |||
841 | while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { | ||
842 | if ((flags & PASTE_OVERWRITE) || !file_exists(target)) { | ||
843 | /* Rename and possibly overwrite the file */ | ||
844 | if (poll_cancel_action(src)) { | ||
845 | rc = OPRC_CANCELLED; | ||
846 | } else { | ||
847 | rc = rename(src, target); | ||
848 | } | ||
849 | |||
850 | #ifdef HAVE_MULTIVOLUME | ||
851 | if (rc < 0 && errno == EXDEV) { | ||
852 | /* Failed because cross volume rename doesn't work; force | ||
853 | a move instead */ | ||
854 | flags |= PASTE_EXDEV; | ||
855 | break; | ||
856 | } | ||
857 | #endif /* HAVE_MULTIVOLUME */ | ||
858 | } | ||
859 | |||
860 | return rc; | ||
861 | } | ||
862 | |||
863 | /* See if we can get the plugin buffer for the file copy buffer */ | ||
864 | size_t buffersize; | ||
865 | char *buffer = (char *) plugin_get_buffer(&buffersize); | ||
866 | if (buffer == NULL || buffersize < 512) { | ||
867 | /* Not large enough, try for a disk sector worth of stack | ||
868 | instead */ | ||
869 | buffersize = 512; | ||
870 | buffer = (char *)alloca(buffersize); | ||
871 | } | ||
872 | |||
873 | if (buffer == NULL) { | ||
874 | return -1; | ||
875 | } | ||
876 | |||
877 | buffersize &= ~0x1ff; /* Round buffer size to multiple of sector | ||
878 | size */ | ||
879 | |||
880 | int src_fd = open(src, O_RDONLY); | ||
881 | if (src_fd >= 0) { | ||
882 | int oflag = O_WRONLY|O_CREAT; | ||
883 | |||
884 | if (!(flags & PASTE_OVERWRITE)) { | ||
885 | oflag |= O_EXCL; | ||
886 | } | ||
887 | |||
888 | int target_fd = open(target, oflag, 0666); | ||
889 | if (target_fd >= 0) { | ||
890 | off_t total_size = 0; | ||
891 | off_t next_cancel_test = 0; /* No excessive button polling */ | ||
892 | |||
893 | rc = OPRC_SUCCESS; | ||
894 | |||
895 | while (rc == OPRC_SUCCESS) { | ||
896 | if (total_size >= next_cancel_test) { | ||
897 | next_cancel_test = total_size + 0x10000; | ||
898 | if (poll_cancel_action(src)) { | ||
899 | rc = OPRC_CANCELLED; | ||
900 | break; | ||
901 | } | ||
902 | } | ||
903 | |||
904 | ssize_t bytesread = read(src_fd, buffer, buffersize); | ||
905 | if (bytesread <= 0) { | ||
906 | if (bytesread < 0) { | ||
907 | rc = -1; | ||
908 | } | ||
909 | /* else eof on buffer boundary; nothing to write */ | ||
910 | break; | ||
911 | } | ||
912 | |||
913 | ssize_t byteswritten = write(target_fd, buffer, bytesread); | ||
914 | if (byteswritten < bytesread) { | ||
915 | /* Some I/O error */ | ||
916 | rc = -1; | ||
917 | break; | ||
918 | } | ||
919 | |||
920 | total_size += byteswritten; | ||
921 | |||
922 | if (bytesread < (ssize_t)buffersize) { | ||
923 | /* EOF with trailing bytes */ | ||
924 | break; | ||
925 | } | ||
926 | } | ||
927 | |||
928 | if (rc == OPRC_SUCCESS) { | ||
929 | /* If overwriting, set the correct length if original was | ||
930 | longer */ | ||
931 | rc = ftruncate(target_fd, total_size); | ||
932 | } | ||
933 | |||
934 | close(target_fd); | ||
935 | |||
936 | if (rc != OPRC_SUCCESS) { | ||
937 | /* Copy failed. Cleanup. */ | ||
938 | remove(target); | ||
939 | } | ||
940 | } | ||
941 | |||
942 | close(src_fd); | ||
943 | } | ||
944 | |||
945 | if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) { | ||
946 | /* Remove the source file */ | ||
947 | rc = remove(src); | ||
948 | } | ||
949 | |||
950 | return rc; | ||
951 | } | ||
952 | |||
953 | /* Paste a directory */ | ||
954 | static int clipboard_pastedirectory(struct dirrecurse_params *src, | ||
955 | struct dirrecurse_params *target, | ||
956 | unsigned int flags) | ||
957 | { | ||
958 | int rc = -1; | ||
959 | |||
960 | while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { | ||
961 | if ((flags & PASTE_OVERWRITE) || !file_exists(target->path)) { | ||
962 | /* Just try to move the directory */ | ||
963 | if (poll_cancel_action(src->path)) { | ||
964 | rc = OPRC_CANCELLED; | ||
965 | } else { | ||
966 | rc = rename(src->path, target->path); | ||
967 | } | ||
968 | |||
969 | if (rc < 0) { | ||
970 | int errnum = errno; | ||
971 | if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { | ||
972 | /* Directory is not empty thus rename() will not do a quick | ||
973 | overwrite */ | ||
974 | break; | ||
975 | } | ||
976 | #ifdef HAVE_MULTIVOLUME | ||
977 | else if (errnum == EXDEV) { | ||
978 | /* Failed because cross volume rename doesn't work; force | ||
979 | a move instead */ | ||
980 | flags |= PASTE_EXDEV; | ||
981 | break; | ||
982 | } | ||
983 | #endif /* HAVE_MULTIVOLUME */ | ||
984 | } | ||
985 | } | ||
986 | |||
987 | return rc; | ||
988 | } | ||
989 | |||
990 | DIR *srcdir = opendir(src->path); | ||
991 | |||
992 | if (srcdir) { | ||
993 | /* Make a directory to copy things to */ | ||
994 | rc = mkdir(target->path); | ||
995 | if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) { | ||
996 | /* Exists and overwrite was approved */ | ||
997 | rc = OPRC_SUCCESS; | ||
998 | } | ||
999 | } | ||
1000 | |||
1001 | size_t srcap = src->append, targetap = target->append; | ||
1002 | |||
1003 | /* Walk through the directory content; this loop will exit as soon as | ||
1004 | there's a problem */ | ||
1005 | while (rc == OPRC_SUCCESS) { | ||
1006 | errno = 0; /* Distinguish failure from eod */ | ||
1007 | struct dirent *entry = readdir(srcdir); | ||
1008 | if (!entry) { | ||
1009 | if (errno) { | ||
1010 | rc = -1; | ||
1011 | } | ||
1012 | break; | ||
1013 | } | ||
1014 | |||
1015 | struct dirinfo info = dir_get_info(srcdir, entry); | ||
1016 | if ((info.attribute & ATTR_DIRECTORY) && | ||
1017 | is_dotdir_name(entry->d_name)) { | ||
1018 | continue; /* Skip these */ | ||
1019 | } | ||
1020 | |||
1021 | /* Append names to current directories */ | ||
1022 | src->append = srcap + | ||
1023 | path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name, | ||
1024 | sizeof(src->path) - srcap); | ||
1025 | |||
1026 | target->append = targetap + | ||
1027 | path_append(&target->path[targetap], PA_SEP_HARD, entry->d_name, | ||
1028 | sizeof (target->path) - targetap); | ||
1029 | |||
1030 | if (src->append >= sizeof (src->path) || | ||
1031 | target->append >= sizeof (target->path)) { | ||
1032 | rc = -1; /* No space left in buffer */ | ||
1033 | break; | ||
1034 | } | ||
1035 | |||
1036 | if (poll_cancel_action(src->path)) { | ||
1037 | rc = OPRC_CANCELLED; | ||
1038 | break; | ||
1039 | } | ||
1040 | |||
1041 | DEBUGF("Copy %s to %s\n", src->path, target->path); | ||
1042 | |||
1043 | if (info.attribute & ATTR_DIRECTORY) { | ||
1044 | /* Copy/move a subdirectory */ | ||
1045 | rc = clipboard_pastedirectory(src, target, flags); /* recursion */ | ||
1046 | } else { | ||
1047 | /* Copy/move a file */ | ||
1048 | rc = clipboard_pastefile(src->path, target->path, flags); | ||
1049 | } | ||
1050 | |||
1051 | /* Remove basenames we added above */ | ||
1052 | src->path[srcap] = target->path[targetap] = '\0'; | ||
1053 | } | ||
1054 | |||
1055 | if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) { | ||
1056 | /* Remove the now empty directory */ | ||
1057 | rc = rmdir(src->path); | ||
1058 | } | ||
1059 | |||
1060 | closedir(srcdir); | ||
1061 | return rc; | ||
1062 | } | 578 | } |
1063 | 579 | ||
1064 | static bool clipboard_cut(void) | 580 | static bool clipboard_cut(void) |
@@ -1079,82 +595,27 @@ static int clipboard_paste(void) | |||
1079 | if (!clipboard.path[0]) | 595 | if (!clipboard.path[0]) |
1080 | return 1; | 596 | return 1; |
1081 | 597 | ||
1082 | int rc = -1; | 598 | int rc = copy_move_fileobject(clipboard.path, getcwd(NULL, 0), clipboard.flags); |
1083 | |||
1084 | struct dirrecurse_params src, target; | ||
1085 | unsigned int flags = clipboard.flags; | ||
1086 | |||
1087 | /* Figure out the name of the selection */ | ||
1088 | const char *nameptr; | ||
1089 | path_basename(clipboard.path, &nameptr); | ||
1090 | 599 | ||
1091 | /* Final target is current directory plus name of selection */ | ||
1092 | target.append = path_append(target.path, getcwd(NULL, 0), | ||
1093 | nameptr, sizeof (target.path)); | ||
1094 | 600 | ||
1095 | switch (target.append < sizeof (target.path) ? | 601 | clear_screen_buffer(true); |
1096 | relate(clipboard.path, target.path) : -1) | ||
1097 | { | ||
1098 | case RELATE_SAME: | ||
1099 | rc = OPRC_NOOP; | ||
1100 | break; | ||
1101 | |||
1102 | case RELATE_DIFFERENT: | ||
1103 | if (file_exists(target.path)) { | ||
1104 | /* If user chooses not to overwrite, cancel */ | ||
1105 | if (confirm_overwrite_yesno() == YESNO_NO) { | ||
1106 | rc = OPRC_NOOVERWRT; | ||
1107 | break; | ||
1108 | } | ||
1109 | |||
1110 | flags |= PASTE_OVERWRITE; | ||
1111 | } | ||
1112 | |||
1113 | clear_display(true); | ||
1114 | splash(HZ/2, (flags & PASTE_COPY) ? ID2P(LANG_COPYING) : | ||
1115 | ID2P(LANG_MOVING)); | ||
1116 | |||
1117 | /* Now figure out what we're doing */ | ||
1118 | cpu_boost(true); | ||
1119 | |||
1120 | if (clipboard.attr & ATTR_DIRECTORY) { | ||
1121 | /* Copy or move a subdirectory */ | ||
1122 | src.append = strlcpy(src.path, clipboard.path, | ||
1123 | sizeof (src.path)); | ||
1124 | if (src.append < sizeof (src.path)) { | ||
1125 | rc = clipboard_pastedirectory(&src, &target, flags); | ||
1126 | } | ||
1127 | } else { | ||
1128 | /* Copy or move a file */ | ||
1129 | rc = clipboard_pastefile(clipboard.path, target.path, flags); | ||
1130 | } | ||
1131 | |||
1132 | cpu_boost(false); | ||
1133 | break; | ||
1134 | |||
1135 | case RELATE_PREFIX: | ||
1136 | default: /* Some other relation / failure */ | ||
1137 | break; | ||
1138 | } | ||
1139 | |||
1140 | clear_display(true); | ||
1141 | 602 | ||
1142 | switch (rc) | 603 | switch (rc) |
1143 | { | 604 | { |
1144 | case OPRC_CANCELLED: | 605 | case FORC_CANCELLED: |
1145 | splash_cancelled(); | 606 | splash_cancelled(); |
1146 | /* Fallthrough */ | 607 | /* Fallthrough */ |
1147 | case OPRC_SUCCESS: | 608 | case FORC_SUCCESS: |
1148 | onplay_result = ONPLAY_RELOAD_DIR; | 609 | onplay_result = ONPLAY_RELOAD_DIR; |
1149 | /* Fallthrough */ | 610 | /* Fallthrough */ |
1150 | case OPRC_NOOP: | 611 | case FORC_NOOP: |
1151 | clipboard_clear_selection(&clipboard); | 612 | clipboard_clear_selection(&clipboard); |
1152 | /* Fallthrough */ | 613 | /* Fallthrough */ |
1153 | case OPRC_NOOVERWRT: | 614 | case FORC_NOOVERWRT: |
1154 | break; | 615 | break; |
1155 | default: | 616 | default: |
1156 | if (rc < OPRC_SUCCESS) { | 617 | if (rc < FORC_SUCCESS) { |
1157 | splash_failed(LANG_PASTE); | 618 | splash_failed(LANG_PASTE, rc); |
1158 | onplay_result = ONPLAY_RELOAD_DIR; | 619 | onplay_result = ONPLAY_RELOAD_DIR; |
1159 | } | 620 | } |
1160 | } | 621 | } |
@@ -1249,13 +710,57 @@ MENUITEM_FUNCTION(pitch_screen_item, 0, ID2P(LANG_PITCH), | |||
1249 | gui_syncpitchscreen_run, NULL, Icon_Audio); | 710 | gui_syncpitchscreen_run, NULL, Icon_Audio); |
1250 | #endif | 711 | #endif |
1251 | 712 | ||
713 | static int clipboard_delete_selected_fileobject(void) | ||
714 | { | ||
715 | int rc = delete_fileobject(selected_file); | ||
716 | if (rc < FORC_SUCCESS) { | ||
717 | splash_failed(LANG_DELETE, rc); | ||
718 | } else if (rc == FORC_CANCELLED) { | ||
719 | splash_cancelled(); | ||
720 | } | ||
721 | if (rc != FORC_NOOP) { | ||
722 | /* Could have failed after some but not all needed changes; reload */ | ||
723 | onplay_result = ONPLAY_RELOAD_DIR; | ||
724 | } | ||
725 | return 1; | ||
726 | } | ||
727 | |||
728 | static void show_result(int rc, int lang_what) | ||
729 | { | ||
730 | if (rc < FORC_SUCCESS) { | ||
731 | splash_failed(lang_what, rc); | ||
732 | } else if (rc == FORC_CANCELLED) { | ||
733 | /* splash_cancelled(); kbd_input() splashes it */ | ||
734 | } else if (rc == FORC_SUCCESS) { | ||
735 | onplay_result = ONPLAY_RELOAD_DIR; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | static int clipboard_create_dir(void) | ||
740 | { | ||
741 | int rc = create_dir(); | ||
742 | |||
743 | show_result(rc, LANG_CREATE_DIR); | ||
744 | |||
745 | return 1; | ||
746 | } | ||
747 | |||
748 | static int clipboard_rename_selected_file(void) | ||
749 | { | ||
750 | int rc = rename_file(selected_file); | ||
751 | |||
752 | show_result(rc, LANG_RENAME); | ||
753 | |||
754 | return 1; | ||
755 | } | ||
756 | |||
1252 | /* CONTEXT_[TREE|ID3DB] items */ | 757 | /* CONTEXT_[TREE|ID3DB] items */ |
1253 | static int clipboard_callback(int action, | 758 | static int clipboard_callback(int action, |
1254 | const struct menu_item_ex *this_item, | 759 | const struct menu_item_ex *this_item, |
1255 | struct gui_synclist *this_list); | 760 | struct gui_synclist *this_list); |
1256 | 761 | ||
1257 | MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME), | 762 | MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME), |
1258 | rename_file, clipboard_callback, Icon_NOICON); | 763 | clipboard_rename_selected_file, clipboard_callback, Icon_NOICON); |
1259 | MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT), | 764 | MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT), |
1260 | clipboard_cut, clipboard_callback, Icon_NOICON); | 765 | clipboard_cut, clipboard_callback, Icon_NOICON); |
1261 | MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY), | 766 | MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY), |
@@ -1263,11 +768,11 @@ MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY), | |||
1263 | MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE), | 768 | MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE), |
1264 | clipboard_paste, clipboard_callback, Icon_NOICON); | 769 | clipboard_paste, clipboard_callback, Icon_NOICON); |
1265 | MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE), | 770 | MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE), |
1266 | delete_file_dir, clipboard_callback, Icon_NOICON); | 771 | clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON); |
1267 | MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR), | 772 | MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR), |
1268 | delete_file_dir, clipboard_callback, Icon_NOICON); | 773 | clipboard_delete_selected_fileobject, clipboard_callback, Icon_NOICON); |
1269 | MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR), | 774 | MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR), |
1270 | create_dir, clipboard_callback, Icon_NOICON); | 775 | clipboard_create_dir, clipboard_callback, Icon_NOICON); |
1271 | 776 | ||
1272 | /* other items */ | 777 | /* other items */ |
1273 | static bool list_viewers(void) | 778 | static bool list_viewers(void) |
@@ -1337,12 +842,18 @@ MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES), | |||
1337 | onplay_add_to_shortcuts, | 842 | onplay_add_to_shortcuts, |
1338 | clipboard_callback, Icon_NOICON); | 843 | clipboard_callback, Icon_NOICON); |
1339 | 844 | ||
845 | static void set_dir_helper(char* dirnamebuf, size_t bufsz) | ||
846 | { | ||
847 | path_append(dirnamebuf, selected_file, PA_SEP_HARD, bufsz); | ||
848 | settings_save(); | ||
849 | } | ||
850 | |||
1340 | #if LCD_DEPTH > 1 | 851 | #if LCD_DEPTH > 1 |
1341 | static bool set_backdrop(void) | 852 | static bool set_backdrop(void) |
1342 | { | 853 | { |
1343 | path_append(global_settings.backdrop_file, selected_file, | 854 | set_dir_helper(global_settings.backdrop_file, |
1344 | PA_SEP_HARD, sizeof(global_settings.backdrop_file)); | 855 | sizeof(global_settings.backdrop_file)); |
1345 | settings_save(); | 856 | |
1346 | skin_backdrop_load_setting(); | 857 | skin_backdrop_load_setting(); |
1347 | skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN)); | 858 | skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN)); |
1348 | return true; | 859 | return true; |
@@ -1353,9 +864,8 @@ MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP), | |||
1353 | #ifdef HAVE_RECORDING | 864 | #ifdef HAVE_RECORDING |
1354 | static bool set_recdir(void) | 865 | static bool set_recdir(void) |
1355 | { | 866 | { |
1356 | path_append(global_settings.rec_directory, selected_file, | 867 | set_dir_helper(global_settings.rec_directory, |
1357 | PA_SEP_HARD, sizeof(global_settings.rec_directory)); | 868 | sizeof(global_settings.rec_directory)); |
1358 | settings_save(); | ||
1359 | return false; | 869 | return false; |
1360 | } | 870 | } |
1361 | MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR), | 871 | MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR), |
@@ -1363,10 +873,8 @@ MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR), | |||
1363 | #endif | 873 | #endif |
1364 | static bool set_startdir(void) | 874 | static bool set_startdir(void) |
1365 | { | 875 | { |
1366 | path_append(global_settings.start_directory, selected_file, | 876 | set_dir_helper(global_settings.start_directory, |
1367 | PA_SEP_HARD, sizeof(global_settings.start_directory)); | 877 | sizeof(global_settings.start_directory)); |
1368 | |||
1369 | settings_save(); | ||
1370 | return false; | 878 | return false; |
1371 | } | 879 | } |
1372 | MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR), | 880 | MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR), |
@@ -1384,16 +892,14 @@ MENUITEM_FUNCTION(set_catalogdir_item, 0, ID2P(LANG_PLAYLIST_DIR), | |||
1384 | #ifdef HAVE_TAGCACHE | 892 | #ifdef HAVE_TAGCACHE |
1385 | static bool set_databasedir(void) | 893 | static bool set_databasedir(void) |
1386 | { | 894 | { |
1387 | path_append(global_settings.tagcache_db_path, selected_file, | ||
1388 | PA_SEP_SOFT, sizeof(global_settings.tagcache_db_path)); | ||
1389 | |||
1390 | struct tagcache_stat *tc_stat = tagcache_get_stat(); | 895 | struct tagcache_stat *tc_stat = tagcache_get_stat(); |
1391 | if (strcasecmp(global_settings.tagcache_db_path, tc_stat->db_path)) | 896 | if (strcasecmp(selected_file, tc_stat->db_path)) |
1392 | { | 897 | { |
1393 | splash(HZ, ID2P(LANG_PLEASE_REBOOT)); | 898 | splash(HZ, ID2P(LANG_PLEASE_REBOOT)); |
1394 | } | 899 | } |
1395 | 900 | ||
1396 | settings_save(); | 901 | set_dir_helper(global_settings.tagcache_db_path, |
902 | sizeof(global_settings.tagcache_db_path)); | ||
1397 | return false; | 903 | return false; |
1398 | } | 904 | } |
1399 | MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR), | 905 | MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR), |
@@ -1589,7 +1095,7 @@ static bool hotkey_delete_item(void) | |||
1589 | return false; | 1095 | return false; |
1590 | #endif | 1096 | #endif |
1591 | 1097 | ||
1592 | return delete_file_dir(); | 1098 | return clipboard_delete_selected_fileobject(); |
1593 | } | 1099 | } |
1594 | 1100 | ||
1595 | static bool hotkey_open_with(void) | 1101 | static bool hotkey_open_with(void) |