git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
107
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp
vendored
Normal file
107
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ApplicationProperties::ApplicationProperties()
|
||||
{
|
||||
}
|
||||
|
||||
ApplicationProperties::~ApplicationProperties()
|
||||
{
|
||||
closeFiles();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationProperties::setStorageParameters (const PropertiesFile::Options& newOptions)
|
||||
{
|
||||
options = newOptions;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationProperties::openFiles()
|
||||
{
|
||||
// You need to call setStorageParameters() before trying to get hold of the properties!
|
||||
jassert (options.applicationName.isNotEmpty());
|
||||
|
||||
if (options.applicationName.isNotEmpty())
|
||||
{
|
||||
PropertiesFile::Options o (options);
|
||||
|
||||
if (userProps == nullptr)
|
||||
{
|
||||
o.commonToAllUsers = false;
|
||||
userProps.reset (new PropertiesFile (o));
|
||||
}
|
||||
|
||||
if (commonProps == nullptr)
|
||||
{
|
||||
o.commonToAllUsers = true;
|
||||
commonProps.reset (new PropertiesFile (o));
|
||||
}
|
||||
|
||||
userProps->setFallbackPropertySet (commonProps.get());
|
||||
}
|
||||
}
|
||||
|
||||
PropertiesFile* ApplicationProperties::getUserSettings()
|
||||
{
|
||||
if (userProps == nullptr)
|
||||
openFiles();
|
||||
|
||||
return userProps.get();
|
||||
}
|
||||
|
||||
PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly)
|
||||
{
|
||||
if (commonProps == nullptr)
|
||||
openFiles();
|
||||
|
||||
if (returnUserPropsIfReadOnly)
|
||||
{
|
||||
if (commonSettingsAreReadOnly == 0)
|
||||
commonSettingsAreReadOnly = commonProps->save() ? -1 : 1;
|
||||
|
||||
if (commonSettingsAreReadOnly > 0)
|
||||
return userProps.get();
|
||||
}
|
||||
|
||||
return commonProps.get();
|
||||
}
|
||||
|
||||
bool ApplicationProperties::saveIfNeeded()
|
||||
{
|
||||
return (userProps == nullptr || userProps->saveIfNeeded())
|
||||
&& (commonProps == nullptr || commonProps->saveIfNeeded());
|
||||
}
|
||||
|
||||
void ApplicationProperties::closeFiles()
|
||||
{
|
||||
userProps.reset();
|
||||
commonProps.reset();
|
||||
}
|
||||
|
||||
} // namespace juce
|
133
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h
vendored
Normal file
133
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages a collection of properties.
|
||||
|
||||
This is a slightly higher-level wrapper for managing PropertiesFile objects.
|
||||
|
||||
It holds two different PropertiesFile objects internally, one for user-specific
|
||||
settings (stored in your user directory), and one for settings that are common to
|
||||
all users (stored in a folder accessible to all users).
|
||||
|
||||
The class manages the creation of these files on-demand, allowing access via the
|
||||
getUserSettings() and getCommonSettings() methods.
|
||||
|
||||
After creating an instance of an ApplicationProperties object, you should first
|
||||
of all call setStorageParameters() to tell it the parameters to use to create
|
||||
its files.
|
||||
|
||||
@see PropertiesFile
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API ApplicationProperties
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates an ApplicationProperties object.
|
||||
|
||||
Before using it, you must call setStorageParameters() to give it the info
|
||||
it needs to create the property files.
|
||||
*/
|
||||
ApplicationProperties();
|
||||
|
||||
/** Destructor. */
|
||||
~ApplicationProperties();
|
||||
|
||||
//==============================================================================
|
||||
/** Gives the object the information it needs to create the appropriate properties files.
|
||||
See the PropertiesFile::Options class for details about what options you need to set.
|
||||
*/
|
||||
void setStorageParameters (const PropertiesFile::Options& options);
|
||||
|
||||
/** Returns the current storage parameters.
|
||||
@see setStorageParameters
|
||||
*/
|
||||
const PropertiesFile::Options& getStorageParameters() const noexcept { return options; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the user settings file.
|
||||
|
||||
The first time this is called, it will create and load the properties file.
|
||||
|
||||
Note that when you search the user PropertiesFile for a value that it doesn't contain,
|
||||
the common settings are used as a second-chance place to look. This is done via the
|
||||
PropertySet::setFallbackPropertySet() method - by default the common settings are set
|
||||
to the fallback for the user settings.
|
||||
|
||||
@see getCommonSettings
|
||||
*/
|
||||
PropertiesFile* getUserSettings();
|
||||
|
||||
/** Returns the common settings file.
|
||||
|
||||
The first time this is called, it will create and load the properties file.
|
||||
|
||||
@param returnUserPropsIfReadOnly if this is true, and the common properties file is
|
||||
read-only (e.g. because the user doesn't have permission to write
|
||||
to shared files), then this will return the user settings instead,
|
||||
(like getUserSettings() would do). This is handy if you'd like to
|
||||
write a value to the common settings, but if that's no possible,
|
||||
then you'd rather write to the user settings than none at all.
|
||||
If returnUserPropsIfReadOnly is false, this method will always return
|
||||
the common settings, even if any changes to them can't be saved.
|
||||
@see getUserSettings
|
||||
*/
|
||||
PropertiesFile* getCommonSettings (bool returnUserPropsIfReadOnly);
|
||||
|
||||
//==============================================================================
|
||||
/** Saves both files if they need to be saved.
|
||||
|
||||
@see PropertiesFile::saveIfNeeded
|
||||
*/
|
||||
bool saveIfNeeded();
|
||||
|
||||
/** Flushes and closes both files if they are open.
|
||||
|
||||
This flushes any pending changes to disk with PropertiesFile::saveIfNeeded()
|
||||
and closes both files. They will then be re-opened the next time getUserSettings()
|
||||
or getCommonSettings() is called.
|
||||
*/
|
||||
void closeFiles();
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
PropertiesFile::Options options;
|
||||
std::unique_ptr<PropertiesFile> userProps, commonProps;
|
||||
int commonSettingsAreReadOnly = 0;
|
||||
|
||||
void openFiles();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationProperties)
|
||||
};
|
||||
|
||||
} // namespace juce
|
358
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp
vendored
Normal file
358
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp
vendored
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
namespace PropertyFileConstants
|
||||
{
|
||||
constexpr static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P');
|
||||
constexpr static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P');
|
||||
|
||||
constexpr static const char* const fileTag = "PROPERTIES";
|
||||
constexpr static const char* const valueTag = "VALUE";
|
||||
constexpr static const char* const nameAttribute = "name";
|
||||
constexpr static const char* const valueAttribute = "val";
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile::Options::Options()
|
||||
: commonToAllUsers (false),
|
||||
ignoreCaseOfKeyNames (false),
|
||||
doNotSave (false),
|
||||
millisecondsBeforeSaving (3000),
|
||||
storageFormat (PropertiesFile::storeAsXML),
|
||||
processLock (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
File PropertiesFile::Options::getDefaultFile() const
|
||||
{
|
||||
// mustn't have illegal characters in this name..
|
||||
jassert (applicationName == File::createLegalFileName (applicationName));
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
File dir (commonToAllUsers ? "/Library/"
|
||||
: "~/Library/");
|
||||
|
||||
if (osxLibrarySubFolder != "Preferences"
|
||||
&& ! osxLibrarySubFolder.startsWith ("Application Support")
|
||||
&& ! osxLibrarySubFolder.startsWith ("Containers"))
|
||||
{
|
||||
/* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
|
||||
have changed their advice, and now stipulate that settings should go in "Library/Application Support",
|
||||
or Library/Containers/[app_bundle_id] for a sandboxed app.
|
||||
|
||||
Because older apps would be broken by a silent change in this class's behaviour, you must now
|
||||
explicitly set the osxLibrarySubFolder value to indicate which path you want to use.
|
||||
|
||||
In newer apps, you should always set this to "Application Support"
|
||||
or "Application Support/YourSubFolderName".
|
||||
|
||||
If your app needs to load settings files that were created by older versions of juce and
|
||||
you want to maintain backwards-compatibility, then you can set this to "Preferences".
|
||||
But.. for better Apple-compliance, the recommended approach would be to write some code that
|
||||
finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
|
||||
and then uses the new path.
|
||||
*/
|
||||
jassertfalse;
|
||||
|
||||
dir = dir.getChildFile ("Application Support");
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = dir.getChildFile (osxLibrarySubFolder);
|
||||
}
|
||||
|
||||
if (folderName.isNotEmpty())
|
||||
dir = dir.getChildFile (folderName);
|
||||
|
||||
#elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
|
||||
auto dir = File (commonToAllUsers ? "/var" : "~")
|
||||
.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: ("." + applicationName));
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
auto dir = File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
|
||||
: File::userApplicationDataDirectory);
|
||||
|
||||
if (dir == File())
|
||||
return {};
|
||||
|
||||
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: applicationName);
|
||||
#endif
|
||||
|
||||
return (filenameSuffix.startsWithChar (L'.')
|
||||
? dir.getChildFile (applicationName).withFileExtension (filenameSuffix)
|
||||
: dir.getChildFile (applicationName + "." + filenameSuffix));
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile::PropertiesFile (const File& f, const Options& o)
|
||||
: PropertySet (o.ignoreCaseOfKeyNames),
|
||||
file (f), options (o)
|
||||
{
|
||||
reload();
|
||||
}
|
||||
|
||||
PropertiesFile::PropertiesFile (const Options& o)
|
||||
: PropertySet (o.ignoreCaseOfKeyNames),
|
||||
file (o.getDefaultFile()), options (o)
|
||||
{
|
||||
reload();
|
||||
}
|
||||
|
||||
bool PropertiesFile::reload()
|
||||
{
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return false; // locking failure..
|
||||
|
||||
loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml();
|
||||
return loadedOk;
|
||||
}
|
||||
|
||||
PropertiesFile::~PropertiesFile()
|
||||
{
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
|
||||
{
|
||||
return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveIfNeeded()
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
return (! needsWriting) || save();
|
||||
}
|
||||
|
||||
bool PropertiesFile::needsToBeSaved() const
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
return needsWriting;
|
||||
}
|
||||
|
||||
void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_)
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
needsWriting = needsToBeSaved_;
|
||||
}
|
||||
|
||||
bool PropertiesFile::save()
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
|
||||
stopTimer();
|
||||
|
||||
if (options.doNotSave
|
||||
|| file == File()
|
||||
|| file.isDirectory()
|
||||
|| ! file.getParentDirectory().createDirectory())
|
||||
return false;
|
||||
|
||||
if (options.storageFormat == storeAsXML)
|
||||
return saveAsXml();
|
||||
|
||||
return saveAsBinary();
|
||||
}
|
||||
|
||||
bool PropertiesFile::loadAsXml()
|
||||
{
|
||||
if (auto doc = parseXMLIfTagMatches (file, PropertyFileConstants::fileTag))
|
||||
{
|
||||
for (auto* e : doc->getChildWithTagNameIterator (PropertyFileConstants::valueTag))
|
||||
{
|
||||
auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute);
|
||||
|
||||
if (name.isNotEmpty())
|
||||
getAllProperties().set (name,
|
||||
e->getFirstChildElement() != nullptr
|
||||
? e->getFirstChildElement()->toString (XmlElement::TextFormat().singleLine().withoutHeader())
|
||||
: e->getStringAttribute (PropertyFileConstants::valueAttribute));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveAsXml()
|
||||
{
|
||||
XmlElement doc (PropertyFileConstants::fileTag);
|
||||
auto& props = getAllProperties();
|
||||
|
||||
for (int i = 0; i < props.size(); ++i)
|
||||
{
|
||||
auto* e = doc.createNewChildElement (PropertyFileConstants::valueTag);
|
||||
e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);
|
||||
|
||||
// if the value seems to contain xml, store it as such..
|
||||
if (auto childElement = parseXML (props.getAllValues() [i]))
|
||||
e->addChildElement (childElement.release());
|
||||
else
|
||||
e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
|
||||
}
|
||||
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return false; // locking failure..
|
||||
|
||||
if (doc.writeTo (file, {}))
|
||||
{
|
||||
needsWriting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PropertiesFile::loadAsBinary()
|
||||
{
|
||||
FileInputStream fileStream (file);
|
||||
|
||||
if (fileStream.openedOk())
|
||||
{
|
||||
auto magicNumber = fileStream.readInt();
|
||||
|
||||
if (magicNumber == PropertyFileConstants::magicNumberCompressed)
|
||||
{
|
||||
SubregionStream subStream (&fileStream, 4, -1, false);
|
||||
GZIPDecompressorInputStream gzip (subStream);
|
||||
return loadAsBinary (gzip);
|
||||
}
|
||||
|
||||
if (magicNumber == PropertyFileConstants::magicNumber)
|
||||
return loadAsBinary (fileStream);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PropertiesFile::loadAsBinary (InputStream& input)
|
||||
{
|
||||
BufferedInputStream in (input, 2048);
|
||||
|
||||
int numValues = in.readInt();
|
||||
|
||||
while (--numValues >= 0 && ! in.isExhausted())
|
||||
{
|
||||
auto key = in.readString();
|
||||
auto value = in.readString();
|
||||
jassert (key.isNotEmpty());
|
||||
|
||||
if (key.isNotEmpty())
|
||||
getAllProperties().set (key, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveAsBinary()
|
||||
{
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return false; // locking failure..
|
||||
|
||||
TemporaryFile tempFile (file);
|
||||
|
||||
{
|
||||
FileOutputStream out (tempFile.getFile());
|
||||
|
||||
if (! out.openedOk())
|
||||
return false;
|
||||
|
||||
if (options.storageFormat == storeAsCompressedBinary)
|
||||
{
|
||||
out.writeInt (PropertyFileConstants::magicNumberCompressed);
|
||||
out.flush();
|
||||
|
||||
GZIPCompressorOutputStream zipped (out, 9);
|
||||
|
||||
if (! writeToStream (zipped))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// have you set up the storage option flags correctly?
|
||||
jassert (options.storageFormat == storeAsBinary);
|
||||
|
||||
out.writeInt (PropertyFileConstants::magicNumber);
|
||||
|
||||
if (! writeToStream (out))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! tempFile.overwriteTargetFileWithTemporary())
|
||||
return false;
|
||||
|
||||
needsWriting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertiesFile::writeToStream (OutputStream& out)
|
||||
{
|
||||
auto& props = getAllProperties();
|
||||
auto& keys = props.getAllKeys();
|
||||
auto& values = props.getAllValues();
|
||||
auto numProperties = props.size();
|
||||
|
||||
if (! out.writeInt (numProperties))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < numProperties; ++i)
|
||||
{
|
||||
if (! out.writeString (keys[i])) return false;
|
||||
if (! out.writeString (values[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PropertiesFile::timerCallback()
|
||||
{
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
void PropertiesFile::propertyChanged()
|
||||
{
|
||||
sendChangeMessage();
|
||||
needsWriting = true;
|
||||
|
||||
if (options.millisecondsBeforeSaving > 0)
|
||||
startTimer (options.millisecondsBeforeSaving);
|
||||
else if (options.millisecondsBeforeSaving == 0)
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
} // namespace juce
|
255
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.h
vendored
Normal file
255
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.h
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** Wrapper on a file that stores a list of key/value data pairs.
|
||||
|
||||
Useful for storing application settings, etc. See the PropertySet class for
|
||||
the interfaces that read and write values.
|
||||
|
||||
Not designed for very large amounts of data, as it keeps all the values in
|
||||
memory and writes them out to disk lazily when they are changed.
|
||||
|
||||
Because this class derives from ChangeBroadcaster, ChangeListeners can be registered
|
||||
with it, and these will be signalled when a value changes.
|
||||
|
||||
@see PropertySet
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API PropertiesFile : public PropertySet,
|
||||
public ChangeBroadcaster,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum StorageFormat
|
||||
{
|
||||
storeAsBinary,
|
||||
storeAsCompressedBinary,
|
||||
storeAsXML
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Structure describing properties file options */
|
||||
struct JUCE_API Options
|
||||
{
|
||||
/** Creates an empty Options structure.
|
||||
You'll need to fill-in the data members appropriately before using this structure.
|
||||
*/
|
||||
Options();
|
||||
|
||||
/** The name of your application - this is used to help generate the path and filename
|
||||
at which the properties file will be stored. */
|
||||
String applicationName;
|
||||
|
||||
/** The suffix to use for your properties file.
|
||||
It doesn't really matter what this is - you may want to use ".settings" or
|
||||
".properties" or something. If the suffix includes the prefixing dot (for example
|
||||
".settings") then the suffix of applicationName will be replaced with your suffix
|
||||
("MyApp.exe" -> "MyApp.settings"). If your filenameSuffix does NOT include the dot,
|
||||
then the suffix will be appended to the applicationName ("MyApp.exe" ->
|
||||
"MyApp.exe.settings").
|
||||
*/
|
||||
String filenameSuffix;
|
||||
|
||||
/** The name of a subfolder in which you'd like your properties file to live.
|
||||
See the getDefaultFile() method for more details about how this is used.
|
||||
*/
|
||||
String folderName;
|
||||
|
||||
/** If you're using properties files on a Mac, you must set this value - failure to
|
||||
do so will cause a runtime assertion.
|
||||
|
||||
The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
|
||||
have changed their advice, and now stipulate that settings should go in "Library/Application Support".
|
||||
|
||||
Because older apps would be broken by a silent change in this class's behaviour, you must now
|
||||
explicitly set the osxLibrarySubFolder value to indicate which path you want to use.
|
||||
|
||||
In newer apps, you should always set this to "Application Support" or
|
||||
"Application Support/YourSubFolderName".
|
||||
|
||||
If your app needs to load settings files that were created by older versions of juce and
|
||||
you want to maintain backwards-compatibility, then you can set this to "Preferences".
|
||||
But.. for better Apple-compliance, the recommended approach would be to write some code that
|
||||
finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
|
||||
and then uses the new path.
|
||||
*/
|
||||
String osxLibrarySubFolder;
|
||||
|
||||
/** If true, the file will be created in a location that's shared between users.
|
||||
The default constructor initialises this value to false.
|
||||
*/
|
||||
bool commonToAllUsers;
|
||||
|
||||
/** If true, this means that property names are matched in a case-insensitive manner.
|
||||
See the PropertySet constructor for more info.
|
||||
The default constructor initialises this value to false.
|
||||
*/
|
||||
bool ignoreCaseOfKeyNames;
|
||||
|
||||
/** If set to true, this prevents the file from being written to disk. */
|
||||
bool doNotSave;
|
||||
|
||||
/** If this is zero or greater, then after a value is changed, the object will wait
|
||||
for this amount of time and then save the file. If this zero, the file will be
|
||||
written to disk immediately on being changed (which might be slow, as it'll re-write
|
||||
synchronously each time a value-change method is called). If it is less than zero,
|
||||
the file won't be saved until save() or saveIfNeeded() are explicitly called.
|
||||
The default constructor sets this to a reasonable value of a few seconds, so you
|
||||
only need to change it if you need a special case.
|
||||
*/
|
||||
int millisecondsBeforeSaving;
|
||||
|
||||
/** Specifies whether the file should be written as XML, binary, etc.
|
||||
The default constructor sets this to storeAsXML, so you only need to set it explicitly
|
||||
if you want to use a different format.
|
||||
*/
|
||||
StorageFormat storageFormat;
|
||||
|
||||
/** An optional InterprocessLock object that will be used to prevent multiple threads or
|
||||
processes from writing to the file at the same time. The PropertiesFile will keep a
|
||||
pointer to this object but will not take ownership of it - the caller is responsible for
|
||||
making sure that the lock doesn't get deleted before the PropertiesFile has been deleted.
|
||||
The default constructor initialises this value to nullptr, so you don't need to touch it
|
||||
unless you want to use a lock.
|
||||
*/
|
||||
InterProcessLock* processLock;
|
||||
|
||||
/** This can be called to suggest a file that should be used, based on the values
|
||||
in this structure.
|
||||
|
||||
So on a Mac, this will return a file called:
|
||||
~/Library/[osxLibrarySubFolder]/[folderName]/[applicationName].[filenameSuffix]
|
||||
|
||||
On Windows it'll return something like:
|
||||
C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[filenameSuffix]
|
||||
|
||||
On Linux it'll return
|
||||
~/[folderName]/[applicationName].[filenameSuffix]
|
||||
|
||||
If the folderName variable is empty, it'll use the app name for this (or omit the
|
||||
folder name on the Mac).
|
||||
|
||||
The paths will also vary depending on whether commonToAllUsers is true.
|
||||
*/
|
||||
File getDefaultFile() const;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a PropertiesFile object.
|
||||
The file used will be chosen by calling PropertiesFile::Options::getDefaultFile()
|
||||
for the options provided. To set the file explicitly, use the other constructor.
|
||||
*/
|
||||
explicit PropertiesFile (const Options& options);
|
||||
|
||||
/** Creates a PropertiesFile object.
|
||||
Unlike the other constructor, this one allows you to explicitly set the file that you
|
||||
want to be used, rather than using the default one.
|
||||
*/
|
||||
PropertiesFile (const File& file,
|
||||
const Options& options);
|
||||
|
||||
/** Destructor.
|
||||
When deleted, the file will first call saveIfNeeded() to flush any changes to disk.
|
||||
*/
|
||||
~PropertiesFile() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this file was created from a valid (or non-existent) file.
|
||||
If the file failed to load correctly because it was corrupt or had insufficient
|
||||
access, this will be false.
|
||||
*/
|
||||
bool isValidFile() const noexcept { return loadedOk; }
|
||||
|
||||
//==============================================================================
|
||||
/** This will flush all the values to disk if they've changed since the last
|
||||
time they were saved.
|
||||
|
||||
Returns false if it fails to write to the file for some reason (maybe because
|
||||
it's read-only or the directory doesn't exist or something).
|
||||
|
||||
@see save
|
||||
*/
|
||||
bool saveIfNeeded();
|
||||
|
||||
/** This will force a write-to-disk of the current values, regardless of whether
|
||||
anything has changed since the last save.
|
||||
|
||||
Returns false if it fails to write to the file for some reason (maybe because
|
||||
it's read-only or the directory doesn't exist or something).
|
||||
|
||||
@see saveIfNeeded
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/** Returns true if the properties have been altered since the last time they were saved.
|
||||
The file is flagged as needing to be saved when you change a value, but you can
|
||||
explicitly set this flag with setNeedsToBeSaved().
|
||||
*/
|
||||
bool needsToBeSaved() const;
|
||||
|
||||
/** Explicitly sets the flag to indicate whether the file needs saving or not.
|
||||
@see needsToBeSaved
|
||||
*/
|
||||
void setNeedsToBeSaved (bool needsToBeSaved);
|
||||
|
||||
/** Attempts to reload the settings from the file. */
|
||||
bool reload();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the file that's being used. */
|
||||
const File& getFile() const noexcept { return file; }
|
||||
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void propertyChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
File file;
|
||||
Options options;
|
||||
bool loadedOk = false, needsWriting = false;
|
||||
|
||||
using ProcessScopedLock = const std::unique_ptr<InterProcessLock::ScopedLockType>;
|
||||
InterProcessLock::ScopedLockType* createProcessLock() const;
|
||||
|
||||
void timerCallback() override;
|
||||
bool saveAsXml();
|
||||
bool saveAsBinary();
|
||||
bool loadAsXml();
|
||||
bool loadAsBinary();
|
||||
bool loadAsBinary (InputStream&);
|
||||
bool writeToStream (OutputStream&);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile)
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user