VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_gui_extra / documents / juce_FileBasedDocument.cpp
blobd41832d7af6cfc662b2cf22251c86cdac4a111d3
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
29 //==============================================================================
30 class FileBasedDocument::Pimpl
32 private:
33 //==============================================================================
34 class SafeParentPointer
36 public:
37 SafeParentPointer (Pimpl* parent, bool isAsync)
38 : ptr (parent), shouldCheck (isAsync)
41 Pimpl* operator->() const noexcept
43 return ptr.get();
46 bool operator== (Pimpl* object) const noexcept { return ptr.get() == object; }
47 bool operator!= (Pimpl* object) const noexcept { return ptr.get() != object; }
49 bool shouldExitAsyncCallback() const noexcept
51 return shouldCheck && ptr == nullptr;
54 private:
55 WeakReference<Pimpl> ptr;
56 bool shouldCheck = false;
59 public:
60 //==============================================================================
61 Pimpl (FileBasedDocument& parent_,
62 const String& fileExtension_,
63 const String& fileWildcard_,
64 const String& openFileDialogTitle_,
65 const String& saveFileDialogTitle_)
66 : document (parent_),
67 fileExtension (fileExtension_),
68 fileWildcard (fileWildcard_),
69 openFileDialogTitle (openFileDialogTitle_),
70 saveFileDialogTitle (saveFileDialogTitle_)
74 //==============================================================================
75 bool hasChangedSinceSaved() const
77 return changedSinceSave;
80 void setChangedFlag (bool hasChanged)
82 if (changedSinceSave != hasChanged)
84 changedSinceSave = hasChanged;
85 document.sendChangeMessage();
89 void changed()
91 changedSinceSave = true;
92 document.sendChangeMessage();
95 //==============================================================================
96 Result loadFrom (const File& newFile, bool showMessageOnFailure, bool showWaitCursor = true)
98 SafeParentPointer parent { this, false };
99 auto result = Result::ok();
100 loadFromImpl (parent,
101 newFile,
102 showMessageOnFailure,
103 showWaitCursor,
104 [this] (const File& file, const auto& callback) { callback (document.loadDocument (file)); },
105 [&result] (Result r) { result = r; });
106 return result;
109 void loadFromAsync (const File& newFile,
110 bool showMessageOnFailure,
111 std::function<void (Result)> callback)
113 SafeParentPointer parent { this, true };
114 loadFromImpl (parent,
115 newFile,
116 showMessageOnFailure,
117 false,
118 [parent] (const File& file, auto cb)
120 if (parent != nullptr)
121 parent->document.loadDocumentAsync (file, std::move (cb));
123 std::move (callback));
126 //==============================================================================
127 #if JUCE_MODAL_LOOPS_PERMITTED
128 Result loadFromUserSpecifiedFile (bool showMessageOnFailure)
130 FileChooser fc (openFileDialogTitle,
131 document.getLastDocumentOpened(),
132 fileWildcard);
134 if (fc.browseForFileToOpen())
135 return loadFrom (fc.getResult(), showMessageOnFailure);
137 return Result::fail (TRANS ("User cancelled"));
139 #endif
141 void loadFromUserSpecifiedFileAsync (const bool showMessageOnFailure, std::function<void (Result)> callback)
143 asyncFc = std::make_unique<FileChooser> (openFileDialogTitle,
144 document.getLastDocumentOpened(),
145 fileWildcard);
147 asyncFc->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
148 [this, showMessageOnFailure, callback = std::move (callback)] (const FileChooser& fc)
150 auto chosenFile = fc.getResult();
152 if (chosenFile == File{})
154 if (callback != nullptr)
155 callback (Result::fail (TRANS ("User cancelled")));
157 return;
160 WeakReference<Pimpl> parent { this };
161 loadFromAsync (chosenFile, showMessageOnFailure, [parent, callback] (Result result)
163 if (parent != nullptr && callback != nullptr)
164 callback (result);
167 asyncFc = nullptr;
171 //==============================================================================
172 #if JUCE_MODAL_LOOPS_PERMITTED
173 FileBasedDocument::SaveResult save (bool askUserForFileIfNotSpecified,
174 bool showMessageOnFailure)
176 return saveAs (documentFile,
177 false,
178 askUserForFileIfNotSpecified,
179 showMessageOnFailure);
181 #endif
183 void saveAsync (bool askUserForFileIfNotSpecified,
184 bool showMessageOnFailure,
185 std::function<void (SaveResult)> callback)
187 saveAsAsync (documentFile,
188 false,
189 askUserForFileIfNotSpecified,
190 showMessageOnFailure,
191 std::move (callback));
194 //==============================================================================
195 #if JUCE_MODAL_LOOPS_PERMITTED
196 FileBasedDocument::SaveResult saveIfNeededAndUserAgrees()
198 SafeParentPointer parent { this, false };
199 SaveResult result;
200 saveIfNeededAndUserAgreesImpl (parent,
201 [&result] (SaveResult r) { result = r; },
202 AskToSaveChangesSync { *this },
203 SaveSync { *this });
204 return result;
206 #endif
208 void saveIfNeededAndUserAgreesAsync (std::function<void (SaveResult)> callback)
210 SafeParentPointer parent { this, true };
212 saveIfNeededAndUserAgreesImpl (parent,
213 std::move (callback),
214 [] (SafeParentPointer ptr, auto cb)
216 if (ptr != nullptr)
217 ptr->askToSaveChanges (ptr, std::move (cb));
219 [parent] (bool askUserForFileIfNotSpecified,
220 bool showMessageOnFailure,
221 auto cb)
223 if (parent != nullptr)
224 parent->saveAsync (askUserForFileIfNotSpecified,
225 showMessageOnFailure,
226 std::move (cb));
230 //==============================================================================
231 #if JUCE_MODAL_LOOPS_PERMITTED
232 FileBasedDocument::SaveResult saveAs (const File& newFile,
233 bool warnAboutOverwritingExistingFiles,
234 bool askUserForFileIfNotSpecified,
235 bool showMessageOnFailure,
236 bool showWaitCursor = true)
238 SafeParentPointer parent { this, false };
239 SaveResult result{};
240 saveAsSyncImpl (parent,
241 newFile,
242 warnAboutOverwritingExistingFiles,
243 askUserForFileIfNotSpecified,
244 showMessageOnFailure,
245 [&result] (SaveResult r) { result = r; },
246 showWaitCursor);
247 return result;
249 #endif
251 void saveAsAsync (const File& newFile,
252 bool warnAboutOverwritingExistingFiles,
253 bool askUserForFileIfNotSpecified,
254 bool showMessageOnFailure,
255 std::function<void (SaveResult)> callback)
257 SafeParentPointer parent { this, true };
258 saveAsAsyncImpl (parent,
259 newFile,
260 warnAboutOverwritingExistingFiles,
261 askUserForFileIfNotSpecified,
262 showMessageOnFailure,
263 std::move (callback),
264 false);
267 //==============================================================================
268 #if JUCE_MODAL_LOOPS_PERMITTED
269 FileBasedDocument::SaveResult saveAsInteractive (bool warnAboutOverwritingExistingFiles)
271 SafeParentPointer parent { this, false };
272 SaveResult result{};
273 saveAsInteractiveSyncImpl (parent,
274 warnAboutOverwritingExistingFiles,
275 [&result] (SaveResult r) { result = r; });
276 return result;
278 #endif
280 void saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles,
281 std::function<void (SaveResult)> callback)
283 SafeParentPointer parent { this, true };
284 saveAsInteractiveAsyncImpl (parent,
285 warnAboutOverwritingExistingFiles,
286 std::move (callback));
289 //==============================================================================
290 const File& getFile() const
292 return documentFile;
295 void setFile (const File& newFile)
297 if (documentFile != newFile)
299 documentFile = newFile;
300 changed();
304 //==============================================================================
305 const String& getFileExtension() const
307 return fileExtension;
310 private:
311 //==============================================================================
312 template <typename DoLoadDocument>
313 void loadFromImpl (SafeParentPointer parent,
314 const File& newFile,
315 bool showMessageOnFailure,
316 bool showWaitCursor,
317 DoLoadDocument&& doLoadDocument,
318 std::function<void (Result)> completed)
320 if (parent.shouldExitAsyncCallback())
321 return;
323 if (showWaitCursor)
324 MouseCursor::showWaitCursor();
326 auto oldFile = documentFile;
327 documentFile = newFile;
329 auto tidyUp = [parent, newFile, oldFile, showMessageOnFailure, showWaitCursor, completed] (Result result)
331 if (parent.shouldExitAsyncCallback())
332 return;
334 parent->documentFile = oldFile;
336 if (showWaitCursor)
337 MouseCursor::hideWaitCursor();
339 if (showMessageOnFailure)
340 AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
341 TRANS ("Failed to open file..."),
342 TRANS ("There was an error while trying to load the file: FLNM")
343 .replace ("FLNM", "\n" + newFile.getFullPathName())
344 + "\n\n"
345 + result.getErrorMessage());
347 if (completed != nullptr)
348 completed (result);
351 if (newFile.existsAsFile())
353 auto afterLoading = [parent,
354 showWaitCursor,
355 newFile,
356 completed = std::move (completed),
357 tidyUp] (Result result)
359 if (result.wasOk())
361 parent->setChangedFlag (false);
363 if (showWaitCursor)
364 MouseCursor::hideWaitCursor();
366 parent->document.setLastDocumentOpened (newFile);
368 if (completed != nullptr)
369 completed (result);
371 return;
374 tidyUp (result);
377 doLoadDocument (newFile, std::move (afterLoading));
379 return;
382 tidyUp (Result::fail (TRANS ("The file doesn't exist")));
385 //==============================================================================
386 template <typename DoAskToSaveChanges, typename DoSave>
387 void saveIfNeededAndUserAgreesImpl (SafeParentPointer parent,
388 std::function<void (SaveResult)> completed,
389 DoAskToSaveChanges&& doAskToSaveChanges,
390 DoSave&& doSave)
392 if (parent.shouldExitAsyncCallback())
393 return;
395 if (! hasChangedSinceSaved())
397 if (completed != nullptr)
398 completed (savedOk);
400 return;
403 auto afterAsking = [doSave = std::forward<DoSave> (doSave),
404 completed = std::move (completed)] (SafeParentPointer ptr,
405 int alertResult)
407 if (ptr.shouldExitAsyncCallback())
408 return;
410 switch (alertResult)
412 case 1: // save changes
413 doSave (true, true, [ptr, completed] (SaveResult result)
415 if (ptr.shouldExitAsyncCallback())
416 return;
418 if (completed != nullptr)
419 completed (result);
421 return;
423 case 2: // discard changes
424 if (completed != nullptr)
425 completed (savedOk);
427 return;
430 if (completed != nullptr)
431 completed (userCancelledSave);
434 doAskToSaveChanges (parent, std::move (afterAsking));
437 //==============================================================================
438 int askToSaveChanges (SafeParentPointer parent,
439 std::function<void (SafeParentPointer, int)> callback)
441 auto* modalCallback = callback == nullptr
442 ? nullptr
443 : ModalCallbackFunction::create ([parent, callback = std::move (callback)] (int alertResult)
445 if (parent != nullptr)
446 callback (parent, alertResult);
449 return AlertWindow::showYesNoCancelBox (MessageBoxIconType::QuestionIcon,
450 TRANS ("Closing document..."),
451 TRANS ("Do you want to save the changes to \"DCNM\"?")
452 .replace ("DCNM", document.getDocumentTitle()),
453 TRANS ("Save"),
454 TRANS ("Discard changes"),
455 TRANS ("Cancel"),
456 nullptr,
457 modalCallback);
460 //==============================================================================
461 template <typename DoSaveDocument>
462 void saveInternal (SafeParentPointer parent,
463 const File& newFile,
464 bool showMessageOnFailure,
465 bool showWaitCursor,
466 std::function<void (SaveResult)> afterSave,
467 DoSaveDocument&& doSaveDocument)
469 if (showWaitCursor)
470 MouseCursor::showWaitCursor();
472 auto oldFile = documentFile;
473 documentFile = newFile;
475 doSaveDocument (newFile, [parent,
476 showMessageOnFailure,
477 showWaitCursor,
478 oldFile,
479 newFile,
480 afterSave = std::move (afterSave)] (Result result)
482 if (parent.shouldExitAsyncCallback())
484 if (showWaitCursor)
485 MouseCursor::hideWaitCursor();
487 return;
490 if (result.wasOk())
492 parent->setChangedFlag (false);
494 if (showWaitCursor)
495 MouseCursor::hideWaitCursor();
497 parent->document.sendChangeMessage(); // because the filename may have changed
499 if (afterSave != nullptr)
500 afterSave (savedOk);
502 return;
505 parent->documentFile = oldFile;
507 if (showWaitCursor)
508 MouseCursor::hideWaitCursor();
510 if (showMessageOnFailure)
511 AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
512 TRANS ("Error writing to file..."),
513 TRANS ("An error occurred while trying to save \"DCNM\" to the file: FLNM")
514 .replace ("DCNM", parent->document.getDocumentTitle())
515 .replace ("FLNM", "\n" + newFile.getFullPathName())
516 + "\n\n"
517 + result.getErrorMessage());
519 parent->document.sendChangeMessage(); // because the filename may have changed
521 if (afterSave != nullptr)
522 afterSave (failedToWriteToFile);
526 template <typename DoSaveAsInteractive, typename DoAskToOverwriteFile, typename DoSaveDocument>
527 void saveAsImpl (SafeParentPointer parent,
528 const File& newFile,
529 bool warnAboutOverwritingExistingFiles,
530 bool askUserForFileIfNotSpecified,
531 bool showMessageOnFailure,
532 std::function<void (SaveResult)> callback,
533 bool showWaitCursor,
534 DoSaveAsInteractive&& doSaveAsInteractive,
535 DoAskToOverwriteFile&& doAskToOverwriteFile,
536 DoSaveDocument&& doSaveDocument)
538 if (parent.shouldExitAsyncCallback())
539 return;
541 if (newFile == File())
543 if (askUserForFileIfNotSpecified)
545 doSaveAsInteractive (parent, true, std::move (callback));
546 return;
549 // can't save to an unspecified file
550 jassertfalse;
552 if (callback != nullptr)
553 callback (failedToWriteToFile);
555 return;
558 auto saveInternalHelper = [parent,
559 callback,
560 newFile,
561 showMessageOnFailure,
562 showWaitCursor,
563 doSaveDocument = std::forward<DoSaveDocument> (doSaveDocument)]
565 if (! parent.shouldExitAsyncCallback())
566 parent->saveInternal (parent,
567 newFile,
568 showMessageOnFailure,
569 showWaitCursor,
570 callback,
571 doSaveDocument);
574 if (warnAboutOverwritingExistingFiles && newFile.exists())
576 auto afterAsking = [callback = std::move (callback),
577 saveInternalHelper] (SafeParentPointer ptr,
578 bool shouldOverwrite)
580 if (ptr.shouldExitAsyncCallback())
581 return;
583 if (shouldOverwrite)
584 saveInternalHelper();
585 else if (callback != nullptr)
586 callback (userCancelledSave);
588 doAskToOverwriteFile (parent, newFile, std::move (afterAsking));
589 return;
592 saveInternalHelper();
595 void saveAsAsyncImpl (SafeParentPointer parent,
596 const File& newFile,
597 bool warnAboutOverwritingExistingFiles,
598 bool askUserForFileIfNotSpecified,
599 bool showMessageOnFailure,
600 std::function<void (SaveResult)> callback,
601 bool showWaitCursor)
603 saveAsImpl (parent,
604 newFile,
605 warnAboutOverwritingExistingFiles,
606 askUserForFileIfNotSpecified,
607 showMessageOnFailure,
608 std::move (callback),
609 showWaitCursor,
610 [] (SafeParentPointer ptr, bool warnAboutOverwriting, auto cb)
612 if (ptr != nullptr)
613 ptr->saveAsInteractiveAsyncImpl (ptr, warnAboutOverwriting, std::move (cb));
615 [] (SafeParentPointer ptr, const File& destination, std::function<void (SafeParentPointer, bool)> cb)
617 if (ptr != nullptr)
618 ptr->askToOverwriteFile (ptr, destination, std::move (cb));
620 [parent] (const File& destination, std::function<void (Result)> cb)
622 if (parent != nullptr)
623 parent->document.saveDocumentAsync (destination, std::move (cb));
627 //==============================================================================
628 void saveAsInteractiveAsyncImpl (SafeParentPointer parent,
629 bool warnAboutOverwritingExistingFiles,
630 std::function<void (SaveResult)> callback)
632 if (parent == nullptr)
633 return;
635 saveAsInteractiveImpl (parent,
636 warnAboutOverwritingExistingFiles,
637 std::move (callback),
638 [] (SafeParentPointer ptr, bool warnAboutOverwriting, auto cb)
640 if (ptr != nullptr)
641 ptr->getSaveAsFilenameAsync (ptr, warnAboutOverwriting, std::move (cb));
643 [] (SafeParentPointer ptr,
644 const File& newFile,
645 bool warnAboutOverwriting,
646 bool askUserForFileIfNotSpecified,
647 bool showMessageOnFailure,
648 auto cb,
649 bool showWaitCursor)
651 if (ptr != nullptr)
652 ptr->saveAsAsyncImpl (ptr,
653 newFile,
654 warnAboutOverwriting,
655 askUserForFileIfNotSpecified,
656 showMessageOnFailure,
657 std::move (cb),
658 showWaitCursor);
660 [] (SafeParentPointer ptr, const File& destination, auto cb)
662 if (ptr != nullptr)
663 ptr->askToOverwriteFile (ptr, destination, std::move (cb));
667 //==============================================================================
668 bool askToOverwriteFile (SafeParentPointer parent,
669 const File& newFile,
670 std::function<void (SafeParentPointer, bool)> callback)
672 if (parent == nullptr)
673 return false;
675 auto* modalCallback = callback == nullptr
676 ? nullptr
677 : ModalCallbackFunction::create ([parent, callback = std::move (callback)] (int r)
679 if (parent != nullptr)
680 callback (parent, r == 1);
683 return AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
684 TRANS ("File already exists"),
685 TRANS ("There's already a file called: FLNM")
686 .replace ("FLNM", newFile.getFullPathName())
687 + "\n\n"
688 + TRANS ("Are you sure you want to overwrite it?"),
689 TRANS ("Overwrite"),
690 TRANS ("Cancel"),
691 nullptr,
692 modalCallback);
695 //==============================================================================
696 void getSaveAsFilenameAsync (SafeParentPointer parent,
697 bool warnAboutOverwritingExistingFiles,
698 std::function<void (SafeParentPointer, const File&)> callback)
700 asyncFc = getInteractiveFileChooser();
702 auto flags = FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles;
704 if (warnAboutOverwritingExistingFiles)
705 flags |= FileBrowserComponent::warnAboutOverwriting;
707 asyncFc->launchAsync (flags, [parent, callback = std::move (callback)] (const FileChooser& fc)
709 callback (parent, fc.getResult());
713 //==============================================================================
714 template <typename DoSelectFilename, typename DoSaveAs, typename DoAskToOverwriteFile>
715 void saveAsInteractiveImpl (SafeParentPointer parent,
716 bool warnAboutOverwritingExistingFiles,
717 std::function<void (SaveResult)> callback,
718 DoSelectFilename&& doSelectFilename,
719 DoSaveAs&& doSaveAs,
720 DoAskToOverwriteFile&& doAskToOverwriteFile)
722 doSelectFilename (parent,
723 warnAboutOverwritingExistingFiles,
724 [doSaveAs = std::forward<DoSaveAs> (doSaveAs),
725 doAskToOverwriteFile = std::forward<DoAskToOverwriteFile> (doAskToOverwriteFile),
726 callback = std::move (callback)] (SafeParentPointer parentPtr, File chosen)
728 if (parentPtr.shouldExitAsyncCallback())
729 return;
731 if (chosen == File{})
733 if (callback != nullptr)
734 callback (userCancelledSave);
736 return;
739 auto updateAndSaveAs = [parentPtr, doSaveAs, callback] (const File& chosenFile)
741 if (parentPtr.shouldExitAsyncCallback())
742 return;
744 parentPtr->document.setLastDocumentOpened (chosenFile);
745 doSaveAs (parentPtr, chosenFile, false, false, true, callback, false);
748 if (chosen.getFileExtension().isEmpty())
750 chosen = chosen.withFileExtension (parentPtr->fileExtension);
752 if (chosen.exists())
754 auto afterAsking = [chosen, updateAndSaveAs, callback] (SafeParentPointer overwritePtr,
755 bool overwrite)
757 if (overwritePtr.shouldExitAsyncCallback())
758 return;
760 if (overwrite)
761 updateAndSaveAs (chosen);
762 else if (callback != nullptr)
763 callback (userCancelledSave);
766 doAskToOverwriteFile (parentPtr, chosen, std::move (afterAsking));
767 return;
771 updateAndSaveAs (chosen);
775 //==============================================================================
776 std::unique_ptr<FileChooser> getInteractiveFileChooser()
778 auto f = documentFile.existsAsFile() ? documentFile : document.getLastDocumentOpened();
780 auto legalFilename = File::createLegalFileName (document.getDocumentTitle());
782 if (legalFilename.isEmpty())
783 legalFilename = "unnamed";
785 f = (f.existsAsFile() || f.getParentDirectory().isDirectory())
786 ? f.getSiblingFile (legalFilename)
787 : File::getSpecialLocation (File::userDocumentsDirectory).getChildFile (legalFilename);
789 f = document.getSuggestedSaveAsFile (f);
791 return std::make_unique<FileChooser> (saveFileDialogTitle,
793 fileWildcard);
796 //==============================================================================
797 #if JUCE_MODAL_LOOPS_PERMITTED
798 struct SaveAsInteractiveSyncImpl
800 template <typename... Ts>
801 void operator() (Ts&&... ts) const noexcept
803 p.saveAsInteractiveSyncImpl (std::forward<Ts> (ts)...);
806 Pimpl& p;
809 struct AskToOverwriteFileSync
811 template <typename... Ts>
812 void operator() (Ts&&... ts) const noexcept
814 p.askToOverwriteFileSync (std::forward<Ts> (ts)...);
817 Pimpl& p;
820 struct AskToSaveChangesSync
822 template <typename... Ts>
823 void operator() (Ts&&... ts) const noexcept
825 p.askToSaveChangesSync (std::forward<Ts> (ts)...);
828 Pimpl& p;
831 struct SaveSync
833 template <typename... Ts>
834 void operator() (Ts&&... ts) const noexcept
836 p.saveSync (std::forward<Ts> (ts)...);
839 Pimpl& p;
842 struct GetSaveAsFilenameSync
844 template <typename... Ts>
845 void operator() (Ts&&... ts) const noexcept
847 p.getSaveAsFilenameSync (std::forward<Ts> (ts)...);
850 Pimpl& p;
853 struct SaveAsSyncImpl
855 template <typename... Ts>
856 void operator() (Ts&&... ts) const noexcept
858 p.saveAsSyncImpl (std::forward<Ts> (ts)...);
861 Pimpl& p;
864 //==============================================================================
865 void saveAsSyncImpl (SafeParentPointer parent,
866 const File& newFile,
867 bool warnAboutOverwritingExistingFiles,
868 bool askUserForFileIfNotSpecified,
869 bool showMessageOnFailure,
870 std::function<void (SaveResult)> callback,
871 bool showWaitCursor)
873 saveAsImpl (parent,
874 newFile,
875 warnAboutOverwritingExistingFiles,
876 askUserForFileIfNotSpecified,
877 showMessageOnFailure,
878 std::move (callback),
879 showWaitCursor,
880 SaveAsInteractiveSyncImpl { *this },
881 AskToOverwriteFileSync { *this },
882 [this] (const File& file, const auto& cb) { cb (document.saveDocument (file)); });
885 //==============================================================================
886 template <typename Callback>
887 void askToSaveChangesSync (SafeParentPointer parent, Callback&& callback)
889 callback (parent, askToSaveChanges (parent, nullptr));
892 //==============================================================================
893 void saveAsInteractiveSyncImpl (SafeParentPointer parent,
894 bool warnAboutOverwritingExistingFiles,
895 std::function<void (SaveResult)> callback)
897 saveAsInteractiveImpl (parent,
898 warnAboutOverwritingExistingFiles,
899 std::move (callback),
900 GetSaveAsFilenameSync { *this },
901 SaveAsSyncImpl { *this },
902 AskToOverwriteFileSync { *this });
905 //==============================================================================
906 template <typename Callback>
907 void askToOverwriteFileSync (SafeParentPointer parent,
908 const File& newFile,
909 Callback&& callback)
911 callback (parent, askToOverwriteFile (parent, newFile, nullptr));
914 //==============================================================================
915 template <typename Callback>
916 void saveSync (bool askUserForFileIfNotSpecified,
917 bool showMessageOnFailure,
918 Callback&& callback)
920 callback (save (askUserForFileIfNotSpecified, showMessageOnFailure));
923 //==============================================================================
924 template <typename Callback>
925 void getSaveAsFilenameSync (SafeParentPointer parent,
926 bool warnAboutOverwritingExistingFiles,
927 Callback&& callback)
929 auto fc = getInteractiveFileChooser();
931 if (fc->browseForFileToSave (warnAboutOverwritingExistingFiles))
933 callback (parent, fc->getResult());
934 return;
937 callback (parent, {});
939 #endif
941 //==============================================================================
942 FileBasedDocument& document;
944 File documentFile;
945 bool changedSinceSave = false;
946 String fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle;
947 std::unique_ptr<FileChooser> asyncFc;
949 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
950 JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl)
953 //==============================================================================
954 FileBasedDocument::FileBasedDocument (const String& fileExtension,
955 const String& fileWildcard,
956 const String& openFileDialogTitle,
957 const String& saveFileDialogTitle)
958 : pimpl (new Pimpl (*this,
959 fileExtension,
960 fileWildcard,
961 openFileDialogTitle,
962 saveFileDialogTitle))
966 FileBasedDocument::~FileBasedDocument() = default;
968 //==============================================================================
969 bool FileBasedDocument::hasChangedSinceSaved() const
971 return pimpl->hasChangedSinceSaved();
974 void FileBasedDocument::setChangedFlag (bool hasChanged)
976 pimpl->setChangedFlag (hasChanged);
979 void FileBasedDocument::changed()
981 pimpl->changed();
984 //==============================================================================
985 Result FileBasedDocument::loadFrom (const File& fileToLoadFrom,
986 bool showMessageOnFailure,
987 bool showWaitCursor)
989 return pimpl->loadFrom (fileToLoadFrom, showMessageOnFailure, showWaitCursor);
992 void FileBasedDocument::loadFromAsync (const File& fileToLoadFrom,
993 bool showMessageOnFailure,
994 std::function<void (Result)> callback)
996 pimpl->loadFromAsync (fileToLoadFrom, showMessageOnFailure, std::move (callback));
999 //==============================================================================
1000 #if JUCE_MODAL_LOOPS_PERMITTED
1001 Result FileBasedDocument::loadFromUserSpecifiedFile (bool showMessageOnFailure)
1003 return pimpl->loadFromUserSpecifiedFile (showMessageOnFailure);
1005 #endif
1007 void FileBasedDocument::loadFromUserSpecifiedFileAsync (const bool showMessageOnFailure,
1008 std::function<void (Result)> callback)
1010 pimpl->loadFromUserSpecifiedFileAsync (showMessageOnFailure, std::move (callback));
1013 //==============================================================================
1014 #if JUCE_MODAL_LOOPS_PERMITTED
1015 FileBasedDocument::SaveResult FileBasedDocument::save (bool askUserForFileIfNotSpecified,
1016 bool showMessageOnFailure)
1018 return pimpl->save (askUserForFileIfNotSpecified, showMessageOnFailure);
1020 #endif
1022 void FileBasedDocument::saveAsync (bool askUserForFileIfNotSpecified,
1023 bool showMessageOnFailure,
1024 std::function<void (SaveResult)> callback)
1026 pimpl->saveAsync (askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback));
1029 //==============================================================================
1030 #if JUCE_MODAL_LOOPS_PERMITTED
1031 FileBasedDocument::SaveResult FileBasedDocument::saveIfNeededAndUserAgrees()
1033 return pimpl->saveIfNeededAndUserAgrees();
1035 #endif
1037 void FileBasedDocument::saveIfNeededAndUserAgreesAsync (std::function<void (SaveResult)> callback)
1039 pimpl->saveIfNeededAndUserAgreesAsync (std::move (callback));
1042 //==============================================================================
1043 #if JUCE_MODAL_LOOPS_PERMITTED
1044 FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile,
1045 bool warnAboutOverwritingExistingFiles,
1046 bool askUserForFileIfNotSpecified,
1047 bool showMessageOnFailure,
1048 bool showWaitCursor)
1050 return pimpl->saveAs (newFile,
1051 warnAboutOverwritingExistingFiles,
1052 askUserForFileIfNotSpecified,
1053 showMessageOnFailure,
1054 showWaitCursor);
1056 #endif
1058 void FileBasedDocument::saveAsAsync (const File& newFile,
1059 bool warnAboutOverwritingExistingFiles,
1060 bool askUserForFileIfNotSpecified,
1061 bool showMessageOnFailure,
1062 std::function<void (SaveResult)> callback)
1064 pimpl->saveAsAsync (newFile,
1065 warnAboutOverwritingExistingFiles,
1066 askUserForFileIfNotSpecified,
1067 showMessageOnFailure,
1068 std::move (callback));
1071 //==============================================================================
1072 #if JUCE_MODAL_LOOPS_PERMITTED
1073 FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (bool warnAboutOverwritingExistingFiles)
1075 return pimpl->saveAsInteractive (warnAboutOverwritingExistingFiles);
1077 #endif
1079 void FileBasedDocument::saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles,
1080 std::function<void (SaveResult)> callback)
1082 pimpl->saveAsInteractiveAsync (warnAboutOverwritingExistingFiles, std::move (callback));
1085 //==============================================================================
1086 const File& FileBasedDocument::getFile() const
1088 return pimpl->getFile();
1091 void FileBasedDocument::setFile (const File& newFile)
1093 pimpl->setFile (newFile);
1096 //==============================================================================
1097 void FileBasedDocument::loadDocumentAsync (const File& file, std::function<void (Result)> callback)
1099 const auto result = loadDocument (file);
1101 if (callback != nullptr)
1102 callback (result);
1105 void FileBasedDocument::saveDocumentAsync (const File& file, std::function<void (Result)> callback)
1107 const auto result = saveDocument (file);
1109 if (callback != nullptr)
1110 callback (result);
1113 File FileBasedDocument::getSuggestedSaveAsFile (const File& defaultFile)
1115 return defaultFile.withFileExtension (pimpl->getFileExtension()).getNonexistentSibling (true);
1118 } // namespace juce