1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - 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 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-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
26 namespace juce
27 {
28
FileBasedDocument(const String & fileExtension_,const String & fileWildcard_,const String & openFileDialogTitle_,const String & saveFileDialogTitle_)29 FileBasedDocument::FileBasedDocument (const String& fileExtension_,
30 const String& fileWildcard_,
31 const String& openFileDialogTitle_,
32 const String& saveFileDialogTitle_)
33 : fileExtension (fileExtension_),
34 fileWildcard (fileWildcard_),
35 openFileDialogTitle (openFileDialogTitle_),
36 saveFileDialogTitle (saveFileDialogTitle_)
37 {
38 }
39
~FileBasedDocument()40 FileBasedDocument::~FileBasedDocument()
41 {
42 }
43
44 //==============================================================================
setChangedFlag(bool hasChanged)45 void FileBasedDocument::setChangedFlag (bool hasChanged)
46 {
47 if (changedSinceSave != hasChanged)
48 {
49 changedSinceSave = hasChanged;
50 sendChangeMessage();
51 }
52 }
53
changed()54 void FileBasedDocument::changed()
55 {
56 changedSinceSave = true;
57 sendChangeMessage();
58 }
59
60 //==============================================================================
setFile(const File & newFile)61 void FileBasedDocument::setFile (const File& newFile)
62 {
63 if (documentFile != newFile)
64 {
65 documentFile = newFile;
66 changed();
67 }
68 }
69
70 //==============================================================================
loadFrom(const File & newFile,bool showMessageOnFailure,bool showWaitCursor)71 Result FileBasedDocument::loadFrom (const File& newFile, bool showMessageOnFailure, bool showWaitCursor)
72 {
73 if (showWaitCursor)
74 MouseCursor::showWaitCursor();
75
76 auto oldFile = documentFile;
77 documentFile = newFile;
78
79 auto result = Result::fail (TRANS("The file doesn't exist"));
80
81 if (newFile.existsAsFile())
82 {
83 result = loadDocument (newFile);
84
85 if (result.wasOk())
86 {
87 setChangedFlag (false);
88
89 if (showWaitCursor)
90 MouseCursor::hideWaitCursor();
91
92 setLastDocumentOpened (newFile);
93 return result;
94 }
95 }
96
97 documentFile = oldFile;
98
99 if (showWaitCursor)
100 MouseCursor::hideWaitCursor();
101
102 if (showMessageOnFailure)
103 AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
104 TRANS("Failed to open file..."),
105 TRANS("There was an error while trying to load the file: FLNM")
106 .replace ("FLNM", "\n" + newFile.getFullPathName())
107 + "\n\n"
108 + result.getErrorMessage());
109
110 return result;
111 }
112
113 #if JUCE_MODAL_LOOPS_PERMITTED
loadFromUserSpecifiedFile(const bool showMessageOnFailure)114 Result FileBasedDocument::loadFromUserSpecifiedFile (const bool showMessageOnFailure)
115 {
116 FileChooser fc (openFileDialogTitle,
117 getLastDocumentOpened(),
118 fileWildcard);
119
120 if (fc.browseForFileToOpen())
121 return loadFrom (fc.getResult(), showMessageOnFailure);
122
123 return Result::fail (TRANS("User cancelled"));
124 }
125
askToOverwriteFile(const File & newFile)126 static bool askToOverwriteFile (const File& newFile)
127 {
128 return AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
129 TRANS("File already exists"),
130 TRANS("There's already a file called: FLNM")
131 .replace ("FLNM", newFile.getFullPathName())
132 + "\n\n"
133 + TRANS("Are you sure you want to overwrite it?"),
134 TRANS("Overwrite"),
135 TRANS("Cancel"));
136 }
137
138 //==============================================================================
save(bool askUserForFileIfNotSpecified,bool showMessageOnFailure)139 FileBasedDocument::SaveResult FileBasedDocument::save (bool askUserForFileIfNotSpecified,
140 bool showMessageOnFailure)
141 {
142 return saveAs (documentFile,
143 false,
144 askUserForFileIfNotSpecified,
145 showMessageOnFailure);
146 }
147
saveAs(const File & newFile,bool warnAboutOverwritingExistingFiles,bool askUserForFileIfNotSpecified,bool showMessageOnFailure,bool showWaitCursor)148 FileBasedDocument::SaveResult FileBasedDocument::saveAs (const File& newFile,
149 bool warnAboutOverwritingExistingFiles,
150 bool askUserForFileIfNotSpecified,
151 bool showMessageOnFailure,
152 bool showWaitCursor)
153 {
154 if (newFile == File())
155 {
156 if (askUserForFileIfNotSpecified)
157 return saveAsInteractive (true);
158
159 // can't save to an unspecified file
160 jassertfalse;
161 return failedToWriteToFile;
162 }
163
164 if (warnAboutOverwritingExistingFiles
165 && newFile.exists()
166 && ! askToOverwriteFile (newFile))
167 return userCancelledSave;
168
169 if (showWaitCursor)
170 MouseCursor::showWaitCursor();
171
172 auto oldFile = documentFile;
173 documentFile = newFile;
174
175 auto result = saveDocument (newFile);
176
177 if (result.wasOk())
178 {
179 setChangedFlag (false);
180
181 if (showWaitCursor)
182 MouseCursor::hideWaitCursor();
183
184 sendChangeMessage(); // because the filename may have changed
185 return savedOk;
186 }
187
188 documentFile = oldFile;
189
190 if (showWaitCursor)
191 MouseCursor::hideWaitCursor();
192
193 if (showMessageOnFailure)
194 AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
195 TRANS("Error writing to file..."),
196 TRANS("An error occurred while trying to save \"DCNM\" to the file: FLNM")
197 .replace ("DCNM", getDocumentTitle())
198 .replace ("FLNM", "\n" + newFile.getFullPathName())
199 + "\n\n"
200 + result.getErrorMessage());
201
202 sendChangeMessage(); // because the filename may have changed
203 return failedToWriteToFile;
204 }
205
saveIfNeededAndUserAgrees()206 FileBasedDocument::SaveResult FileBasedDocument::saveIfNeededAndUserAgrees()
207 {
208 if (! hasChangedSinceSaved())
209 return savedOk;
210
211 auto r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
212 TRANS("Closing document..."),
213 TRANS("Do you want to save the changes to \"DCNM\"?")
214 .replace ("DCNM", getDocumentTitle()),
215 TRANS("Save"),
216 TRANS("Discard changes"),
217 TRANS("Cancel"));
218
219 if (r == 1) // save changes
220 return save (true, true);
221
222 if (r == 2) // discard changes
223 return savedOk;
224
225 return userCancelledSave;
226 }
227
getSuggestedSaveAsFile(const File & defaultFile)228 File FileBasedDocument::getSuggestedSaveAsFile (const File& defaultFile)
229 {
230 return defaultFile.withFileExtension (fileExtension).getNonexistentSibling (true);
231 }
232
saveAsInteractive(bool warnAboutOverwritingExistingFiles)233 FileBasedDocument::SaveResult FileBasedDocument::saveAsInteractive (bool warnAboutOverwritingExistingFiles)
234 {
235 auto f = documentFile.existsAsFile() ? documentFile : getLastDocumentOpened();
236
237 auto legalFilename = File::createLegalFileName (getDocumentTitle());
238
239 if (legalFilename.isEmpty())
240 legalFilename = "unnamed";
241
242 if (f.existsAsFile() || f.getParentDirectory().isDirectory())
243 f = f.getSiblingFile (legalFilename);
244 else
245 f = File::getSpecialLocation (File::userDocumentsDirectory).getChildFile (legalFilename);
246
247 f = getSuggestedSaveAsFile (f);
248
249 FileChooser fc (saveFileDialogTitle, f, fileWildcard);
250
251 if (fc.browseForFileToSave (warnAboutOverwritingExistingFiles))
252 {
253 auto chosen = fc.getResult();
254
255 if (chosen.getFileExtension().isEmpty())
256 {
257 chosen = chosen.withFileExtension (fileExtension);
258
259 if (chosen.exists() && ! askToOverwriteFile (chosen))
260 return userCancelledSave;
261 }
262
263 setLastDocumentOpened (chosen);
264 return saveAs (chosen, false, false, true);
265 }
266
267 return userCancelledSave;
268 }
269 #endif
270
271 } // namespace juce
272