432 lines
20 KiB
C
432 lines
20 KiB
C
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE library.
|
||
|
Copyright (c) 2020 - Raw Material Software Limited
|
||
|
|
||
|
JUCE is an open source library subject to commercial or open-source
|
||
|
licensing.
|
||
|
|
||
|
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||
|
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||
|
|
||
|
End User License Agreement: www.juce.com/juce-6-licence
|
||
|
Privacy Policy: www.juce.com/juce-privacy-policy
|
||
|
|
||
|
Or: You may also use this code under the terms of the GPL v3 (see
|
||
|
www.gnu.org/licenses).
|
||
|
|
||
|
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
|
||
|
{
|
||
|
|
||
|
//==============================================================================
|
||
|
/**
|
||
|
A class to take care of the logic involved with the loading/saving of some kind
|
||
|
of document.
|
||
|
|
||
|
There's quite a lot of tedious logic involved in writing all the load/save/save-as
|
||
|
functions you need for documents that get saved to a file, so this class attempts
|
||
|
to abstract most of the boring stuff.
|
||
|
|
||
|
Your subclass should just implement all the pure virtual methods, and you can
|
||
|
then use the higher-level public methods to do the load/save dialogs, to warn the user
|
||
|
about overwriting files, etc.
|
||
|
|
||
|
The document object keeps track of whether it has changed since it was last saved or
|
||
|
loaded, so when you change something, call its changed() method. This will set a
|
||
|
flag so it knows it needs saving, and will also broadcast a change message using the
|
||
|
ChangeBroadcaster base class.
|
||
|
|
||
|
@see ChangeBroadcaster
|
||
|
|
||
|
@tags{GUI}
|
||
|
*/
|
||
|
class JUCE_API FileBasedDocument : public ChangeBroadcaster
|
||
|
{
|
||
|
public:
|
||
|
/** Creates a FileBasedDocument.
|
||
|
|
||
|
@param fileExtension the extension to use when loading/saving files, e.g. ".doc"
|
||
|
@param fileWildCard the wildcard to use in file dialogs, e.g. "*.doc"
|
||
|
@param openFileDialogTitle the title to show on an open-file dialog, e.g. "Choose a file to open.."
|
||
|
@param saveFileDialogTitle the title to show on an save-file dialog, e.g. "Choose a file to save as.."
|
||
|
*/
|
||
|
FileBasedDocument (const String& fileExtension,
|
||
|
const String& fileWildCard,
|
||
|
const String& openFileDialogTitle,
|
||
|
const String& saveFileDialogTitle);
|
||
|
|
||
|
/** Destructor. */
|
||
|
~FileBasedDocument() override;
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Returns true if the changed() method has been called since the file was
|
||
|
last saved or loaded.
|
||
|
|
||
|
@see setChangedFlag, changed
|
||
|
*/
|
||
|
bool hasChangedSinceSaved() const;
|
||
|
|
||
|
/** Called to indicate that the document has changed and needs saving.
|
||
|
|
||
|
This method will also trigger a change message to be sent out using the
|
||
|
ChangeBroadcaster base class.
|
||
|
|
||
|
After calling the method, the hasChangedSinceSaved() method will return true, until
|
||
|
it is reset either by saving to a file or using the setChangedFlag() method.
|
||
|
|
||
|
@see hasChangedSinceSaved, setChangedFlag
|
||
|
*/
|
||
|
virtual void changed();
|
||
|
|
||
|
/** Sets the state of the 'changed' flag.
|
||
|
|
||
|
The 'changed' flag is set to true when the changed() method is called - use this method
|
||
|
to reset it or to set it without also broadcasting a change message.
|
||
|
|
||
|
@see changed, hasChangedSinceSaved
|
||
|
*/
|
||
|
void setChangedFlag (bool hasChanged);
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Tries to open a file.
|
||
|
|
||
|
If the file opens correctly the document's file (see the getFile() method) is set
|
||
|
to this new one; if it fails, the document's file is left unchanged, and optionally
|
||
|
a message box is shown telling the user there was an error.
|
||
|
|
||
|
@returns A result indicating whether the new file loaded successfully, or the error
|
||
|
message if it failed.
|
||
|
@see loadDocument, loadFromUserSpecifiedFile
|
||
|
*/
|
||
|
Result loadFrom (const File& fileToLoadFrom,
|
||
|
bool showMessageOnFailure,
|
||
|
bool showWaitCursor = true);
|
||
|
|
||
|
/** Tries to open a file.
|
||
|
|
||
|
The callback is called with the result indicating whether the new file loaded
|
||
|
successfully, or the error message if it failed.
|
||
|
|
||
|
If the file opens correctly the document's file (see the getFile() method) is set
|
||
|
to this new one; if it fails, the document's file is left unchanged, and optionally
|
||
|
a message box is shown telling the user there was an error.
|
||
|
|
||
|
@see loadDocumentAsync, loadFromUserSpecifiedFileAsync
|
||
|
*/
|
||
|
void loadFromAsync (const File& fileToLoadFrom,
|
||
|
bool showMessageOnFailure,
|
||
|
std::function<void (Result)> callback);
|
||
|
|
||
|
#if JUCE_MODAL_LOOPS_PERMITTED
|
||
|
/** Asks the user for a file and tries to load it.
|
||
|
|
||
|
This will pop up a dialog box using the title, file extension and
|
||
|
wildcard specified in the document's constructor, and asks the user
|
||
|
for a file. If they pick one, the loadFrom() method is used to
|
||
|
try to load it, optionally showing a message if it fails.
|
||
|
|
||
|
@returns a result indicating success; This will be a failure message if the user
|
||
|
cancelled or if they picked a file which failed to load correctly
|
||
|
@see loadFrom
|
||
|
*/
|
||
|
Result loadFromUserSpecifiedFile (bool showMessageOnFailure);
|
||
|
#endif
|
||
|
|
||
|
/** Asks the user for a file and tries to load it.
|
||
|
|
||
|
This will pop up a dialog box using the title, file extension and
|
||
|
wildcard specified in the document's constructor, and asks the user
|
||
|
for a file. If they pick one, the loadFrom() method is used to
|
||
|
try to load it, optionally showing a message if it fails. The result
|
||
|
of the operation is provided in the callback function.
|
||
|
|
||
|
@see loadFrom
|
||
|
*/
|
||
|
void loadFromUserSpecifiedFileAsync (bool showMessageOnFailure, std::function<void (Result)> callback);
|
||
|
|
||
|
//==============================================================================
|
||
|
/** A set of possible outcomes of one of the save() methods
|
||
|
*/
|
||
|
enum SaveResult
|
||
|
{
|
||
|
savedOk = 0, /**< indicates that a file was saved successfully. */
|
||
|
userCancelledSave, /**< indicates that the user aborted the save operation. */
|
||
|
failedToWriteToFile /**< indicates that it tried to write to a file but this failed. */
|
||
|
};
|
||
|
|
||
|
#if JUCE_MODAL_LOOPS_PERMITTED
|
||
|
/** Tries to save the document to the last file it was saved or loaded from.
|
||
|
|
||
|
This will always try to write to the file, even if the document isn't flagged as
|
||
|
having changed.
|
||
|
|
||
|
@param askUserForFileIfNotSpecified if there's no file currently specified and this is
|
||
|
true, it will prompt the user to pick a file, as if
|
||
|
saveAsInteractive() was called.
|
||
|
@param showMessageOnFailure if true it will show a warning message when if the
|
||
|
save operation fails
|
||
|
@see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive
|
||
|
*/
|
||
|
SaveResult save (bool askUserForFileIfNotSpecified,
|
||
|
bool showMessageOnFailure);
|
||
|
#endif
|
||
|
|
||
|
/** Tries to save the document to the last file it was saved or loaded from.
|
||
|
|
||
|
This will always try to write to the file, even if the document isn't flagged as
|
||
|
having changed.
|
||
|
|
||
|
@param askUserForFileIfNotSpecified if there's no file currently specified and this is
|
||
|
true, it will prompt the user to pick a file, as if
|
||
|
saveAsInteractive() was called.
|
||
|
@param showMessageOnFailure if true it will show a warning message when if the
|
||
|
save operation fails
|
||
|
@param callback called after the save operation with the result
|
||
|
@see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive
|
||
|
*/
|
||
|
void saveAsync (bool askUserForFileIfNotSpecified,
|
||
|
bool showMessageOnFailure,
|
||
|
std::function<void (SaveResult)> callback);
|
||
|
|
||
|
#if JUCE_MODAL_LOOPS_PERMITTED
|
||
|
/** If the file needs saving, it'll ask the user if that's what they want to do, and save
|
||
|
it if they say yes.
|
||
|
|
||
|
If you've got a document open and want to close it (e.g. to quit the app), this is the
|
||
|
method to call.
|
||
|
|
||
|
If the document doesn't need saving it'll return the value savedOk so
|
||
|
you can go ahead and delete the document.
|
||
|
|
||
|
If it does need saving it'll prompt the user, and if they say "discard changes" it'll
|
||
|
return savedOk, so again, you can safely delete the document.
|
||
|
|
||
|
If the user clicks "cancel", it'll return userCancelledSave, so if you can abort the
|
||
|
close-document operation.
|
||
|
|
||
|
And if they click "save changes", it'll try to save and either return savedOk, or
|
||
|
failedToWriteToFile if there was a problem.
|
||
|
|
||
|
@see save, saveAs, saveAsInteractive
|
||
|
*/
|
||
|
SaveResult saveIfNeededAndUserAgrees();
|
||
|
#endif
|
||
|
|
||
|
/** If the file needs saving, it'll ask the user if that's what they want to do, and save
|
||
|
it if they say yes.
|
||
|
|
||
|
If you've got a document open and want to close it (e.g. to quit the app), this is the
|
||
|
method to call.
|
||
|
|
||
|
If the document doesn't need saving the callback will be called with the value savedOk
|
||
|
so you can go ahead and delete the document.
|
||
|
|
||
|
If it does need saving it'll prompt the user, and if they say "discard changes" the
|
||
|
callback will be called with savedOk, so again, you can safely delete the document.
|
||
|
|
||
|
If the user clicks "cancel", the callback will be aclled with userCancelledSave, so
|
||
|
you can abort the close-document operation.
|
||
|
|
||
|
And if they click "save changes", it'll try to save and the callback will be called
|
||
|
with either savedOk, or failedToWriteToFile if there was a problem.
|
||
|
|
||
|
@see saveAsync, saveAsAsync, saveAsInteractiveAsync
|
||
|
*/
|
||
|
void saveIfNeededAndUserAgreesAsync (std::function<void (SaveResult)> callback);
|
||
|
|
||
|
#if JUCE_MODAL_LOOPS_PERMITTED
|
||
|
/** Tries to save the document to a specified file.
|
||
|
|
||
|
If this succeeds, it'll also change the document's internal file (as returned by
|
||
|
the getFile() method). If it fails, the file will be left unchanged.
|
||
|
|
||
|
@param newFile the file to try to write to
|
||
|
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||
|
the user first if they want to overwrite it
|
||
|
@param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll
|
||
|
use the saveAsInteractive() method to ask the user for a
|
||
|
filename
|
||
|
@param showMessageOnFailure if true and the write operation fails, it'll show
|
||
|
a message box to warn the user
|
||
|
@param showWaitCursor if true, the 'wait' mouse cursor will be showin during
|
||
|
saving
|
||
|
@see saveIfNeededAndUserAgrees, save, saveAsInteractive
|
||
|
*/
|
||
|
SaveResult saveAs (const File& newFile,
|
||
|
bool warnAboutOverwritingExistingFiles,
|
||
|
bool askUserForFileIfNotSpecified,
|
||
|
bool showMessageOnFailure,
|
||
|
bool showWaitCursor = true);
|
||
|
#endif
|
||
|
|
||
|
/** Tries to save the document to a specified file.
|
||
|
|
||
|
If this succeeds, it'll also change the document's internal file (as returned by
|
||
|
the getFile() method). If it fails, the file will be left unchanged.
|
||
|
|
||
|
@param newFile the file to try to write to
|
||
|
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask the user
|
||
|
first if they want to overwrite it
|
||
|
@param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll
|
||
|
use the saveAsInteractive() method to ask the user
|
||
|
for a filename
|
||
|
@param showMessageOnFailure if true and the write operation fails, it'll show
|
||
|
a message box to warn the user
|
||
|
@param callback called with the result of the save operation
|
||
|
|
||
|
@see saveIfNeededAndUserAgreesAsync, saveAsync, saveAsInteractiveAsync
|
||
|
*/
|
||
|
void saveAsAsync (const File& newFile,
|
||
|
bool warnAboutOverwritingExistingFiles,
|
||
|
bool askUserForFileIfNotSpecified,
|
||
|
bool showMessageOnFailure,
|
||
|
std::function<void (SaveResult)> callback);
|
||
|
|
||
|
#if JUCE_MODAL_LOOPS_PERMITTED
|
||
|
/** Prompts the user for a filename and tries to save to it.
|
||
|
|
||
|
This will pop up a dialog box using the title, file extension and
|
||
|
wildcard specified in the document's constructor, and asks the user
|
||
|
for a file. If they pick one, the saveAs() method is used to try to save
|
||
|
to this file.
|
||
|
|
||
|
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||
|
the user first if they want to overwrite it
|
||
|
@see saveIfNeededAndUserAgrees, save, saveAs
|
||
|
*/
|
||
|
SaveResult saveAsInteractive (bool warnAboutOverwritingExistingFiles);
|
||
|
#endif
|
||
|
|
||
|
/** Prompts the user for a filename and tries to save to it.
|
||
|
|
||
|
This will pop up a dialog box using the title, file extension and
|
||
|
wildcard specified in the document's constructor, and asks the user
|
||
|
for a file. If they pick one, the saveAs() method is used to try to save
|
||
|
to this file.
|
||
|
|
||
|
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||
|
the user first if they want to overwrite it
|
||
|
@param callback called with the result of the save operation
|
||
|
@see saveIfNeededAndUserAgreesAsync, saveAsync, saveAsAsync
|
||
|
*/
|
||
|
void saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles,
|
||
|
std::function<void (SaveResult)> callback);
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Returns the file that this document was last successfully saved or loaded from.
|
||
|
|
||
|
When the document object is created, this will be set to File().
|
||
|
|
||
|
It is changed when one of the load or save methods is used, or when setFile()
|
||
|
is used to explicitly set it.
|
||
|
*/
|
||
|
const File& getFile() const;
|
||
|
|
||
|
/** Sets the file that this document thinks it was loaded from.
|
||
|
|
||
|
This won't actually load anything - it just changes the file stored internally.
|
||
|
|
||
|
@see getFile
|
||
|
*/
|
||
|
void setFile (const File& newFile);
|
||
|
|
||
|
protected:
|
||
|
//==============================================================================
|
||
|
/** Overload this to return the title of the document.
|
||
|
|
||
|
This is used in message boxes, filenames and file choosers, so it should be
|
||
|
something sensible.
|
||
|
*/
|
||
|
virtual String getDocumentTitle() = 0;
|
||
|
|
||
|
/** This method should try to load your document from the given file.
|
||
|
@returns a Result object to indicate the whether there was an error.
|
||
|
*/
|
||
|
virtual Result loadDocument (const File& file) = 0;
|
||
|
|
||
|
/** This method should try to load your document from the given file, then
|
||
|
call the provided callback on the message thread, passing the result of the load.
|
||
|
|
||
|
By default, this will synchronously call through to loadDocument.
|
||
|
|
||
|
For longer-running load operations, you may wish to override this function to
|
||
|
run the load on a background thread, and then to call the callback later on the
|
||
|
message thread to signal that the load has completed.
|
||
|
*/
|
||
|
virtual void loadDocumentAsync (const File& file, std::function<void (Result)> callback);
|
||
|
|
||
|
/** This method should try to write your document to the given file.
|
||
|
@returns a Result object to indicate the whether there was an error.
|
||
|
*/
|
||
|
virtual Result saveDocument (const File& file) = 0;
|
||
|
|
||
|
/** This method should try to write your document to the given file, then
|
||
|
call the provided callback on the message thread, passing the result of the write.
|
||
|
|
||
|
By default, this will synchronously call through to saveDocument.
|
||
|
|
||
|
For longer-running save operations, you may wish to override this function to
|
||
|
run the save on a background thread, and then to call the callback later on the
|
||
|
message thread to signal that the save has completed.
|
||
|
*/
|
||
|
virtual void saveDocumentAsync (const File& file, std::function<void (Result)> callback);
|
||
|
|
||
|
/** This is used for dialog boxes to make them open at the last folder you
|
||
|
were using.
|
||
|
|
||
|
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||
|
the last document that was used - you might want to store this value
|
||
|
in a static variable, or even in your application's properties. It should
|
||
|
be a global setting rather than a property of this object.
|
||
|
|
||
|
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||
|
object to manage your recent-files list.
|
||
|
|
||
|
As a default value, it's ok to return File(), and the document object will
|
||
|
use a sensible one instead.
|
||
|
|
||
|
@see RecentlyOpenedFilesList
|
||
|
*/
|
||
|
virtual File getLastDocumentOpened() = 0;
|
||
|
|
||
|
/** This is used for dialog boxes to make them open at the last folder you
|
||
|
were using.
|
||
|
|
||
|
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||
|
the last document that was used - you might want to store this value
|
||
|
in a static variable, or even in your application's properties. It should
|
||
|
be a global setting rather than a property of this object.
|
||
|
|
||
|
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||
|
object to manage your recent-files list.
|
||
|
|
||
|
@see RecentlyOpenedFilesList
|
||
|
*/
|
||
|
virtual void setLastDocumentOpened (const File& file) = 0;
|
||
|
|
||
|
/** This is called by saveAsInteractiveAsync() to allow you to optionally customise the
|
||
|
filename that the user is presented with in the save dialog.
|
||
|
The defaultFile parameter is an initial suggestion based on what the class knows
|
||
|
about the current document - you can return a variation on this file with a different
|
||
|
extension, etc, or just return something completely different.
|
||
|
*/
|
||
|
virtual File getSuggestedSaveAsFile (const File& defaultFile);
|
||
|
|
||
|
private:
|
||
|
//==============================================================================
|
||
|
class Pimpl;
|
||
|
std::unique_ptr<Pimpl> pimpl;
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBasedDocument)
|
||
|
};
|
||
|
|
||
|
} // namespace juce
|