| 766 | } |
| 767 | |
| 768 | void View::doPaste(const std::string& src, bool is_cut, const std::string& dst) { |
| 769 | bool success = false; |
| 770 | bool src_delete_failed = false; |
| 771 | if (is_cut) { |
| 772 | auto lock = file::getLock(src); |
| 773 | lock->lock(); |
| 774 | success = (rename(src.c_str(), dst.c_str()) == 0); |
| 775 | lock->unlock(); |
| 776 | if (!success) { |
| 777 | // Fallback for cross-filesystem moves: copy then delete. |
| 778 | // Only mark success if both halves succeed — if the source removal |
| 779 | // fails we leave success=false so the clipboard is preserved and |
| 780 | // the error is surfaced; the user must remove the source manually. |
| 781 | if (copyRecursive(src, dst)) { |
| 782 | if (file::deleteRecursively(src)) { |
| 783 | success = true; |
| 784 | } else { |
| 785 | src_delete_failed = true; |
| 786 | LOGGER.error("Cut: copied \"{}\" to \"{}\" but failed to remove source — manual cleanup required", src, dst); |
| 787 | } |
| 788 | } |
| 789 | } |
| 790 | } else { |
| 791 | success = copyRecursive(src, dst); |
| 792 | } |
| 793 | |
| 794 | const std::string filename = file::getLastPathSegment(src); |
| 795 | if (success) { |
| 796 | LOGGER.info("{} \"{}\" to \"{}\"", is_cut ? "Moved" : "Copied", src, dst); |
| 797 | if (is_cut) { |
| 798 | state->clearClipboard(); |
| 799 | } |
| 800 | } else if (src_delete_failed) { |
| 801 | state->setPendingAction(State::ActionNone); // prevent re-trigger on dialog dismiss |
| 802 | alertdialog::start("Move incomplete", "\"" + filename + "\" was copied but the original could not be removed.\nPlease delete it manually."); |
| 803 | } else { |
| 804 | LOGGER.error("Failed to {} \"{}\" to \"{}\"", is_cut ? "move" : "copy", src, dst); |
| 805 | state->setPendingAction(State::ActionNone); // prevent re-trigger on dialog dismiss |
| 806 | alertdialog::start( |
| 807 | std::string("Failed to ") + (is_cut ? "move" : "copy"), |
| 808 | "\"" + filename + "\" could not be " + (is_cut ? "moved." : "copied.") |
| 809 | ); |
| 810 | } |
| 811 | |
| 812 | state->setEntriesForPath(state->getCurrentPath()); |
| 813 | update(); |
| 814 | } |
| 815 | |
| 816 | void View::deinit(const AppContext& appContext) { |
| 817 | lv_obj_remove_event_cb(dir_entry_list, dirEntryListScrollBeginCallback); |
nothing calls this directly
no test coverage detected