34 class SafeParentPointer
37 SafeParentPointer (
Pimpl* parent,
bool isAsync)
38 : ptr (parent), shouldCheck (isAsync)
41 Pimpl* operator->()
const noexcept
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;
56 bool shouldCheck =
false;
62 const String& fileExtension_,
63 const String& fileWildcard_,
64 const String& openFileDialogTitle_,
65 const String& saveFileDialogTitle_)
67 fileExtension (fileExtension_),
68 fileWildcard (fileWildcard_),
69 openFileDialogTitle (openFileDialogTitle_),
70 saveFileDialogTitle (saveFileDialogTitle_)
75 bool hasChangedSinceSaved()
const
77 return changedSinceSave;
80 void setChangedFlag (
bool hasChanged)
82 if (changedSinceSave != hasChanged)
84 changedSinceSave = hasChanged;
91 changedSinceSave =
true;
96 Result loadFrom (
const File& newFile,
bool showMessageOnFailure,
bool showWaitCursor =
true)
98 SafeParentPointer parent {
this,
false };
100 loadFromImpl (parent,
102 showMessageOnFailure,
104 [
this] (
const File& file,
const auto& callback) { callback (document.
loadDocument (file)); },
105 [&result] (
Result r) { result = r; });
109 void loadFromAsync (
const File& newFile,
110 bool showMessageOnFailure,
113 SafeParentPointer parent {
this,
true };
114 loadFromImpl (parent,
116 showMessageOnFailure,
118 [parent] (
const File& file,
auto cb)
120 if (parent !=
nullptr)
121 parent->document.loadDocumentAsync (file, std::move (cb));
123 std::move (callback));
127 #if JUCE_MODAL_LOOPS_PERMITTED
128 Result loadFromUserSpecifiedFile (
bool showMessageOnFailure)
134 if (fc.browseForFileToOpen())
135 return loadFrom (fc.
getResult(), showMessageOnFailure);
141 void loadFromUserSpecifiedFileAsync (
const bool showMessageOnFailure,
std::function<
void (
Result)> callback)
148 [
this, showMessageOnFailure, cb = std::move (callback)] (
const FileChooser& fc)
152 if (chosenFile ==
File{})
159 loadFromAsync (chosenFile, showMessageOnFailure, [parent, cb] (
Result result)
161 if (parent !=
nullptr)
162 NullCheckedInvocation::invoke (cb, result);
170 #if JUCE_MODAL_LOOPS_PERMITTED
172 bool showMessageOnFailure)
174 return saveAs (documentFile,
176 askUserForFileIfNotSpecified,
177 showMessageOnFailure);
181 void saveAsync (
bool askUserForFileIfNotSpecified,
182 bool showMessageOnFailure,
185 saveAsAsync (documentFile,
187 askUserForFileIfNotSpecified,
188 showMessageOnFailure,
189 std::move (callback));
193 #if JUCE_MODAL_LOOPS_PERMITTED
196 SafeParentPointer parent {
this,
false };
198 saveIfNeededAndUserAgreesImpl (parent,
200 AskToSaveChangesSync { *
this },
208 SafeParentPointer parent {
this,
true };
210 saveIfNeededAndUserAgreesImpl (parent,
211 std::move (callback),
212 [] (SafeParentPointer ptr,
auto cb)
215 ptr->askToSaveChangesAsync (ptr, std::move (cb));
217 [parent] (
bool askUserForFileIfNotSpecified,
218 bool showMessageOnFailure,
221 if (parent !=
nullptr)
222 parent->saveAsync (askUserForFileIfNotSpecified,
223 showMessageOnFailure,
229 #if JUCE_MODAL_LOOPS_PERMITTED
231 bool warnAboutOverwritingExistingFiles,
232 bool askUserForFileIfNotSpecified,
233 bool showMessageOnFailure,
234 bool showWaitCursor =
true)
236 SafeParentPointer parent {
this,
false };
238 saveAsSyncImpl (parent,
240 warnAboutOverwritingExistingFiles,
241 askUserForFileIfNotSpecified,
242 showMessageOnFailure,
249 void saveAsAsync (
const File& newFile,
250 bool warnAboutOverwritingExistingFiles,
251 bool askUserForFileIfNotSpecified,
252 bool showMessageOnFailure,
255 SafeParentPointer parent {
this,
true };
256 saveAsAsyncImpl (parent,
258 warnAboutOverwritingExistingFiles,
259 askUserForFileIfNotSpecified,
260 showMessageOnFailure,
261 std::move (callback),
266 #if JUCE_MODAL_LOOPS_PERMITTED
269 SafeParentPointer parent {
this,
false };
271 saveAsInteractiveSyncImpl (parent,
272 warnAboutOverwritingExistingFiles,
278 void saveAsInteractiveAsync (
bool warnAboutOverwritingExistingFiles,
281 SafeParentPointer parent {
this,
true };
282 saveAsInteractiveAsyncImpl (parent,
283 warnAboutOverwritingExistingFiles,
284 std::move (callback));
288 const File& getFile()
const
293 void setFile (
const File& newFile)
295 if (documentFile != newFile)
297 documentFile = newFile;
303 const String& getFileExtension()
const
305 return fileExtension;
310 template <
typename DoLoadDocument>
311 void loadFromImpl (SafeParentPointer parent,
313 bool showMessageOnFailure,
315 DoLoadDocument&& doLoadDocument,
318 if (parent.shouldExitAsyncCallback())
324 auto oldFile = documentFile;
325 documentFile = newFile;
327 auto tidyUp = [parent, newFile, oldFile, showMessageOnFailure, showWaitCursor, completed] (
Result result)
329 if (parent.shouldExitAsyncCallback())
332 parent->documentFile = oldFile;
337 if (showMessageOnFailure)
340 TRANS (
"Failed to open file..."),
341 TRANS (
"There was an error while trying to load the file: FLNM")
342 .replace (
"FLNM",
"\n" + newFile.getFullPathName())
344 + result.getErrorMessage());
348 NullCheckedInvocation::invoke (completed, result);
351 if (newFile.existsAsFile())
353 auto afterLoading = [parent,
356 cb = std::move (completed),
361 parent->setChangedFlag (
false);
366 parent->document.setLastDocumentOpened (newFile);
367 NullCheckedInvocation::invoke (cb, result);
374 doLoadDocument (newFile, std::move (afterLoading));
383 template <
typename DoAskToSaveChanges,
typename DoSave>
384 void saveIfNeededAndUserAgreesImpl (SafeParentPointer parent,
386 DoAskToSaveChanges&& doAskToSaveChanges,
389 if (parent.shouldExitAsyncCallback())
392 if (! hasChangedSinceSaved())
394 NullCheckedInvocation::invoke (completed,
savedOk);
399 cb = std::move (completed)] (SafeParentPointer ptr,
402 if (ptr.shouldExitAsyncCallback())
408 save (
true,
true, [ptr, cb] (
SaveResult result)
410 if (ptr.shouldExitAsyncCallback())
413 NullCheckedInvocation::invoke (cb, result);
418 NullCheckedInvocation::invoke (cb,
savedOk);
425 doAskToSaveChanges (parent, std::move (afterAsking));
432 TRANS (
"Closing document..."),
433 TRANS (
"Do you want to save the changes to \"DCNM\"?")
436 TRANS (
"Discard changes"),
440 void askToSaveChangesAsync (SafeParentPointer parent,
444 [parent, cb = std::move (callback)] (
int alertResult)
446 if (parent !=
nullptr)
447 cb (parent, alertResult);
451 #if JUCE_MODAL_LOOPS_PERMITTED
452 int askToSaveChangesSync()
454 return AlertWindow::show (getAskToSaveChangesOptions());
459 template <
typename DoSaveDocument>
460 void saveInternal (SafeParentPointer parent,
462 bool showMessageOnFailure,
465 DoSaveDocument&& doSaveDocument)
470 auto oldFile = documentFile;
471 documentFile = newFile;
473 doSaveDocument (newFile, [parent,
474 showMessageOnFailure,
478 after = std::move (afterSave)] (
Result result)
480 if (parent.shouldExitAsyncCallback())
483 MouseCursor::hideWaitCursor();
490 parent->setChangedFlag (false);
493 MouseCursor::hideWaitCursor();
495 parent->document.sendChangeMessage();
497 NullCheckedInvocation::invoke (after, savedOk);
501 parent->documentFile = oldFile;
506 if (showMessageOnFailure)
509 TRANS (
"Error writing to file..."),
510 TRANS (
"An error occurred while trying to save \"DCNM\" to the file: FLNM")
511 .replace (
"DCNM", parent->document.getDocumentTitle())
518 parent->document.sendChangeMessage();
523 template <
typename DoSaveAsInteractive,
typename DoAskToOverwriteFile,
typename DoSaveDocument>
524 void saveAsImpl (SafeParentPointer parent,
526 bool warnAboutOverwritingExistingFiles,
527 bool askUserForFileIfNotSpecified,
528 bool showMessageOnFailure,
531 DoSaveAsInteractive&& doSaveAsInteractive,
532 DoAskToOverwriteFile&& doAskToOverwriteFile,
533 DoSaveDocument&& doSaveDocument)
535 if (parent.shouldExitAsyncCallback())
538 if (newFile ==
File())
540 if (askUserForFileIfNotSpecified)
542 doSaveAsInteractive (parent,
true, std::move (callback));
553 auto saveInternalHelper = [parent,
556 showMessageOnFailure,
560 if (! parent.shouldExitAsyncCallback())
561 parent->saveInternal (parent,
563 showMessageOnFailure,
569 if (warnAboutOverwritingExistingFiles && newFile.exists())
571 auto afterAsking = [cb = std::move (callback),
572 saveInternalHelper] (SafeParentPointer ptr,
573 bool shouldOverwrite)
575 if (ptr.shouldExitAsyncCallback())
579 saveInternalHelper();
583 doAskToOverwriteFile (parent, newFile, std::move (afterAsking));
587 saveInternalHelper();
590 void saveAsAsyncImpl (SafeParentPointer parent,
592 bool warnAboutOverwritingExistingFiles,
593 bool askUserForFileIfNotSpecified,
594 bool showMessageOnFailure,
600 warnAboutOverwritingExistingFiles,
601 askUserForFileIfNotSpecified,
602 showMessageOnFailure,
603 std::move (callback),
605 [] (SafeParentPointer ptr,
bool warnAboutOverwriting,
auto cb)
608 ptr->saveAsInteractiveAsyncImpl (ptr, warnAboutOverwriting, std::move (cb));
610 [] (SafeParentPointer ptr,
const File& destination,
std::function<void (SafeParentPointer,
bool)> cb)
613 ptr->askToOverwriteFileAsync (ptr, destination, std::move (cb));
617 if (parent !=
nullptr)
618 parent->document.saveDocumentAsync (destination, std::move (cb));
623 void saveAsInteractiveAsyncImpl (SafeParentPointer parent,
624 bool warnAboutOverwritingExistingFiles,
627 if (parent ==
nullptr)
630 saveAsInteractiveImpl (parent,
631 warnAboutOverwritingExistingFiles,
632 std::move (callback),
633 [] (SafeParentPointer ptr,
bool warnAboutOverwriting,
auto cb)
636 ptr->getSaveAsFilenameAsync (ptr, warnAboutOverwriting, std::move (cb));
638 [] (SafeParentPointer ptr,
640 bool warnAboutOverwriting,
641 bool askUserForFileIfNotSpecified,
642 bool showMessageOnFailure,
647 ptr->saveAsAsyncImpl (ptr,
649 warnAboutOverwriting,
650 askUserForFileIfNotSpecified,
651 showMessageOnFailure,
655 [] (SafeParentPointer ptr,
const File& destination,
auto cb)
658 ptr->askToOverwriteFileAsync (ptr, destination, std::move (cb));
666 TRANS (
"File already exists"),
667 TRANS (
"There's already a file called: FLNM")
670 +
TRANS (
"Are you sure you want to overwrite it?"),
675 void askToOverwriteFileAsync (SafeParentPointer parent,
679 if (parent ==
nullptr)
683 [parent, cb = std::move (callback)] (
int r)
685 if (parent !=
nullptr)
686 NullCheckedInvocation::invoke (cb, parent, r != 1);
690 #if JUCE_MODAL_LOOPS_PERMITTED
691 bool askToOverwriteFileSync (
const File& newFile)
693 return AlertWindow::show (getAskToOverwriteFileOptions (newFile));
698 void getSaveAsFilenameAsync (SafeParentPointer parent,
699 bool warnAboutOverwritingExistingFiles,
702 asyncFc = getInteractiveFileChooser();
706 if (warnAboutOverwritingExistingFiles)
709 asyncFc->launchAsync (flags, [parent, cb = std::move (callback)] (
const FileChooser& fc)
716 template <
typename DoSelectFilename,
typename DoSaveAs,
typename DoAskToOverwriteFile>
717 void saveAsInteractiveImpl (SafeParentPointer parent,
718 bool warnAboutOverwritingExistingFiles,
720 DoSelectFilename&& doSelectFilename,
722 DoAskToOverwriteFile&& doAskToOverwriteFile)
724 doSelectFilename (parent,
725 warnAboutOverwritingExistingFiles,
728 cb = std::move (callback)] (SafeParentPointer parentPtr,
File chosen)
730 if (parentPtr.shouldExitAsyncCallback())
733 if (chosen ==
File{})
739 auto updateAndSaveAs = [parentPtr, saveAs, cb] (
const File& chosenFile)
741 if (parentPtr.shouldExitAsyncCallback())
744 parentPtr->document.setLastDocumentOpened (chosenFile);
745 saveAs (parentPtr, chosenFile,
false,
false,
true, cb,
false);
754 auto afterAsking = [chosen, updateAndSaveAs, cb] (SafeParentPointer overwritePtr,
757 if (overwritePtr.shouldExitAsyncCallback())
761 updateAndSaveAs (chosen);
766 askToOverwriteFile (parentPtr, chosen, std::move (afterAsking));
771 updateAndSaveAs (chosen);
782 if (legalFilename.isEmpty())
783 legalFilename =
"unnamed";
785 f = (f.existsAsFile() || f.getParentDirectory().isDirectory())
786 ? f.getSiblingFile (legalFilename)
797 #if JUCE_MODAL_LOOPS_PERMITTED
798 struct SaveAsInteractiveSyncImpl
800 template <
typename... Ts>
801 void operator() (Ts&&... ts)
const noexcept
809 struct AskToOverwriteFileSync
811 template <
typename... Ts>
812 void operator() (Ts&&... ts)
const noexcept
820 struct AskToSaveChangesSync
822 template <
typename... Ts>
823 void operator() (Ts&&... ts)
const noexcept
833 template <
typename... Ts>
834 void operator() (Ts&&... ts)
const noexcept
842 struct GetSaveAsFilenameSync
844 template <
typename... Ts>
845 void operator() (Ts&&... ts)
const noexcept
853 struct SaveAsSyncImpl
855 template <
typename... Ts>
856 void operator() (Ts&&... ts)
const noexcept
865 void saveAsSyncImpl (SafeParentPointer parent,
867 bool warnAboutOverwritingExistingFiles,
868 bool askUserForFileIfNotSpecified,
869 bool showMessageOnFailure,
875 warnAboutOverwritingExistingFiles,
876 askUserForFileIfNotSpecified,
877 showMessageOnFailure,
878 std::move (callback),
880 SaveAsInteractiveSyncImpl { *
this },
881 AskToOverwriteFileSync { *
this },
882 [
this] (
const File& file,
const auto& cb) { cb (document.
saveDocument (file)); });
886 template <
typename Callback>
887 void askToSaveChangesSync (SafeParentPointer parent, Callback&& callback)
889 callback (parent, askToSaveChangesSync());
893 void saveAsInteractiveSyncImpl (SafeParentPointer parent,
894 bool warnAboutOverwritingExistingFiles,
897 saveAsInteractiveImpl (parent,
898 warnAboutOverwritingExistingFiles,
899 std::move (callback),
900 GetSaveAsFilenameSync { *
this },
901 SaveAsSyncImpl { *
this },
902 AskToOverwriteFileSync { *
this });
906 template <
typename Callback>
907 void askToOverwriteFileSync (SafeParentPointer parent,
911 callback (parent, askToOverwriteFileSync (newFile));
915 template <
typename Callback>
916 void saveSync (
bool askUserForFileIfNotSpecified,
917 bool showMessageOnFailure,
920 callback (save (askUserForFileIfNotSpecified, showMessageOnFailure));
924 template <
typename Callback>
925 void getSaveAsFilenameSync (SafeParentPointer parent,
926 bool warnAboutOverwritingExistingFiles,
929 auto fc = getInteractiveFileChooser();
931 if (fc->browseForFileToSave (warnAboutOverwritingExistingFiles))
933 callback (parent, fc->getResult());
937 callback (parent, {});
945 bool changedSinceSave =
false;
946 String fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle;