/* ============================================================================== This file is part of the JUCE 7 technical preview. Copyright (c) 2022 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For the technical preview this file cannot be licensed commercially. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { //============================================================================== class FileBasedDocument::Pimpl { private: //============================================================================== class SafeParentPointer { public: SafeParentPointer (Pimpl* parent, bool isAsync) : ptr (parent), shouldCheck (isAsync) {} Pimpl* operator->() const noexcept { return ptr.get(); } bool operator== (Pimpl* object) const noexcept { return ptr.get() == object; } bool operator!= (Pimpl* object) const noexcept { return ptr.get() != object; } bool shouldExitAsyncCallback() const noexcept { return shouldCheck && ptr == nullptr; } private: WeakReference ptr; bool shouldCheck = false; }; public: //============================================================================== Pimpl (FileBasedDocument& parent_, const String& fileExtension_, const String& fileWildcard_, const String& openFileDialogTitle_, const String& saveFileDialogTitle_) : document (parent_), fileExtension (fileExtension_), fileWildcard (fileWildcard_), openFileDialogTitle (openFileDialogTitle_), saveFileDialogTitle (saveFileDialogTitle_) { } //============================================================================== bool hasChangedSinceSaved() const { return changedSinceSave; } void setChangedFlag (bool hasChanged) { if (changedSinceSave != hasChanged) { changedSinceSave = hasChanged; document.sendChangeMessage(); } } void changed() { changedSinceSave = true; document.sendChangeMessage(); } //============================================================================== Result loadFrom (const File& newFile, bool showMessageOnFailure, bool showWaitCursor = true) { SafeParentPointer parent { this, false }; auto result = Result::ok(); loadFromImpl (parent, newFile, showMessageOnFailure, showWaitCursor, [this] (const File& file, const auto& callback) { callback (document.loadDocument (file)); }, [&result] (Result r) { result = r; }); return result; } void loadFromAsync (const File& newFile, bool showMessageOnFailure, std::function callback) { SafeParentPointer parent { this, true }; loadFromImpl (parent, newFile, showMessageOnFailure, false, [parent] (const File& file, auto cb) { if (parent != nullptr) parent->document.loadDocumentAsync (file, std::move (cb)); }, std::move (callback)); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED Result loadFromUserSpecifiedFile (bool showMessageOnFailure) { FileChooser fc (openFileDialogTitle, document.getLastDocumentOpened(), fileWildcard); if (fc.browseForFileToOpen()) return loadFrom (fc.getResult(), showMessageOnFailure); return Result::fail (TRANS ("User cancelled")); } #endif void loadFromUserSpecifiedFileAsync (const bool showMessageOnFailure, std::function callback) { asyncFc = std::make_unique (openFileDialogTitle, document.getLastDocumentOpened(), fileWildcard); asyncFc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, [this, showMessageOnFailure, callback = std::move (callback)] (const FileChooser& fc) { auto chosenFile = fc.getResult(); if (chosenFile == File{}) { if (callback != nullptr) callback (Result::fail (TRANS ("User cancelled"))); return; } WeakReference parent { this }; loadFromAsync (chosenFile, showMessageOnFailure, [parent, callback] (Result result) { if (parent != nullptr && callback != nullptr) callback (result); }); asyncFc = nullptr; }); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult save (bool askUserForFileIfNotSpecified, bool showMessageOnFailure) { return saveAs (documentFile, false, askUserForFileIfNotSpecified, showMessageOnFailure); } #endif void saveAsync (bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function callback) { saveAsAsync (documentFile, false, askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback)); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult saveIfNeededAndUserAgrees() { SafeParentPointer parent { this, false }; SaveResult result; saveIfNeededAndUserAgreesImpl (parent, [&result] (SaveResult r) { result = r; }, AskToSaveChangesSync { *this }, SaveSync { *this }); return result; } #endif void saveIfNeededAndUserAgreesAsync (std::function callback) { SafeParentPointer parent { this, true }; saveIfNeededAndUserAgreesImpl (parent, std::move (callback), [] (SafeParentPointer ptr, auto cb) { if (ptr != nullptr) ptr->askToSaveChanges (ptr, std::move (cb)); }, [parent] (bool askUserForFileIfNotSpecified, bool showMessageOnFailure, auto cb) { if (parent != nullptr) parent->saveAsync (askUserForFileIfNotSpecified, showMessageOnFailure, std::move (cb)); }); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult saveAs (const File& newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, bool showWaitCursor = true) { SafeParentPointer parent { this, false }; SaveResult result{}; saveAsSyncImpl (parent, newFile, warnAboutOverwritingExistingFiles, askUserForFileIfNotSpecified, showMessageOnFailure, [&result] (SaveResult r) { result = r; }, showWaitCursor); return result; } #endif void saveAsAsync (const File& newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function callback) { SafeParentPointer parent { this, true }; saveAsAsyncImpl (parent, newFile, warnAboutOverwritingExistingFiles, askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback), false); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult saveAsInteractive (bool warnAboutOverwritingExistingFiles) { SafeParentPointer parent { this, false }; SaveResult result{}; saveAsInteractiveSyncImpl (parent, warnAboutOverwritingExistingFiles, [&result] (SaveResult r) { result = r; }); return result; } #endif void saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles, std::function callback) { SafeParentPointer parent { this, true }; saveAsInteractiveAsyncImpl (parent, warnAboutOverwritingExistingFiles, std::move (callback)); } //============================================================================== const File& getFile() const { return documentFile; } void setFile (const File& newFile) { if (documentFile != newFile) { documentFile = newFile; changed(); } } //============================================================================== const String& getFileExtension() const { return fileExtension; } private: //============================================================================== template void loadFromImpl (SafeParentPointer parent, const File& newFile, bool showMessageOnFailure, bool showWaitCursor, DoLoadDocument&& doLoadDocument, std::function completed) { if (parent.shouldExitAsyncCallback()) return; if (showWaitCursor) MouseCursor::showWaitCursor(); auto oldFile = documentFile; documentFile = newFile; auto tidyUp = [parent, newFile, oldFile, showMessageOnFailure, showWaitCursor, completed] (Result result) { if (parent.shouldExitAsyncCallback()) return; parent->documentFile = oldFile; if (showWaitCursor) MouseCursor::hideWaitCursor(); if (showMessageOnFailure) AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS ("Failed to open file..."), TRANS ("There was an error while trying to load the file: FLNM") .replace ("FLNM", "\n" + newFile.getFullPathName()) + "\n\n" + result.getErrorMessage()); if (completed != nullptr) completed (result); }; if (newFile.existsAsFile()) { auto afterLoading = [parent, showWaitCursor, newFile, completed = std::move (completed), tidyUp] (Result result) { if (result.wasOk()) { parent->setChangedFlag (false); if (showWaitCursor) MouseCursor::hideWaitCursor(); parent->document.setLastDocumentOpened (newFile); if (completed != nullptr) completed (result); return; } tidyUp (result); }; doLoadDocument (newFile, std::move (afterLoading)); return; } tidyUp (Result::fail (TRANS ("The file doesn't exist"))); } //============================================================================== template void saveIfNeededAndUserAgreesImpl (SafeParentPointer parent, std::function completed, DoAskToSaveChanges&& doAskToSaveChanges, DoSave&& doSave) { if (parent.shouldExitAsyncCallback()) return; if (! hasChangedSinceSaved()) { if (completed != nullptr) completed (savedOk); return; } auto afterAsking = [doSave = std::forward (doSave), completed = std::move (completed)] (SafeParentPointer ptr, int alertResult) { if (ptr.shouldExitAsyncCallback()) return; switch (alertResult) { case 1: // save changes doSave (true, true, [ptr, completed] (SaveResult result) { if (ptr.shouldExitAsyncCallback()) return; if (completed != nullptr) completed (result); }); return; case 2: // discard changes if (completed != nullptr) completed (savedOk); return; } if (completed != nullptr) completed (userCancelledSave); }; doAskToSaveChanges (parent, std::move (afterAsking)); } //============================================================================== int askToSaveChanges (SafeParentPointer parent, std::function callback) { auto* modalCallback = callback == nullptr ? nullptr : ModalCallbackFunction::create ([parent, callback = std::move (callback)] (int alertResult) { if (parent != nullptr) callback (parent, alertResult); }); return AlertWindow::showYesNoCancelBox (MessageBoxIconType::QuestionIcon, TRANS ("Closing document..."), TRANS ("Do you want to save the changes to \"DCNM\"?") .replace ("DCNM", document.getDocumentTitle()), TRANS ("Save"), TRANS ("Discard changes"), TRANS ("Cancel"), nullptr, modalCallback); } //============================================================================== template void saveInternal (SafeParentPointer parent, const File& newFile, bool showMessageOnFailure, bool showWaitCursor, std::function afterSave, DoSaveDocument&& doSaveDocument) { if (showWaitCursor) MouseCursor::showWaitCursor(); auto oldFile = documentFile; documentFile = newFile; doSaveDocument (newFile, [parent, showMessageOnFailure, showWaitCursor, oldFile, newFile, afterSave = std::move (afterSave)] (Result result) { if (parent.shouldExitAsyncCallback()) { if (showWaitCursor) MouseCursor::hideWaitCursor(); return; } if (result.wasOk()) { parent->setChangedFlag (false); if (showWaitCursor) MouseCursor::hideWaitCursor(); parent->document.sendChangeMessage(); // because the filename may have changed if (afterSave != nullptr) afterSave (savedOk); return; } parent->documentFile = oldFile; if (showWaitCursor) MouseCursor::hideWaitCursor(); if (showMessageOnFailure) AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, TRANS ("Error writing to file..."), TRANS ("An error occurred while trying to save \"DCNM\" to the file: FLNM") .replace ("DCNM", parent->document.getDocumentTitle()) .replace ("FLNM", "\n" + newFile.getFullPathName()) + "\n\n" + result.getErrorMessage()); parent->document.sendChangeMessage(); // because the filename may have changed if (afterSave != nullptr) afterSave (failedToWriteToFile); }); } template void saveAsImpl (SafeParentPointer parent, const File& newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function callback, bool showWaitCursor, DoSaveAsInteractive&& doSaveAsInteractive, DoAskToOverwriteFile&& doAskToOverwriteFile, DoSaveDocument&& doSaveDocument) { if (parent.shouldExitAsyncCallback()) return; if (newFile == File()) { if (askUserForFileIfNotSpecified) { doSaveAsInteractive (parent, true, std::move (callback)); return; } // can't save to an unspecified file jassertfalse; if (callback != nullptr) callback (failedToWriteToFile); return; } auto saveInternalHelper = [parent, callback, newFile, showMessageOnFailure, showWaitCursor, doSaveDocument = std::forward (doSaveDocument)] { if (! parent.shouldExitAsyncCallback()) parent->saveInternal (parent, newFile, showMessageOnFailure, showWaitCursor, callback, doSaveDocument); }; if (warnAboutOverwritingExistingFiles && newFile.exists()) { auto afterAsking = [callback = std::move (callback), saveInternalHelper] (SafeParentPointer ptr, bool shouldOverwrite) { if (ptr.shouldExitAsyncCallback()) return; if (shouldOverwrite) saveInternalHelper(); else if (callback != nullptr) callback (userCancelledSave); }; doAskToOverwriteFile (parent, newFile, std::move (afterAsking)); return; } saveInternalHelper(); } void saveAsAsyncImpl (SafeParentPointer parent, const File& newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function callback, bool showWaitCursor) { saveAsImpl (parent, newFile, warnAboutOverwritingExistingFiles, askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback), showWaitCursor, [] (SafeParentPointer ptr, bool warnAboutOverwriting, auto cb) { if (ptr != nullptr) ptr->saveAsInteractiveAsyncImpl (ptr, warnAboutOverwriting, std::move (cb)); }, [] (SafeParentPointer ptr, const File& destination, std::function cb) { if (ptr != nullptr) ptr->askToOverwriteFile (ptr, destination, std::move (cb)); }, [parent] (const File& destination, std::function cb) { if (parent != nullptr) parent->document.saveDocumentAsync (destination, std::move (cb)); }); } //============================================================================== void saveAsInteractiveAsyncImpl (SafeParentPointer parent, bool warnAboutOverwritingExistingFiles, std::function callback) { if (parent == nullptr) return; saveAsInteractiveImpl (parent, warnAboutOverwritingExistingFiles, std::move (callback), [] (SafeParentPointer ptr, bool warnAboutOverwriting, auto cb) { if (ptr != nullptr) ptr->getSaveAsFilenameAsync (ptr, warnAboutOverwriting, std::move (cb)); }, [] (SafeParentPointer ptr, const File& newFile, bool warnAboutOverwriting, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, auto cb, bool showWaitCursor) { if (ptr != nullptr) ptr->saveAsAsyncImpl (ptr, newFile, warnAboutOverwriting, askUserForFileIfNotSpecified, showMessageOnFailure, std::move (cb), showWaitCursor); }, [] (SafeParentPointer ptr, const File& destination, auto cb) { if (ptr != nullptr) ptr->askToOverwriteFile (ptr, destination, std::move (cb)); }); } //============================================================================== bool askToOverwriteFile (SafeParentPointer parent, const File& newFile, std::function callback) { if (parent == nullptr) return false; auto* modalCallback = callback == nullptr ? nullptr : ModalCallbackFunction::create ([parent, callback = std::move (callback)] (int r) { if (parent != nullptr) callback (parent, r == 1); }); return AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, TRANS ("File already exists"), TRANS ("There's already a file called: FLNM") .replace ("FLNM", newFile.getFullPathName()) + "\n\n" + TRANS ("Are you sure you want to overwrite it?"), TRANS ("Overwrite"), TRANS ("Cancel"), nullptr, modalCallback); } //============================================================================== void getSaveAsFilenameAsync (SafeParentPointer parent, bool warnAboutOverwritingExistingFiles, std::function callback) { asyncFc = getInteractiveFileChooser(); auto flags = FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles; if (warnAboutOverwritingExistingFiles) flags |= FileBrowserComponent::warnAboutOverwriting; asyncFc->launchAsync (flags, [parent, callback = std::move (callback)] (const FileChooser& fc) { callback (parent, fc.getResult()); }); } //============================================================================== template void saveAsInteractiveImpl (SafeParentPointer parent, bool warnAboutOverwritingExistingFiles, std::function callback, DoSelectFilename&& doSelectFilename, DoSaveAs&& doSaveAs, DoAskToOverwriteFile&& doAskToOverwriteFile) { doSelectFilename (parent, warnAboutOverwritingExistingFiles, [doSaveAs = std::forward (doSaveAs), doAskToOverwriteFile = std::forward (doAskToOverwriteFile), callback = std::move (callback)] (SafeParentPointer parentPtr, File chosen) { if (parentPtr.shouldExitAsyncCallback()) return; if (chosen == File{}) { if (callback != nullptr) callback (userCancelledSave); return; } auto updateAndSaveAs = [parentPtr, doSaveAs, callback] (const File& chosenFile) { if (parentPtr.shouldExitAsyncCallback()) return; parentPtr->document.setLastDocumentOpened (chosenFile); doSaveAs (parentPtr, chosenFile, false, false, true, callback, false); }; if (chosen.getFileExtension().isEmpty()) { chosen = chosen.withFileExtension (parentPtr->fileExtension); if (chosen.exists()) { auto afterAsking = [chosen, updateAndSaveAs, callback] (SafeParentPointer overwritePtr, bool overwrite) { if (overwritePtr.shouldExitAsyncCallback()) return; if (overwrite) updateAndSaveAs (chosen); else if (callback != nullptr) callback (userCancelledSave); }; doAskToOverwriteFile (parentPtr, chosen, std::move (afterAsking)); return; } } updateAndSaveAs (chosen); }); } //============================================================================== std::unique_ptr getInteractiveFileChooser() { auto f = documentFile.existsAsFile() ? documentFile : document.getLastDocumentOpened(); auto legalFilename = File::createLegalFileName (document.getDocumentTitle()); if (legalFilename.isEmpty()) legalFilename = "unnamed"; f = (f.existsAsFile() || f.getParentDirectory().isDirectory()) ? f.getSiblingFile (legalFilename) : File::getSpecialLocation (File::userDocumentsDirectory).getChildFile (legalFilename); f = document.getSuggestedSaveAsFile (f); return std::make_unique (saveFileDialogTitle, f, fileWildcard); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED struct SaveAsInteractiveSyncImpl { template void operator() (Ts&&... ts) const noexcept { p.saveAsInteractiveSyncImpl (std::forward (ts)...); } Pimpl& p; }; struct AskToOverwriteFileSync { template void operator() (Ts&&... ts) const noexcept { p.askToOverwriteFileSync (std::forward (ts)...); } Pimpl& p; }; struct AskToSaveChangesSync { template void operator() (Ts&&... ts) const noexcept { p.askToSaveChangesSync (std::forward (ts)...); } Pimpl& p; }; struct SaveSync { template void operator() (Ts&&... ts) const noexcept { p.saveSync (std::forward (ts)...); } Pimpl& p; }; struct GetSaveAsFilenameSync { template void operator() (Ts&&... ts) const noexcept { p.getSaveAsFilenameSync (std::forward (ts)...); } Pimpl& p; }; struct SaveAsSyncImpl { template void operator() (Ts&&... ts) const noexcept { p.saveAsSyncImpl (std::forward (ts)...); } Pimpl& p; }; //============================================================================== void saveAsSyncImpl (SafeParentPointer parent, const File& newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function callback, bool showWaitCursor) { saveAsImpl (parent, newFile, warnAboutOverwritingExistingFiles, askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback), showWaitCursor, SaveAsInteractiveSyncImpl { *this }, AskToOverwriteFileSync { *this }, [this] (const File& file, const auto& cb) { cb (document.saveDocument (file)); }); } //============================================================================== template void askToSaveChangesSync (SafeParentPointer parent, Callback&& callback) { callback (parent, askToSaveChanges (parent, nullptr)); } //============================================================================== void saveAsInteractiveSyncImpl (SafeParentPointer parent, bool warnAboutOverwritingExistingFiles, std::function callback) { saveAsInteractiveImpl (parent, warnAboutOverwritingExistingFiles, std::move (callback), GetSaveAsFilenameSync { *this }, SaveAsSyncImpl { *this }, AskToOverwriteFileSync { *this }); } //============================================================================== template void askToOverwriteFileSync (SafeParentPointer parent, const File& newFile, Callback&& callback) { callback (parent, askToOverwriteFile (parent, newFile, nullptr)); } //============================================================================== template void saveSync (bool askUserForFileIfNotSpecified, bool showMessageOnFailure, Callback&& callback) { callback (save (askUserForFileIfNotSpecified, showMessageOnFailure)); } //============================================================================== template void getSaveAsFilenameSync (SafeParentPointer parent, bool warnAboutOverwritingExistingFiles, Callback&& callback) { auto fc = getInteractiveFileChooser(); if (fc->browseForFileToSave (warnAboutOverwritingExistingFiles)) { callback (parent, fc->getResult()); return; } callback (parent, {}); } #endif //============================================================================== FileBasedDocument& document; File documentFile; bool changedSinceSave = false; String fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle; std::unique_ptr asyncFc; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl) }; //============================================================================== FileBasedDocument::FileBasedDocument (const String& fileExtension, const String& fileWildcard, const String& openFileDialogTitle, const String& saveFileDialogTitle) : pimpl (new Pimpl (*this, fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle)) { } FileBasedDocument::~FileBasedDocument() = default; //============================================================================== bool FileBasedDocument::hasChangedSinceSaved() const { return pimpl->hasChangedSinceSaved(); } void FileBasedDocument::setChangedFlag (bool hasChanged) { pimpl->setChangedFlag (hasChanged); } void FileBasedDocument::changed() { pimpl->changed(); } //============================================================================== Result FileBasedDocument::loadFrom (const File& fileToLoadFrom, bool showMessageOnFailure, bool showWaitCursor) { return pimpl->loadFrom (fileToLoadFrom, showMessageOnFailure, showWaitCursor); } void FileBasedDocument::loadFromAsync (const File& fileToLoadFrom, bool showMessageOnFailure, std::function callback) { pimpl->loadFromAsync (fileToLoadFrom, showMessageOnFailure, std::move (callback)); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED Result FileBasedDocument::loadFromUserSpecifiedFile (bool showMessageOnFailure) { return pimpl->loadFromUserSpecifiedFile (showMessageOnFailure); } #endif void FileBasedDocument::loadFromUserSpecifiedFileAsync (const bool showMessageOnFailure, std::function callback) { pimpl->loadFromUserSpecifiedFileAsync (showMessageOnFailure, std::move (callback)); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult FileBasedDocument::save (bool askUserForFileIfNotSpecified, bool showMessageOnFailure) { return pimpl->save (askUserForFileIfNotSpecified, showMessageOnFailure); } #endif void FileBasedDocument::saveAsync (bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function callback) { pimpl->saveAsync (askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback)); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult FileBasedDocument::saveIfNeededAndUserAgrees() { return pimpl->saveIfNeededAndUserAgrees(); } #endif void FileBasedDocument::saveIfNeededAndUserAgreesAsync (std::function callback) { pimpl->saveIfNeededAndUserAgreesAsync (std::move (callback)); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, bool showWaitCursor) { return pimpl->saveAs (newFile, warnAboutOverwritingExistingFiles, askUserForFileIfNotSpecified, showMessageOnFailure, showWaitCursor); } #endif void FileBasedDocument::saveAsAsync (const File& newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function callback) { pimpl->saveAsAsync (newFile, warnAboutOverwritingExistingFiles, askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback)); } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (bool warnAboutOverwritingExistingFiles) { return pimpl->saveAsInteractive (warnAboutOverwritingExistingFiles); } #endif void FileBasedDocument::saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles, std::function callback) { pimpl->saveAsInteractiveAsync (warnAboutOverwritingExistingFiles, std::move (callback)); } //============================================================================== const File& FileBasedDocument::getFile() const { return pimpl->getFile(); } void FileBasedDocument::setFile (const File& newFile) { pimpl->setFile (newFile); } //============================================================================== void FileBasedDocument::loadDocumentAsync (const File& file, std::function callback) { const auto result = loadDocument (file); if (callback != nullptr) callback (result); } void FileBasedDocument::saveDocumentAsync (const File& file, std::function callback) { const auto result = saveDocument (file); if (callback != nullptr) callback (result); } File FileBasedDocument::getSuggestedSaveAsFile (const File& defaultFile) { return defaultFile.withFileExtension (pimpl->getFileExtension()).getNonexistentSibling (true); } } // namespace juce