JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_FileBasedDocument.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
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.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29//==============================================================================
31{
32private:
33 //==============================================================================
34 class SafeParentPointer
35 {
36 public:
37 SafeParentPointer (Pimpl* parent, bool isAsync)
38 : ptr (parent), shouldCheck (isAsync)
39 {}
40
41 Pimpl* operator->() const noexcept
42 {
43 return ptr.get();
44 }
45
46 bool operator== (Pimpl* object) const noexcept { return ptr.get() == object; }
47 bool operator!= (Pimpl* object) const noexcept { return ptr.get() != object; }
48
49 bool shouldExitAsyncCallback() const noexcept
50 {
51 return shouldCheck && ptr == nullptr;
52 }
53
54 private:
56 bool shouldCheck = false;
57 };
58
59public:
60 //==============================================================================
63 const String& fileWildcard_,
66 : document (parent_),
67 fileExtension (fileExtension_),
68 fileWildcard (fileWildcard_),
69 openFileDialogTitle (openFileDialogTitle_),
70 saveFileDialogTitle (saveFileDialogTitle_)
71 {
72 }
73
74 //==============================================================================
75 bool hasChangedSinceSaved() const
76 {
77 return changedSinceSave;
78 }
79
80 void setChangedFlag (bool hasChanged)
81 {
82 if (changedSinceSave != hasChanged)
83 {
84 changedSinceSave = hasChanged;
85 document.sendChangeMessage();
86 }
87 }
88
89 void changed()
90 {
91 changedSinceSave = true;
92 document.sendChangeMessage();
93 }
94
95 //==============================================================================
96 Result loadFrom (const File& newFile, bool showMessageOnFailure, bool showWaitCursor = true)
97 {
98 SafeParentPointer parent { this, false };
99 auto result = Result::ok();
100 loadFromImpl (parent,
101 newFile,
103 showWaitCursor,
104 [this] (const File& file, const auto& callback) { callback (document.loadDocument (file)); },
105 [&result] (Result r) { result = r; });
106 return result;
107 }
108
109 void loadFromAsync (const File& newFile,
111 std::function<void (Result)> callback)
112 {
113 SafeParentPointer parent { this, true };
114 loadFromImpl (parent,
115 newFile,
117 false,
118 [parent] (const File& file, auto cb)
119 {
120 if (parent != nullptr)
121 parent->document.loadDocumentAsync (file, std::move (cb));
122 },
123 std::move (callback));
124 }
125
126 //==============================================================================
127 #if JUCE_MODAL_LOOPS_PERMITTED
129 {
130 FileChooser fc (openFileDialogTitle,
131 document.getLastDocumentOpened(),
132 fileWildcard);
133
134 if (fc.browseForFileToOpen())
135 return loadFrom (fc.getResult(), showMessageOnFailure);
136
137 return Result::fail (TRANS ("User cancelled"));
138 }
139 #endif
140
141 void loadFromUserSpecifiedFileAsync (const bool showMessageOnFailure, std::function<void (Result)> callback)
142 {
143 asyncFc = std::make_unique<FileChooser> (openFileDialogTitle,
144 document.getLastDocumentOpened(),
145 fileWildcard);
146
148 [this, showMessageOnFailure, cb = std::move (callback)] (const FileChooser& fc)
149 {
150 auto chosenFile = fc.getResult();
151
152 if (chosenFile == File{})
153 {
154 NullCheckedInvocation::invoke (cb, Result::fail (TRANS ("User cancelled")));
155 return;
156 }
157
158 WeakReference<Pimpl> parent { this };
159 loadFromAsync (chosenFile, showMessageOnFailure, [parent, cb] (Result result)
160 {
161 if (parent != nullptr)
162 NullCheckedInvocation::invoke (cb, result);
163 });
164
165 asyncFc = nullptr;
166 });
167 }
168
169 //==============================================================================
170 #if JUCE_MODAL_LOOPS_PERMITTED
173 {
174 return saveAs (documentFile,
175 false,
178 }
179 #endif
180
181 void saveAsync (bool askUserForFileIfNotSpecified,
183 std::function<void (SaveResult)> callback)
184 {
185 saveAsAsync (documentFile,
186 false,
189 std::move (callback));
190 }
191
192 //==============================================================================
193 #if JUCE_MODAL_LOOPS_PERMITTED
195 {
196 SafeParentPointer parent { this, false };
197 SaveResult result;
198 saveIfNeededAndUserAgreesImpl (parent,
199 [&result] (SaveResult r) { result = r; },
200 AskToSaveChangesSync { *this },
201 SaveSync { *this });
202 return result;
203 }
204 #endif
205
206 void saveIfNeededAndUserAgreesAsync (std::function<void (SaveResult)> callback)
207 {
208 SafeParentPointer parent { this, true };
209
210 saveIfNeededAndUserAgreesImpl (parent,
211 std::move (callback),
212 [] (SafeParentPointer ptr, auto cb)
213 {
214 if (ptr != nullptr)
215 ptr->askToSaveChangesAsync (ptr, std::move (cb));
216 },
217 [parent] (bool askUserForFileIfNotSpecified,
219 auto cb)
220 {
221 if (parent != nullptr)
222 parent->saveAsync (askUserForFileIfNotSpecified,
224 std::move (cb));
225 });
226 }
227
228 //==============================================================================
229 #if JUCE_MODAL_LOOPS_PERMITTED
231 bool warnAboutOverwritingExistingFiles,
234 bool showWaitCursor = true)
235 {
236 SafeParentPointer parent { this, false };
237 SaveResult result{};
238 saveAsSyncImpl (parent,
239 newFile,
240 warnAboutOverwritingExistingFiles,
243 [&result] (SaveResult r) { result = r; },
244 showWaitCursor);
245 return result;
246 }
247 #endif
248
249 void saveAsAsync (const File& newFile,
250 bool warnAboutOverwritingExistingFiles,
253 std::function<void (SaveResult)> callback)
254 {
255 SafeParentPointer parent { this, true };
256 saveAsAsyncImpl (parent,
257 newFile,
258 warnAboutOverwritingExistingFiles,
261 std::move (callback),
262 false);
263 }
264
265 //==============================================================================
266 #if JUCE_MODAL_LOOPS_PERMITTED
267 FileBasedDocument::SaveResult saveAsInteractive (bool warnAboutOverwritingExistingFiles)
268 {
269 SafeParentPointer parent { this, false };
270 SaveResult result{};
272 warnAboutOverwritingExistingFiles,
273 [&result] (SaveResult r) { result = r; });
274 return result;
275 }
276 #endif
277
278 void saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles,
279 std::function<void (SaveResult)> callback)
280 {
281 SafeParentPointer parent { this, true };
282 saveAsInteractiveAsyncImpl (parent,
283 warnAboutOverwritingExistingFiles,
284 std::move (callback));
285 }
286
287 //==============================================================================
288 const File& getFile() const
289 {
290 return documentFile;
291 }
292
293 void setFile (const File& newFile)
294 {
295 if (documentFile != newFile)
296 {
297 documentFile = newFile;
298 changed();
299 }
300 }
301
302 //==============================================================================
303 const String& getFileExtension() const
304 {
305 return fileExtension;
306 }
307
308private:
309 //==============================================================================
310 template <typename DoLoadDocument>
311 void loadFromImpl (SafeParentPointer parent,
312 const File& newFile,
314 bool showWaitCursor,
317 {
318 if (parent.shouldExitAsyncCallback())
319 return;
320
321 if (showWaitCursor)
323
324 auto oldFile = documentFile;
325 documentFile = newFile;
326
327 auto tidyUp = [parent, newFile, oldFile, showMessageOnFailure, showWaitCursor, completed] (Result result)
328 {
329 if (parent.shouldExitAsyncCallback())
330 return;
331
332 parent->documentFile = oldFile;
333
334 if (showWaitCursor)
336
338 {
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())
343 + "\n\n"
344 + result.getErrorMessage());
345 parent->messageBox = AlertWindow::showScopedAsync (options, nullptr);
346 }
347
348 NullCheckedInvocation::invoke (completed, result);
349 };
350
351 if (newFile.existsAsFile())
352 {
353 auto afterLoading = [parent,
354 showWaitCursor,
355 newFile,
356 cb = std::move (completed),
357 tidyUp] (Result result)
358 {
359 if (result.wasOk())
360 {
361 parent->setChangedFlag (false);
362
363 if (showWaitCursor)
365
366 parent->document.setLastDocumentOpened (newFile);
367 NullCheckedInvocation::invoke (cb, result);
368 return;
369 }
370
371 tidyUp (result);
372 };
373
374 doLoadDocument (newFile, std::move (afterLoading));
375
376 return;
377 }
378
379 tidyUp (Result::fail (TRANS ("The file doesn't exist")));
380 }
381
382 //==============================================================================
383 template <typename DoAskToSaveChanges, typename DoSave>
384 void saveIfNeededAndUserAgreesImpl (SafeParentPointer parent,
387 DoSave&& doSave)
388 {
389 if (parent.shouldExitAsyncCallback())
390 return;
391
392 if (! hasChangedSinceSaved())
393 {
394 NullCheckedInvocation::invoke (completed, savedOk);
395 return;
396 }
397
398 auto afterAsking = [save = std::forward<DoSave> (doSave),
399 cb = std::move (completed)] (SafeParentPointer ptr,
400 int alertResult)
401 {
402 if (ptr.shouldExitAsyncCallback())
403 return;
404
405 switch (alertResult)
406 {
407 case 1: // save changes
408 save (true, true, [ptr, cb] (SaveResult result)
409 {
410 if (ptr.shouldExitAsyncCallback())
411 return;
412
413 NullCheckedInvocation::invoke (cb, result);
414 });
415 return;
416
417 case 2: // discard changes
418 NullCheckedInvocation::invoke (cb, savedOk);
419 return;
420 }
421
422 NullCheckedInvocation::invoke (cb, userCancelledSave);
423 };
424
425 doAskToSaveChanges (parent, std::move (afterAsking));
426 }
427
428 //==============================================================================
429 MessageBoxOptions getAskToSaveChangesOptions() const
430 {
432 TRANS ("Closing document..."),
433 TRANS ("Do you want to save the changes to \"DCNM\"?")
434 .replace ("DCNM", document.getDocumentTitle()),
435 TRANS ("Save"),
436 TRANS ("Discard changes"),
437 TRANS ("Cancel"));
438 }
439
440 void askToSaveChangesAsync (SafeParentPointer parent,
441 std::function<void (SafeParentPointer, int)> callback)
442 {
443 messageBox = AlertWindow::showScopedAsync (getAskToSaveChangesOptions(),
444 [parent, cb = std::move (callback)] (int alertResult)
445 {
446 if (parent != nullptr)
447 cb (parent, alertResult);
448 });
449 }
450
451 #if JUCE_MODAL_LOOPS_PERMITTED
453 {
454 return AlertWindow::show (getAskToSaveChangesOptions());
455 }
456 #endif
457
458 //==============================================================================
459 template <typename DoSaveDocument>
460 void saveInternal (SafeParentPointer parent,
461 const File& newFile,
463 bool showWaitCursor,
466 {
467 if (showWaitCursor)
469
470 auto oldFile = documentFile;
471 documentFile = newFile;
472
473 doSaveDocument (newFile, [parent,
475 showWaitCursor,
476 oldFile,
477 newFile,
478 after = std::move (afterSave)] (Result result)
479 {
480 if (parent.shouldExitAsyncCallback())
481 {
482 if (showWaitCursor)
483 MouseCursor::hideWaitCursor();
484
485 return;
486 }
487
488 if (result.wasOk())
489 {
490 parent->setChangedFlag (false);
491
492 if (showWaitCursor)
493 MouseCursor::hideWaitCursor();
494
495 parent->document.sendChangeMessage(); // because the filename may have changed
496
497 NullCheckedInvocation::invoke (after, savedOk);
498 return;
499 }
500
501 parent->documentFile = oldFile;
502
503 if (showWaitCursor)
505
507 {
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())
512 .replace ("FLNM", "\n" + newFile.getFullPathName())
513 + "\n\n"
514 + result.getErrorMessage());
515 parent->messageBox = AlertWindow::showScopedAsync (options, nullptr);
516 }
517
518 parent->document.sendChangeMessage(); // because the filename may have changed
519 NullCheckedInvocation::invoke (after, failedToWriteToFile);
520 });
521 }
522
523 template <typename DoSaveAsInteractive, typename DoAskToOverwriteFile, typename DoSaveDocument>
524 void saveAsImpl (SafeParentPointer parent,
525 const File& newFile,
526 bool warnAboutOverwritingExistingFiles,
529 std::function<void (SaveResult)> callback,
530 bool showWaitCursor,
534 {
535 if (parent.shouldExitAsyncCallback())
536 return;
537
538 if (newFile == File())
539 {
541 {
542 doSaveAsInteractive (parent, true, std::move (callback));
543 return;
544 }
545
546 // can't save to an unspecified file
548
549 NullCheckedInvocation::invoke (callback, failedToWriteToFile);
550 return;
551 }
552
553 auto saveInternalHelper = [parent,
554 callback,
555 newFile,
557 showWaitCursor,
558 saveDocument = std::forward<DoSaveDocument> (doSaveDocument)]
559 {
560 if (! parent.shouldExitAsyncCallback())
561 parent->saveInternal (parent,
562 newFile,
564 showWaitCursor,
565 callback,
567 };
568
569 if (warnAboutOverwritingExistingFiles && newFile.exists())
570 {
571 auto afterAsking = [cb = std::move (callback),
572 saveInternalHelper] (SafeParentPointer ptr,
573 bool shouldOverwrite)
574 {
575 if (ptr.shouldExitAsyncCallback())
576 return;
577
578 if (shouldOverwrite)
580 else
581 NullCheckedInvocation::invoke (cb, userCancelledSave);
582 };
583 doAskToOverwriteFile (parent, newFile, std::move (afterAsking));
584 return;
585 }
586
588 }
589
590 void saveAsAsyncImpl (SafeParentPointer parent,
591 const File& newFile,
592 bool warnAboutOverwritingExistingFiles,
595 std::function<void (SaveResult)> callback,
596 bool showWaitCursor)
597 {
598 saveAsImpl (parent,
599 newFile,
600 warnAboutOverwritingExistingFiles,
603 std::move (callback),
604 showWaitCursor,
605 [] (SafeParentPointer ptr, bool warnAboutOverwriting, auto cb)
606 {
607 if (ptr != nullptr)
608 ptr->saveAsInteractiveAsyncImpl (ptr, warnAboutOverwriting, std::move (cb));
609 },
610 [] (SafeParentPointer ptr, const File& destination, std::function<void (SafeParentPointer, bool)> cb)
611 {
612 if (ptr != nullptr)
613 ptr->askToOverwriteFileAsync (ptr, destination, std::move (cb));
614 },
615 [parent] (const File& destination, std::function<void (Result)> cb)
616 {
617 if (parent != nullptr)
618 parent->document.saveDocumentAsync (destination, std::move (cb));
619 });
620 }
621
622 //==============================================================================
623 void saveAsInteractiveAsyncImpl (SafeParentPointer parent,
624 bool warnAboutOverwritingExistingFiles,
625 std::function<void (SaveResult)> callback)
626 {
627 if (parent == nullptr)
628 return;
629
630 saveAsInteractiveImpl (parent,
631 warnAboutOverwritingExistingFiles,
632 std::move (callback),
633 [] (SafeParentPointer ptr, bool warnAboutOverwriting, auto cb)
634 {
635 if (ptr != nullptr)
636 ptr->getSaveAsFilenameAsync (ptr, warnAboutOverwriting, std::move (cb));
637 },
638 [] (SafeParentPointer ptr,
639 const File& newFile,
640 bool warnAboutOverwriting,
643 auto cb,
644 bool showWaitCursor)
645 {
646 if (ptr != nullptr)
647 ptr->saveAsAsyncImpl (ptr,
648 newFile,
649 warnAboutOverwriting,
652 std::move (cb),
653 showWaitCursor);
654 },
655 [] (SafeParentPointer ptr, const File& destination, auto cb)
656 {
657 if (ptr != nullptr)
658 ptr->askToOverwriteFileAsync (ptr, destination, std::move (cb));
659 });
660 }
661
662 //==============================================================================
663 MessageBoxOptions getAskToOverwriteFileOptions (const File& newFile) const
664 {
666 TRANS ("File already exists"),
667 TRANS ("There's already a file called: FLNM")
668 .replace ("FLNM", newFile.getFullPathName())
669 + "\n\n"
670 + TRANS ("Are you sure you want to overwrite it?"),
671 TRANS ("Overwrite"),
672 TRANS ("Cancel"));
673 }
674
675 void askToOverwriteFileAsync (SafeParentPointer parent,
676 const File& newFile,
677 std::function<void (SafeParentPointer, bool)> callback)
678 {
679 if (parent == nullptr)
680 return;
681
682 messageBox = AlertWindow::showScopedAsync (getAskToOverwriteFileOptions (newFile),
683 [parent, cb = std::move (callback)] (int r)
684 {
685 if (parent != nullptr)
686 NullCheckedInvocation::invoke (cb, parent, r != 1);
687 });
688 }
689
690 #if JUCE_MODAL_LOOPS_PERMITTED
692 {
693 return AlertWindow::show (getAskToOverwriteFileOptions (newFile));
694 }
695 #endif
696
697 //==============================================================================
698 void getSaveAsFilenameAsync (SafeParentPointer parent,
699 bool warnAboutOverwritingExistingFiles,
700 std::function<void (SafeParentPointer, const File&)> callback)
701 {
702 asyncFc = getInteractiveFileChooser();
703
705
706 if (warnAboutOverwritingExistingFiles)
708
709 asyncFc->launchAsync (flags, [parent, cb = std::move (callback)] (const FileChooser& fc)
710 {
711 cb (parent, fc.getResult());
712 });
713 }
714
715 //==============================================================================
716 template <typename DoSelectFilename, typename DoSaveAs, typename DoAskToOverwriteFile>
717 void saveAsInteractiveImpl (SafeParentPointer parent,
718 bool warnAboutOverwritingExistingFiles,
719 std::function<void (SaveResult)> callback,
723 {
724 doSelectFilename (parent,
725 warnAboutOverwritingExistingFiles,
726 [saveAs = std::forward<DoSaveAs> (doSaveAs),
727 askToOverwriteFile = std::forward<DoAskToOverwriteFile> (doAskToOverwriteFile),
728 cb = std::move (callback)] (SafeParentPointer parentPtr, File chosen)
729 {
730 if (parentPtr.shouldExitAsyncCallback())
731 return;
732
733 if (chosen == File{})
734 {
735 NullCheckedInvocation::invoke (cb, userCancelledSave);
736 return;
737 }
738
740 {
741 if (parentPtr.shouldExitAsyncCallback())
742 return;
743
744 parentPtr->document.setLastDocumentOpened (chosenFile);
745 saveAs (parentPtr, chosenFile, false, false, true, cb, false);
746 };
747
748 if (chosen.getFileExtension().isEmpty())
749 {
750 chosen = chosen.withFileExtension (parentPtr->fileExtension);
751
752 if (chosen.exists())
753 {
754 auto afterAsking = [chosen, updateAndSaveAs, cb] (SafeParentPointer overwritePtr,
755 bool overwrite)
756 {
757 if (overwritePtr.shouldExitAsyncCallback())
758 return;
759
760 if (overwrite)
762 else
763 NullCheckedInvocation::invoke (cb, userCancelledSave);
764 };
765
767 return;
768 }
769 }
770
772 });
773 }
774
775 //==============================================================================
776 std::unique_ptr<FileChooser> getInteractiveFileChooser()
777 {
778 auto f = documentFile.existsAsFile() ? documentFile : document.getLastDocumentOpened();
779
781
782 if (legalFilename.isEmpty())
783 legalFilename = "unnamed";
784
785 f = (f.existsAsFile() || f.getParentDirectory().isDirectory())
786 ? f.getSiblingFile (legalFilename)
788
789 f = document.getSuggestedSaveAsFile (f);
790
791 return std::make_unique<FileChooser> (saveFileDialogTitle,
792 f,
793 fileWildcard);
794 }
795
796 //==============================================================================
797 #if JUCE_MODAL_LOOPS_PERMITTED
799 {
800 template <typename... Ts>
801 void operator() (Ts&&... ts) const noexcept
802 {
803 p.saveAsInteractiveSyncImpl (std::forward<Ts> (ts)...);
804 }
805
806 Pimpl& p;
807 };
808
810 {
811 template <typename... Ts>
812 void operator() (Ts&&... ts) const noexcept
813 {
814 p.askToOverwriteFileSync (std::forward<Ts> (ts)...);
815 }
816
817 Pimpl& p;
818 };
819
821 {
822 template <typename... Ts>
823 void operator() (Ts&&... ts) const noexcept
824 {
825 p.askToSaveChangesSync (std::forward<Ts> (ts)...);
826 }
827
828 Pimpl& p;
829 };
830
831 struct SaveSync
832 {
833 template <typename... Ts>
834 void operator() (Ts&&... ts) const noexcept
835 {
836 p.saveSync (std::forward<Ts> (ts)...);
837 }
838
839 Pimpl& p;
840 };
841
843 {
844 template <typename... Ts>
845 void operator() (Ts&&... ts) const noexcept
846 {
847 p.getSaveAsFilenameSync (std::forward<Ts> (ts)...);
848 }
849
850 Pimpl& p;
851 };
852
853 struct SaveAsSyncImpl
854 {
855 template <typename... Ts>
856 void operator() (Ts&&... ts) const noexcept
857 {
858 p.saveAsSyncImpl (std::forward<Ts> (ts)...);
859 }
860
861 Pimpl& p;
862 };
863
864 //==============================================================================
865 void saveAsSyncImpl (SafeParentPointer parent,
866 const File& newFile,
867 bool warnAboutOverwritingExistingFiles,
870 std::function<void (SaveResult)> callback,
871 bool showWaitCursor)
872 {
873 saveAsImpl (parent,
874 newFile,
875 warnAboutOverwritingExistingFiles,
878 std::move (callback),
879 showWaitCursor,
881 AskToOverwriteFileSync { *this },
882 [this] (const File& file, const auto& cb) { cb (document.saveDocument (file)); });
883 }
884
885 //==============================================================================
886 template <typename Callback>
887 void askToSaveChangesSync (SafeParentPointer parent, Callback&& callback)
888 {
889 callback (parent, askToSaveChangesSync());
890 }
891
892 //==============================================================================
893 void saveAsInteractiveSyncImpl (SafeParentPointer parent,
894 bool warnAboutOverwritingExistingFiles,
895 std::function<void (SaveResult)> callback)
896 {
897 saveAsInteractiveImpl (parent,
898 warnAboutOverwritingExistingFiles,
899 std::move (callback),
900 GetSaveAsFilenameSync { *this },
901 SaveAsSyncImpl { *this },
902 AskToOverwriteFileSync { *this });
903 }
904
905 //==============================================================================
906 template <typename Callback>
907 void askToOverwriteFileSync (SafeParentPointer parent,
908 const File& newFile,
909 Callback&& callback)
910 {
911 callback (parent, askToOverwriteFileSync (newFile));
912 }
913
914 //==============================================================================
915 template <typename Callback>
918 Callback&& callback)
919 {
921 }
922
923 //==============================================================================
924 template <typename Callback>
925 void getSaveAsFilenameSync (SafeParentPointer parent,
926 bool warnAboutOverwritingExistingFiles,
927 Callback&& callback)
928 {
929 auto fc = getInteractiveFileChooser();
930
931 if (fc->browseForFileToSave (warnAboutOverwritingExistingFiles))
932 {
933 callback (parent, fc->getResult());
934 return;
935 }
936
937 callback (parent, {});
938 }
939 #endif
940
941 //==============================================================================
942 FileBasedDocument& document;
943
944 File documentFile;
945 bool changedSinceSave = false;
946 String fileExtension, fileWildcard, openFileDialogTitle, saveFileDialogTitle;
948 ScopedMessageBox messageBox;
949
952};
953
954//==============================================================================
956 const String& fileWildcard,
957 const String& openFileDialogTitle,
958 const String& saveFileDialogTitle)
959 : pimpl (new Pimpl (*this,
960 fileExtension,
961 fileWildcard,
962 openFileDialogTitle,
963 saveFileDialogTitle))
964{
965}
966
968
969//==============================================================================
971{
972 return pimpl->hasChangedSinceSaved();
973}
974
976{
977 pimpl->setChangedFlag (hasChanged);
978}
979
981{
982 pimpl->changed();
983}
984
985//==============================================================================
988 bool showWaitCursor)
989{
990 return pimpl->loadFrom (fileToLoadFrom, showMessageOnFailure, showWaitCursor);
991}
992
995 std::function<void (Result)> callback)
996{
997 pimpl->loadFromAsync (fileToLoadFrom, showMessageOnFailure, std::move (callback));
998}
999
1000//==============================================================================
1001#if JUCE_MODAL_LOOPS_PERMITTED
1002Result FileBasedDocument::loadFromUserSpecifiedFile (bool showMessageOnFailure)
1003{
1004 return pimpl->loadFromUserSpecifiedFile (showMessageOnFailure);
1005}
1006#endif
1007
1009 std::function<void (Result)> callback)
1010{
1011 pimpl->loadFromUserSpecifiedFileAsync (showMessageOnFailure, std::move (callback));
1012}
1013
1014//==============================================================================
1015#if JUCE_MODAL_LOOPS_PERMITTED
1018{
1020}
1021#endif
1022
1025 std::function<void (SaveResult)> callback)
1026{
1027 pimpl->saveAsync (askUserForFileIfNotSpecified, showMessageOnFailure, std::move (callback));
1028}
1029
1030//==============================================================================
1031#if JUCE_MODAL_LOOPS_PERMITTED
1032FileBasedDocument::SaveResult FileBasedDocument::saveIfNeededAndUserAgrees()
1033{
1034 return pimpl->saveIfNeededAndUserAgrees();
1035}
1036#endif
1037
1039{
1040 pimpl->saveIfNeededAndUserAgreesAsync (std::move (callback));
1041}
1042
1043//==============================================================================
1044#if JUCE_MODAL_LOOPS_PERMITTED
1045FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile,
1046 bool warnAboutOverwritingExistingFiles,
1049 bool showWaitCursor)
1050{
1051 return pimpl->saveAs (newFile,
1052 warnAboutOverwritingExistingFiles,
1055 showWaitCursor);
1056}
1057#endif
1058
1060 bool warnAboutOverwritingExistingFiles,
1063 std::function<void (SaveResult)> callback)
1064{
1065 pimpl->saveAsAsync (newFile,
1066 warnAboutOverwritingExistingFiles,
1069 std::move (callback));
1070}
1071
1072//==============================================================================
1073#if JUCE_MODAL_LOOPS_PERMITTED
1074FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (bool warnAboutOverwritingExistingFiles)
1075{
1076 return pimpl->saveAsInteractive (warnAboutOverwritingExistingFiles);
1077}
1078#endif
1079
1080void FileBasedDocument::saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles,
1081 std::function<void (SaveResult)> callback)
1082{
1083 pimpl->saveAsInteractiveAsync (warnAboutOverwritingExistingFiles, std::move (callback));
1084}
1085
1086//==============================================================================
1088{
1089 return pimpl->getFile();
1090}
1091
1093{
1094 pimpl->setFile (newFile);
1095}
1096
1097//==============================================================================
1099{
1100 const auto result = loadDocument (file);
1101 NullCheckedInvocation::invoke (callback, result);
1102}
1103
1105{
1106 const auto result = saveDocument (file);
1107 NullCheckedInvocation::invoke (callback, result);
1108}
1109
1111{
1112 return defaultFile.withFileExtension (pimpl->getFileExtension()).getNonexistentSibling (true);
1113}
1114
1115} // namespace juce
static ScopedMessageBox showScopedAsync(const MessageBoxOptions &options, std::function< void(int)> callback)
Shows an alert window using the specified options.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
A class to take care of the logic involved with the loading/saving of some kind of document.
void loadFromAsync(const File &fileToLoadFrom, bool showMessageOnFailure, std::function< void(Result)> callback)
Tries to open a file.
virtual Result loadDocument(const File &file)=0
This method should try to load your document from the given file.
void saveIfNeededAndUserAgreesAsync(std::function< void(SaveResult)> callback)
If the file needs saving, it'll ask the user if that's what they want to do, and save it if they say ...
void saveAsync(bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function< void(SaveResult)> callback)
Tries to save the document to the last file it was saved or loaded from.
~FileBasedDocument() override
Destructor.
virtual void changed()
Called to indicate that the document has changed and needs saving.
bool hasChangedSinceSaved() const
Returns true if the changed() method has been called since the file was last saved or loaded.
virtual void loadDocumentAsync(const File &file, std::function< void(Result)> callback)
This method should try to load your document from the given file, then call the provided callback on ...
void loadFromUserSpecifiedFileAsync(bool showMessageOnFailure, std::function< void(Result)> callback)
Asks the user for a file and tries to load it.
virtual File getSuggestedSaveAsFile(const File &defaultFile)
This is called by saveAsInteractiveAsync() to allow you to optionally customise the filename that the...
virtual String getDocumentTitle()=0
Overload this to return the title of the document.
Result loadFrom(const File &fileToLoadFrom, bool showMessageOnFailure, bool showWaitCursor=true)
Tries to open a file.
virtual void saveDocumentAsync(const File &file, std::function< void(Result)> callback)
This method should try to write your document to the given file, then call the provided callback on t...
SaveResult
A set of possible outcomes of one of the save() methods.
@ userCancelledSave
indicates that the user aborted the save operation.
@ failedToWriteToFile
indicates that it tried to write to a file but this failed.
@ savedOk
indicates that a file was saved successfully.
void setChangedFlag(bool hasChanged)
Sets the state of the 'changed' flag.
void saveAsInteractiveAsync(bool warnAboutOverwritingExistingFiles, std::function< void(SaveResult)> callback)
Prompts the user for a filename and tries to save to it.
virtual Result saveDocument(const File &file)=0
This method should try to write your document to the given file.
const File & getFile() const
Returns the file that this document was last successfully saved or loaded from.
void setFile(const File &newFile)
Sets the file that this document thinks it was loaded from.
virtual File getLastDocumentOpened()=0
This is used for dialog boxes to make them open at the last folder you were using.
FileBasedDocument(const String &fileExtension, const String &fileWildCard, const String &openFileDialogTitle, const String &saveFileDialogTitle)
Creates a FileBasedDocument.
void saveAsAsync(const File &newFile, bool warnAboutOverwritingExistingFiles, bool askUserForFileIfNotSpecified, bool showMessageOnFailure, std::function< void(SaveResult)> callback)
Tries to save the document to a specified file.
@ saveMode
specifies that the component should allow the user to specify the name of a file that will be used to...
@ warnAboutOverwriting
specifies that the dialog should warn about overwriting existing files (if possible).
@ canSelectFiles
specifies that the user can select files (can be used in conjunction with canSelectDirectories).
@ openMode
specifies that the component should allow the user to choose an existing file with the intention of o...
Creates a dialog box to choose a file or directory to load or save.
Represents a local file or directory.
Definition juce_File.h:45
bool existsAsFile() const
Checks whether the file exists and is a file rather than a directory.
File getChildFile(StringRef relativeOrAbsolutePath) const
Returns a file that represents a relative (or absolute) sub-path of the current one.
File getNonexistentSibling(bool putNumbersInBrackets=true) const
Chooses a filename for a sibling file to this one that doesn't already exist.
@ userDocumentsDirectory
The user's default documents folder.
Definition juce_File.h:875
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
Finds the location of a special type of file or directory, such as a home folder or documents folder.
File withFileExtension(StringRef newExtension) const
Returns a version of this file with a different file extension.
static String createLegalFileName(const String &fileNameToFix)
Returns a version of a filename with any illegal characters removed.
Class used to create a set of options to pass to the AlertWindow and NativeMessageBox methods for sho...
static MessageBoxOptions makeOptionsYesNoCancel(MessageBoxIconType iconType, const String &title, const String &message, const String &button1Text=String(), const String &button2Text=String(), const String &button3Text=String(), Component *associatedComponent=nullptr)
Creates options suitable for a message box with three buttons.
static MessageBoxOptions makeOptionsOkCancel(MessageBoxIconType iconType, const String &title, const String &message, const String &button1Text=String(), const String &button2Text=String(), Component *associatedComponent=nullptr)
Creates options suitable for a message box with two buttons.
static MessageBoxOptions makeOptionsOk(MessageBoxIconType iconType, const String &title, const String &message, const String &buttonText=String(), Component *associatedComponent=nullptr)
Creates options suitable for a message box with a single button.
static void hideWaitCursor()
If showWaitCursor has been called, this will return the mouse to its normal state.
static void showWaitCursor()
Makes the system show its default 'busy' cursor.
Represents the 'success' or 'failure' of an operation, and holds an associated error message to descr...
Definition juce_Result.h:57
bool wasOk() const noexcept
Returns true if this result indicates a success.
const String & getErrorMessage() const noexcept
Returns the error message that was set when this result was created.
static Result fail(const String &errorMessage) noexcept
Creates a 'failure' result.
static Result ok() noexcept
Creates and returns a 'successful' result.
Definition juce_Result.h:61
Objects of this type can be used to programmatically close message boxes.
The JUCE String class!
Definition juce_String.h:53
This class acts as a pointer which will automatically become null if the object to which it points is...
#define TRANS(stringLiteral)
Uses the LocalisedStrings class to translate the given string literal.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
#define JUCE_DECLARE_WEAK_REFERENCEABLE(Class)
Macro to easily allow a class to be made weak-referenceable.
JUCE Namespace.
@ WarningIcon
An exclamation mark to indicate that the dialog is a warning about something and shouldn't be ignored...
@ QuestionIcon
A question-mark icon, for dialog boxes that need the user to answer a question.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88