diff options
-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) |