mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
Refactored the PropertiesFile construction, introducing a PropertiesFile::Options structure to hold all the settings that previously were just passed as parameters. Also added a mandatory option for setting the OSX preferences path to be used - see the PropertiesFile::Options::osxLibrarySubFolder value for details.
Also on OSX, changed the location used for interprocess lock files to /var/tmp - this may affect applications which try to share locks with older builds of the same app.
This commit is contained in:
parent
b94782d388
commit
74469aaa83
15 changed files with 568 additions and 433 deletions
|
|
@ -68,8 +68,32 @@ void StoredSettings::flush()
|
|||
}
|
||||
|
||||
props = nullptr;
|
||||
props = PropertiesFile::createDefaultAppPropertiesFile ("Jucer2", "settings", String::empty,
|
||||
false, 3000, PropertiesFile::storeAsXML);
|
||||
|
||||
{
|
||||
// These settings are used in defining the properties file's location.
|
||||
PropertiesFile::Options options;
|
||||
options.applicationName = "Introjucer";
|
||||
options.folderName = "Introjucer";
|
||||
options.filenameSuffix = "settings";
|
||||
options.osxLibrarySubFolder = "Application Support";
|
||||
|
||||
props = new PropertiesFile (options);
|
||||
|
||||
// Because older versions of the introjucer saved their settings under a different
|
||||
// name, this code is an example of how to migrate your old settings files...
|
||||
if (! props->getFile().exists())
|
||||
{
|
||||
PropertiesFile::Options oldOptions;
|
||||
oldOptions.applicationName = "Jucer2";
|
||||
oldOptions.filenameSuffix = "settings";
|
||||
oldOptions.osxLibrarySubFolder = "Preferences";
|
||||
|
||||
PropertiesFile oldProps (oldOptions);
|
||||
|
||||
if (oldProps.getFile().exists())
|
||||
props->addAllPropertiesFrom (oldProps);
|
||||
}
|
||||
}
|
||||
|
||||
// recent files...
|
||||
recentFiles.restoreFromString (props->getValue ("recentFiles"));
|
||||
|
|
|
|||
|
|
@ -51,10 +51,13 @@ public:
|
|||
void initialise (const String& commandLine)
|
||||
{
|
||||
// initialise our settings file..
|
||||
ApplicationProperties::getInstance()
|
||||
->setStorageParameters ("Juce Audio Plugin Host",
|
||||
"settings", String::empty, 1000,
|
||||
PropertiesFile::storeAsXML);
|
||||
|
||||
PropertiesFile::Options options;
|
||||
options.applicationName = "Juce Audio Plugin Host";
|
||||
options.filenameSuffix = "settings";
|
||||
options.osxLibrarySubFolder = "Preferences";
|
||||
|
||||
ApplicationProperties::getInstance()->setStorageParameters (options);
|
||||
|
||||
commandManager = new ApplicationCommandManager();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
//==============================================================================
|
||||
StoredSettings::StoredSettings()
|
||||
: props (0)
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
|
@ -37,7 +36,7 @@ StoredSettings::StoredSettings()
|
|||
StoredSettings::~StoredSettings()
|
||||
{
|
||||
flush();
|
||||
deleteAndZero (props);
|
||||
props = nullptr;
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
|
|
@ -52,31 +51,30 @@ PropertiesFile& StoredSettings::getProps()
|
|||
|
||||
void StoredSettings::flush()
|
||||
{
|
||||
if (props != 0)
|
||||
if (props != nullptr)
|
||||
{
|
||||
props->setValue ("recentFiles", recentFiles.toString());
|
||||
|
||||
props->removeValue ("keyMappings");
|
||||
|
||||
XmlElement* keys = commandManager->getKeyMappings()->createXml (true);
|
||||
ScopedPointer<XmlElement> keys (commandManager->getKeyMappings()->createXml (true));
|
||||
|
||||
if (keys != 0)
|
||||
{
|
||||
if (keys != nullptr)
|
||||
props->setValue ("keyMappings", keys);
|
||||
delete keys;
|
||||
}
|
||||
|
||||
for (int i = 0; i < swatchColours.size(); ++i)
|
||||
props->setValue ("swatchColour" + String (i), colourToHex (swatchColours [i]));
|
||||
}
|
||||
|
||||
deleteAndZero (props);
|
||||
props = nullptr;
|
||||
|
||||
props = PropertiesFile::createDefaultAppPropertiesFile ("Jucer",
|
||||
"settings",
|
||||
String::empty,
|
||||
false, 3000,
|
||||
PropertiesFile::storeAsXML);
|
||||
{
|
||||
PropertiesFile::Options options;
|
||||
options.applicationName = "Jucer";
|
||||
options.filenameSuffix = "settings";
|
||||
options.osxLibrarySubFolder = "Preferences";
|
||||
|
||||
props = new PropertiesFile (options);
|
||||
}
|
||||
|
||||
// recent files...
|
||||
recentFiles.restoreFromString (props->getValue ("recentFiles"));
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public:
|
|||
Array <Colour> swatchColours;
|
||||
|
||||
private:
|
||||
PropertiesFile* props;
|
||||
ScopedPointer<PropertiesFile> props;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4107,6 +4107,15 @@ bool PropertySet::containsKey (const String& keyName) const noexcept
|
|||
return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys);
|
||||
}
|
||||
|
||||
void PropertySet::addAllPropertiesFrom (const PropertySet& source)
|
||||
{
|
||||
const ScopedLock sl (source.getLock());
|
||||
|
||||
for (int i = 0; i < source.properties.size(); ++i)
|
||||
setValue (source.properties.getAllKeys() [i],
|
||||
source.properties.getAllValues() [i]);
|
||||
}
|
||||
|
||||
void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
|
@ -19899,10 +19908,7 @@ BEGIN_JUCE_NAMESPACE
|
|||
juce_ImplementSingleton (ApplicationProperties)
|
||||
|
||||
ApplicationProperties::ApplicationProperties()
|
||||
: msBeforeSaving (3000),
|
||||
options (PropertiesFile::storeAsBinary),
|
||||
commonSettingsAreReadOnly (0),
|
||||
processLock (nullptr)
|
||||
: commonSettingsAreReadOnly (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -19912,19 +19918,9 @@ ApplicationProperties::~ApplicationProperties()
|
|||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
void ApplicationProperties::setStorageParameters (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName_,
|
||||
const int millisecondsBeforeSaving,
|
||||
const int propertiesFileOptions,
|
||||
InterProcessLock* processLock_)
|
||||
void ApplicationProperties::setStorageParameters (const PropertiesFile::Options& newOptions)
|
||||
{
|
||||
appName = applicationName;
|
||||
fileSuffix = fileNameSuffix;
|
||||
folderName = folderName_;
|
||||
msBeforeSaving = millisecondsBeforeSaving;
|
||||
options = propertiesFileOptions;
|
||||
processLock = processLock_;
|
||||
options = newOptions;
|
||||
}
|
||||
|
||||
bool ApplicationProperties::testWriteAccess (const bool testUserSettings,
|
||||
|
|
@ -19947,9 +19943,9 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings,
|
|||
filenames << '\n' << commonProps->getFile().getFullPathName();
|
||||
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
appName + TRANS(" - Unable to save settings"),
|
||||
options.applicationName + TRANS(" - Unable to save settings"),
|
||||
TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ")
|
||||
+ appName + TRANS(" needs to be able to write to the following files:\n")
|
||||
+ options.applicationName + TRANS(" needs to be able to write to the following files:\n")
|
||||
+ filenames
|
||||
+ TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full."));
|
||||
}
|
||||
|
|
@ -19962,19 +19958,24 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings,
|
|||
|
||||
void ApplicationProperties::openFiles()
|
||||
{
|
||||
// You need to call setStorageParameters() before trying to get hold of the
|
||||
// properties!
|
||||
jassert (appName.isNotEmpty());
|
||||
// You need to call setStorageParameters() before trying to get hold of the properties!
|
||||
jassert (options.applicationName.isNotEmpty());
|
||||
|
||||
if (appName.isNotEmpty())
|
||||
if (options.applicationName.isNotEmpty())
|
||||
{
|
||||
PropertiesFile::Options o (options);
|
||||
|
||||
if (userProps == nullptr)
|
||||
userProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName,
|
||||
false, msBeforeSaving, options, processLock);
|
||||
{
|
||||
o.commonToAllUsers = false;
|
||||
userProps = new PropertiesFile (o);
|
||||
}
|
||||
|
||||
if (commonProps == nullptr)
|
||||
commonProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName,
|
||||
true, msBeforeSaving, options, processLock);
|
||||
{
|
||||
o.commonToAllUsers = true;
|
||||
commonProps = new PropertiesFile (o);
|
||||
}
|
||||
|
||||
userProps->setFallbackPropertySet (commonProps);
|
||||
}
|
||||
|
|
@ -20036,27 +20037,97 @@ namespace PropertyFileConstants
|
|||
static const char* const valueAttribute = "val";
|
||||
}
|
||||
|
||||
PropertiesFile::PropertiesFile (const File& f, const int millisecondsBeforeSaving,
|
||||
const int options_, InterProcessLock* const processLock_)
|
||||
: PropertySet (ignoreCaseOfKeyNames),
|
||||
file (f),
|
||||
timerInterval (millisecondsBeforeSaving),
|
||||
options (options_),
|
||||
loadedOk (false),
|
||||
needsWriting (false),
|
||||
processLock (processLock_)
|
||||
PropertiesFile::Options::Options()
|
||||
: commonToAllUsers (false),
|
||||
ignoreCaseOfKeyNames (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 != "Application Support")
|
||||
{
|
||||
/* 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".
|
||||
|
||||
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_ANDROID
|
||||
const File dir ((commonToAllUsers ? "/var/" : "~/")
|
||||
+ (folderName.isNotEmpty() ? folderName
|
||||
: ("." + applicationName)));
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
|
||||
: File::userApplicationDataDirectory));
|
||||
|
||||
if (dir == File::nonexistent)
|
||||
return File::nonexistent;
|
||||
|
||||
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: applicationName);
|
||||
#endif
|
||||
|
||||
return dir.getChildFile (applicationName)
|
||||
.withFileExtension (filenameSuffix);
|
||||
}
|
||||
|
||||
PropertiesFile::PropertiesFile (const File& file_, const Options& options_)
|
||||
: PropertySet (options.ignoreCaseOfKeyNames),
|
||||
file (file_), options (options_),
|
||||
loadedOk (false), needsWriting (false)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
|
||||
PropertiesFile::PropertiesFile (const Options& options_)
|
||||
: PropertySet (options.ignoreCaseOfKeyNames),
|
||||
file (options_.getDefaultFile()), options (options_),
|
||||
loadedOk (false), needsWriting (false)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
|
||||
void PropertiesFile::initialise()
|
||||
{
|
||||
// You need to correctly specify just one storage format for the file
|
||||
jassert ((options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsBinary
|
||||
|| (options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsCompressedBinary
|
||||
|| (options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsXML);
|
||||
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return; // locking failure..
|
||||
|
||||
ScopedPointer<InputStream> fileStream (f.createInputStream());
|
||||
ScopedPointer<InputStream> fileStream (file.createInputStream());
|
||||
|
||||
if (fileStream != nullptr)
|
||||
{
|
||||
|
|
@ -20090,7 +20161,7 @@ PropertiesFile::PropertiesFile (const File& f, const int millisecondsBeforeSavin
|
|||
// Not a binary props file - let's see if it's XML..
|
||||
fileStream = nullptr;
|
||||
|
||||
XmlDocument parser (f);
|
||||
XmlDocument parser (file);
|
||||
ScopedPointer<XmlElement> doc (parser.getDocumentElement (true));
|
||||
|
||||
if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag))
|
||||
|
|
@ -20126,7 +20197,7 @@ PropertiesFile::PropertiesFile (const File& f, const int millisecondsBeforeSavin
|
|||
}
|
||||
else
|
||||
{
|
||||
loadedOk = ! f.exists();
|
||||
loadedOk = ! file.exists();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -20138,7 +20209,7 @@ PropertiesFile::~PropertiesFile()
|
|||
|
||||
InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
|
||||
{
|
||||
return processLock != nullptr ? new InterProcessLock::ScopedLockType (*processLock) : nullptr;
|
||||
return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveIfNeeded()
|
||||
|
|
@ -20170,7 +20241,7 @@ bool PropertiesFile::save()
|
|||
|| ! file.getParentDirectory().createDirectory())
|
||||
return false;
|
||||
|
||||
if ((options & storeAsXML) != 0)
|
||||
if (options.storageFormat == storeAsXML)
|
||||
{
|
||||
XmlElement doc (PropertyFileConstants::fileTag);
|
||||
|
||||
|
|
@ -20212,7 +20283,7 @@ bool PropertiesFile::save()
|
|||
|
||||
if (out != nullptr)
|
||||
{
|
||||
if ((options & storeAsCompressedBinary) != 0)
|
||||
if (options.storageFormat == storeAsCompressedBinary)
|
||||
{
|
||||
out->writeInt (PropertyFileConstants::magicNumberCompressed);
|
||||
out->flush();
|
||||
|
|
@ -20222,7 +20293,7 @@ bool PropertiesFile::save()
|
|||
else
|
||||
{
|
||||
// have you set up the storage option flags correctly?
|
||||
jassert ((options & storeAsBinary) != 0);
|
||||
jassert (options.storageFormat == storeAsBinary);
|
||||
|
||||
out->writeInt (PropertyFileConstants::magicNumber);
|
||||
}
|
||||
|
|
@ -20261,66 +20332,12 @@ void PropertiesFile::propertyChanged()
|
|||
|
||||
needsWriting = true;
|
||||
|
||||
if (timerInterval > 0)
|
||||
startTimer (timerInterval);
|
||||
else if (timerInterval == 0)
|
||||
if (options.millisecondsBeforeSaving > 0)
|
||||
startTimer (options.millisecondsBeforeSaving);
|
||||
else if (options.millisecondsBeforeSaving == 0)
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
File PropertiesFile::getDefaultAppSettingsFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
const bool commonToAllUsers)
|
||||
{
|
||||
// mustn't have illegal characters in this name..
|
||||
jassert (applicationName == File::createLegalFileName (applicationName));
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
File dir (commonToAllUsers ? "/Library/Preferences"
|
||||
: "~/Library/Preferences");
|
||||
|
||||
if (folderName.isNotEmpty())
|
||||
dir = dir.getChildFile (folderName);
|
||||
#elif JUCE_LINUX || JUCE_ANDROID
|
||||
const File dir ((commonToAllUsers ? "/var/" : "~/")
|
||||
+ (folderName.isNotEmpty() ? folderName
|
||||
: ("." + applicationName)));
|
||||
#elif JUCE_WINDOWS
|
||||
File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
|
||||
: File::userApplicationDataDirectory));
|
||||
|
||||
if (dir == File::nonexistent)
|
||||
return File::nonexistent;
|
||||
|
||||
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: applicationName);
|
||||
#endif
|
||||
|
||||
return dir.getChildFile (applicationName)
|
||||
.withFileExtension (fileNameSuffix);
|
||||
}
|
||||
|
||||
PropertiesFile* PropertiesFile::createDefaultAppPropertiesFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
const bool commonToAllUsers,
|
||||
const int millisecondsBeforeSaving,
|
||||
const int propertiesFileOptions,
|
||||
InterProcessLock* processLock_)
|
||||
{
|
||||
const File file (getDefaultAppSettingsFile (applicationName,
|
||||
fileNameSuffix,
|
||||
folderName,
|
||||
commonToAllUsers));
|
||||
|
||||
jassert (file != File::nonexistent);
|
||||
|
||||
if (file == File::nonexistent)
|
||||
return nullptr;
|
||||
|
||||
return new PropertiesFile (file, millisecondsBeforeSaving, propertiesFileOptions,processLock_);
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
||||
/*** End of inlined file: juce_PropertiesFile.cpp ***/
|
||||
|
|
@ -253709,7 +253726,7 @@ void DirectShowComponent::play()
|
|||
void DirectShowComponent::stop()
|
||||
{
|
||||
if (videoLoaded)
|
||||
context->stop();
|
||||
context->pause();
|
||||
}
|
||||
|
||||
bool DirectShowComponent::isPlaying() const
|
||||
|
|
@ -262523,12 +262540,14 @@ public:
|
|||
Pimpl (const String& name, const int timeOutMillisecs)
|
||||
: handle (0), refCount (1)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
// (don't use getSpecialLocation() to avoid the temp folder being different for each app)
|
||||
const File temp (File ("~/Library/Caches/Juce").getChildFile (name));
|
||||
#else
|
||||
const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name));
|
||||
#endif
|
||||
// Note that we can't get the normal temp folder here, as it might be different for each app.
|
||||
File tempFolder ("/var/tmp");
|
||||
|
||||
if (! tempFolder.isDirectory())
|
||||
tempFolder = "/tmp";
|
||||
|
||||
const File temp (tempFolder.getChildFile (name));
|
||||
|
||||
temp.create();
|
||||
handle = open (temp.getFullPathName().toUTF8(), O_RDWR);
|
||||
|
||||
|
|
@ -272561,12 +272580,14 @@ public:
|
|||
Pimpl (const String& name, const int timeOutMillisecs)
|
||||
: handle (0), refCount (1)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
// (don't use getSpecialLocation() to avoid the temp folder being different for each app)
|
||||
const File temp (File ("~/Library/Caches/Juce").getChildFile (name));
|
||||
#else
|
||||
const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name));
|
||||
#endif
|
||||
// Note that we can't get the normal temp folder here, as it might be different for each app.
|
||||
File tempFolder ("/var/tmp");
|
||||
|
||||
if (! tempFolder.isDirectory())
|
||||
tempFolder = "/tmp";
|
||||
|
||||
const File temp (tempFolder.getChildFile (name));
|
||||
|
||||
temp.create();
|
||||
handle = open (temp.getFullPathName().toUTF8(), O_RDWR);
|
||||
|
||||
|
|
@ -289448,12 +289469,14 @@ public:
|
|||
Pimpl (const String& name, const int timeOutMillisecs)
|
||||
: handle (0), refCount (1)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
// (don't use getSpecialLocation() to avoid the temp folder being different for each app)
|
||||
const File temp (File ("~/Library/Caches/Juce").getChildFile (name));
|
||||
#else
|
||||
const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name));
|
||||
#endif
|
||||
// Note that we can't get the normal temp folder here, as it might be different for each app.
|
||||
File tempFolder ("/var/tmp");
|
||||
|
||||
if (! tempFolder.isDirectory())
|
||||
tempFolder = "/tmp";
|
||||
|
||||
const File temp (tempFolder.getChildFile (name));
|
||||
|
||||
temp.create();
|
||||
handle = open (temp.getFullPathName().toUTF8(), O_RDWR);
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 53
|
||||
#define JUCE_BUILDNUMBER 100
|
||||
#define JUCE_BUILDNUMBER 101
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
@ -13871,6 +13871,11 @@ public:
|
|||
*/
|
||||
void setValue (const String& keyName, const XmlElement* xml);
|
||||
|
||||
/** This copies all the values from a source PropertySet to this one.
|
||||
This won't remove any existing settings, it just adds any that it finds in the source set.
|
||||
*/
|
||||
void addAllPropertiesFrom (const PropertySet& source);
|
||||
|
||||
/** Deletes a property.
|
||||
|
||||
@param keyName the name of the property to delete. (This mustn't be an empty string)
|
||||
|
|
@ -34973,43 +34978,124 @@ class JUCE_API PropertiesFile : public PropertySet,
|
|||
{
|
||||
public:
|
||||
|
||||
enum FileFormatOptions
|
||||
enum StorageFormat
|
||||
{
|
||||
ignoreCaseOfKeyNames = 1,
|
||||
storeAsBinary = 2,
|
||||
storeAsCompressedBinary = 4,
|
||||
storeAsXML = 8
|
||||
storeAsBinary,
|
||||
storeAsCompressedBinary,
|
||||
storeAsXML
|
||||
};
|
||||
|
||||
/**
|
||||
Creates a PropertiesFile object.
|
||||
struct Options
|
||||
{
|
||||
/** Creates an empty Options structure.
|
||||
You'll need to fill-in the data memebers appropriately before using this structure.
|
||||
*/
|
||||
Options();
|
||||
|
||||
@param file the file to use
|
||||
@param millisecondsBeforeSaving 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 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.
|
||||
@param optionFlags a combination of the flags in the FileFormatOptions
|
||||
enum, which specify the type of file to save, and other
|
||||
options.
|
||||
@param processLock 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 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.
|
||||
*/
|
||||
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".
|
||||
|
||||
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 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,
|
||||
int millisecondsBeforeSaving,
|
||||
int optionFlags,
|
||||
InterProcessLock* processLock = nullptr);
|
||||
const Options& options);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
When deleted, the file will first call saveIfNeeded() to flush any changes to disk.
|
||||
*/
|
||||
~PropertiesFile();
|
||||
|
|
@ -35054,64 +35140,21 @@ public:
|
|||
/** Returns the file that's being used. */
|
||||
File getFile() const { return file; }
|
||||
|
||||
/** Handy utility to create a properties file in whatever the standard OS-specific
|
||||
location is for these things.
|
||||
|
||||
This uses getDefaultAppSettingsFile() to decide what file to create, then
|
||||
creates a PropertiesFile object with the specified properties. See
|
||||
getDefaultAppSettingsFile() and the class's constructor for descriptions of
|
||||
what the parameters do.
|
||||
|
||||
@see getDefaultAppSettingsFile
|
||||
*/
|
||||
static PropertiesFile* createDefaultAppPropertiesFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
bool commonToAllUsers,
|
||||
int millisecondsBeforeSaving,
|
||||
int propertiesFileOptions,
|
||||
InterProcessLock* processLock = nullptr);
|
||||
|
||||
/** Handy utility to choose a file in the standard OS-dependent location for application
|
||||
settings files.
|
||||
|
||||
So on a Mac, this will return a file called:
|
||||
~/Library/Preferences/[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 you pass an empty string as the folder name, it'll use the app name for this (or
|
||||
omit the folder name on the Mac).
|
||||
|
||||
If commonToAllUsers is true, then this will return the same file for all users of the
|
||||
computer, regardless of the current user. If it is false, the file will be specific to
|
||||
only the current user. Use this to choose whether you're saving settings that are common
|
||||
or user-specific.
|
||||
*/
|
||||
static File getDefaultAppSettingsFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
bool commonToAllUsers);
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
virtual void propertyChanged();
|
||||
|
||||
private:
|
||||
|
||||
File file;
|
||||
int timerInterval;
|
||||
const int options;
|
||||
Options options;
|
||||
bool loadedOk, needsWriting;
|
||||
|
||||
InterProcessLock* processLock;
|
||||
typedef const ScopedPointer<InterProcessLock::ScopedLockType> ProcessScopedLock;
|
||||
InterProcessLock::ScopedLockType* createProcessLock() const;
|
||||
|
||||
void timerCallback();
|
||||
void initialise();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile);
|
||||
};
|
||||
|
|
@ -35158,16 +35201,9 @@ public:
|
|||
juce_DeclareSingleton (ApplicationProperties, false)
|
||||
|
||||
/** Gives the object the information it needs to create the appropriate properties files.
|
||||
|
||||
See the comments for PropertiesFile::createDefaultAppPropertiesFile() for more
|
||||
info about how these parameters are used.
|
||||
See the PropertiesFile::Options class for details about what options you need to set.
|
||||
*/
|
||||
void setStorageParameters (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
int millisecondsBeforeSaving,
|
||||
int propertiesFileOptions,
|
||||
InterProcessLock* processLock = nullptr);
|
||||
void setStorageParameters (const PropertiesFile::Options& options);
|
||||
|
||||
/** Tests whether the files can be successfully written to, and can show
|
||||
an error message if not.
|
||||
|
|
@ -35228,12 +35264,9 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
PropertiesFile::Options options;
|
||||
ScopedPointer <PropertiesFile> userProps, commonProps;
|
||||
|
||||
String appName, fileSuffix, folderName;
|
||||
int msBeforeSaving, options;
|
||||
int commonSettingsAreReadOnly;
|
||||
InterProcessLock* processLock;
|
||||
|
||||
void openFiles();
|
||||
|
||||
|
|
|
|||
|
|
@ -38,10 +38,7 @@ juce_ImplementSingleton (ApplicationProperties)
|
|||
|
||||
//==============================================================================
|
||||
ApplicationProperties::ApplicationProperties()
|
||||
: msBeforeSaving (3000),
|
||||
options (PropertiesFile::storeAsBinary),
|
||||
commonSettingsAreReadOnly (0),
|
||||
processLock (nullptr)
|
||||
: commonSettingsAreReadOnly (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -52,19 +49,9 @@ ApplicationProperties::~ApplicationProperties()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationProperties::setStorageParameters (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName_,
|
||||
const int millisecondsBeforeSaving,
|
||||
const int propertiesFileOptions,
|
||||
InterProcessLock* processLock_)
|
||||
void ApplicationProperties::setStorageParameters (const PropertiesFile::Options& newOptions)
|
||||
{
|
||||
appName = applicationName;
|
||||
fileSuffix = fileNameSuffix;
|
||||
folderName = folderName_;
|
||||
msBeforeSaving = millisecondsBeforeSaving;
|
||||
options = propertiesFileOptions;
|
||||
processLock = processLock_;
|
||||
options = newOptions;
|
||||
}
|
||||
|
||||
bool ApplicationProperties::testWriteAccess (const bool testUserSettings,
|
||||
|
|
@ -87,9 +74,9 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings,
|
|||
filenames << '\n' << commonProps->getFile().getFullPathName();
|
||||
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
appName + TRANS(" - Unable to save settings"),
|
||||
options.applicationName + TRANS(" - Unable to save settings"),
|
||||
TRANS("An error occurred when trying to save the application's settings file...\n\nIn order to save and restore its settings, ")
|
||||
+ appName + TRANS(" needs to be able to write to the following files:\n")
|
||||
+ options.applicationName + TRANS(" needs to be able to write to the following files:\n")
|
||||
+ filenames
|
||||
+ TRANS("\n\nMake sure that these files aren't read-only, and that the disk isn't full."));
|
||||
}
|
||||
|
|
@ -103,19 +90,24 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings,
|
|||
//==============================================================================
|
||||
void ApplicationProperties::openFiles()
|
||||
{
|
||||
// You need to call setStorageParameters() before trying to get hold of the
|
||||
// properties!
|
||||
jassert (appName.isNotEmpty());
|
||||
// You need to call setStorageParameters() before trying to get hold of the properties!
|
||||
jassert (options.applicationName.isNotEmpty());
|
||||
|
||||
if (appName.isNotEmpty())
|
||||
if (options.applicationName.isNotEmpty())
|
||||
{
|
||||
PropertiesFile::Options o (options);
|
||||
|
||||
if (userProps == nullptr)
|
||||
userProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName,
|
||||
false, msBeforeSaving, options, processLock);
|
||||
{
|
||||
o.commonToAllUsers = false;
|
||||
userProps = new PropertiesFile (o);
|
||||
}
|
||||
|
||||
if (commonProps == nullptr)
|
||||
commonProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName,
|
||||
true, msBeforeSaving, options, processLock);
|
||||
{
|
||||
o.commonToAllUsers = true;
|
||||
commonProps = new PropertiesFile (o);
|
||||
}
|
||||
|
||||
userProps->setFallbackPropertySet (commonProps);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,16 +73,9 @@ public:
|
|||
|
||||
//==============================================================================
|
||||
/** Gives the object the information it needs to create the appropriate properties files.
|
||||
|
||||
See the comments for PropertiesFile::createDefaultAppPropertiesFile() for more
|
||||
info about how these parameters are used.
|
||||
See the PropertiesFile::Options class for details about what options you need to set.
|
||||
*/
|
||||
void setStorageParameters (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
int millisecondsBeforeSaving,
|
||||
int propertiesFileOptions,
|
||||
InterProcessLock* processLock = nullptr);
|
||||
void setStorageParameters (const PropertiesFile::Options& options);
|
||||
|
||||
/** Tests whether the files can be successfully written to, and can show
|
||||
an error message if not.
|
||||
|
|
@ -146,12 +139,9 @@ public:
|
|||
|
||||
private:
|
||||
//==============================================================================
|
||||
PropertiesFile::Options options;
|
||||
ScopedPointer <PropertiesFile> userProps, commonProps;
|
||||
|
||||
String appName, fileSuffix, folderName;
|
||||
int msBeforeSaving, options;
|
||||
int commonSettingsAreReadOnly;
|
||||
InterProcessLock* processLock;
|
||||
|
||||
void openFiles();
|
||||
|
||||
|
|
|
|||
|
|
@ -175,6 +175,15 @@ bool PropertySet::containsKey (const String& keyName) const noexcept
|
|||
return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys);
|
||||
}
|
||||
|
||||
void PropertySet::addAllPropertiesFrom (const PropertySet& source)
|
||||
{
|
||||
const ScopedLock sl (source.getLock());
|
||||
|
||||
for (int i = 0; i < source.properties.size(); ++i)
|
||||
setValue (source.properties.getAllKeys() [i],
|
||||
source.properties.getAllValues() [i]);
|
||||
}
|
||||
|
||||
void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) noexcept
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
|
|
|||
|
|
@ -145,6 +145,11 @@ public:
|
|||
*/
|
||||
void setValue (const String& keyName, const XmlElement* xml);
|
||||
|
||||
/** This copies all the values from a source PropertySet to this one.
|
||||
This won't remove any existing settings, it just adds any that it finds in the source set.
|
||||
*/
|
||||
void addAllPropertiesFrom (const PropertySet& source);
|
||||
|
||||
//==============================================================================
|
||||
/** Deletes a property.
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 53
|
||||
#define JUCE_BUILDNUMBER 100
|
||||
#define JUCE_BUILDNUMBER 101
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
|
|||
|
|
@ -673,12 +673,14 @@ public:
|
|||
Pimpl (const String& name, const int timeOutMillisecs)
|
||||
: handle (0), refCount (1)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
// (don't use getSpecialLocation() to avoid the temp folder being different for each app)
|
||||
const File temp (File ("~/Library/Caches/Juce").getChildFile (name));
|
||||
#else
|
||||
const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name));
|
||||
#endif
|
||||
// Note that we can't get the normal temp folder here, as it might be different for each app.
|
||||
File tempFolder ("/var/tmp");
|
||||
|
||||
if (! tempFolder.isDirectory())
|
||||
tempFolder = "/tmp";
|
||||
|
||||
const File temp (tempFolder.getChildFile (name));
|
||||
|
||||
temp.create();
|
||||
handle = open (temp.getFullPathName().toUTF8(), O_RDWR);
|
||||
|
||||
|
|
|
|||
|
|
@ -896,7 +896,7 @@ void DirectShowComponent::play()
|
|||
void DirectShowComponent::stop()
|
||||
{
|
||||
if (videoLoaded)
|
||||
context->stop();
|
||||
context->pause();
|
||||
}
|
||||
|
||||
bool DirectShowComponent::isPlaying() const
|
||||
|
|
|
|||
|
|
@ -54,27 +54,99 @@ namespace PropertyFileConstants
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile::PropertiesFile (const File& f, const int millisecondsBeforeSaving,
|
||||
const int options_, InterProcessLock* const processLock_)
|
||||
: PropertySet (ignoreCaseOfKeyNames),
|
||||
file (f),
|
||||
timerInterval (millisecondsBeforeSaving),
|
||||
options (options_),
|
||||
loadedOk (false),
|
||||
needsWriting (false),
|
||||
processLock (processLock_)
|
||||
PropertiesFile::Options::Options()
|
||||
: commonToAllUsers (false),
|
||||
ignoreCaseOfKeyNames (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 != "Application Support")
|
||||
{
|
||||
/* 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".
|
||||
|
||||
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_ANDROID
|
||||
const File dir ((commonToAllUsers ? "/var/" : "~/")
|
||||
+ (folderName.isNotEmpty() ? folderName
|
||||
: ("." + applicationName)));
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
|
||||
: File::userApplicationDataDirectory));
|
||||
|
||||
if (dir == File::nonexistent)
|
||||
return File::nonexistent;
|
||||
|
||||
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: applicationName);
|
||||
#endif
|
||||
|
||||
return dir.getChildFile (applicationName)
|
||||
.withFileExtension (filenameSuffix);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile::PropertiesFile (const File& file_, const Options& options_)
|
||||
: PropertySet (options.ignoreCaseOfKeyNames),
|
||||
file (file_), options (options_),
|
||||
loadedOk (false), needsWriting (false)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
|
||||
PropertiesFile::PropertiesFile (const Options& options_)
|
||||
: PropertySet (options.ignoreCaseOfKeyNames),
|
||||
file (options_.getDefaultFile()), options (options_),
|
||||
loadedOk (false), needsWriting (false)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
|
||||
void PropertiesFile::initialise()
|
||||
{
|
||||
// You need to correctly specify just one storage format for the file
|
||||
jassert ((options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsBinary
|
||||
|| (options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsCompressedBinary
|
||||
|| (options_ & (storeAsBinary | storeAsCompressedBinary | storeAsXML)) == storeAsXML);
|
||||
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return; // locking failure..
|
||||
|
||||
ScopedPointer<InputStream> fileStream (f.createInputStream());
|
||||
ScopedPointer<InputStream> fileStream (file.createInputStream());
|
||||
|
||||
if (fileStream != nullptr)
|
||||
{
|
||||
|
|
@ -108,7 +180,7 @@ PropertiesFile::PropertiesFile (const File& f, const int millisecondsBeforeSavin
|
|||
// Not a binary props file - let's see if it's XML..
|
||||
fileStream = nullptr;
|
||||
|
||||
XmlDocument parser (f);
|
||||
XmlDocument parser (file);
|
||||
ScopedPointer<XmlElement> doc (parser.getDocumentElement (true));
|
||||
|
||||
if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag))
|
||||
|
|
@ -144,7 +216,7 @@ PropertiesFile::PropertiesFile (const File& f, const int millisecondsBeforeSavin
|
|||
}
|
||||
else
|
||||
{
|
||||
loadedOk = ! f.exists();
|
||||
loadedOk = ! file.exists();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +228,7 @@ PropertiesFile::~PropertiesFile()
|
|||
|
||||
InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
|
||||
{
|
||||
return processLock != nullptr ? new InterProcessLock::ScopedLockType (*processLock) : nullptr;
|
||||
return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveIfNeeded()
|
||||
|
|
@ -188,7 +260,7 @@ bool PropertiesFile::save()
|
|||
|| ! file.getParentDirectory().createDirectory())
|
||||
return false;
|
||||
|
||||
if ((options & storeAsXML) != 0)
|
||||
if (options.storageFormat == storeAsXML)
|
||||
{
|
||||
XmlElement doc (PropertyFileConstants::fileTag);
|
||||
|
||||
|
|
@ -230,7 +302,7 @@ bool PropertiesFile::save()
|
|||
|
||||
if (out != nullptr)
|
||||
{
|
||||
if ((options & storeAsCompressedBinary) != 0)
|
||||
if (options.storageFormat == storeAsCompressedBinary)
|
||||
{
|
||||
out->writeInt (PropertyFileConstants::magicNumberCompressed);
|
||||
out->flush();
|
||||
|
|
@ -240,7 +312,7 @@ bool PropertiesFile::save()
|
|||
else
|
||||
{
|
||||
// have you set up the storage option flags correctly?
|
||||
jassert ((options & storeAsBinary) != 0);
|
||||
jassert (options.storageFormat == storeAsBinary);
|
||||
|
||||
out->writeInt (PropertyFileConstants::magicNumber);
|
||||
}
|
||||
|
|
@ -279,66 +351,11 @@ void PropertiesFile::propertyChanged()
|
|||
|
||||
needsWriting = true;
|
||||
|
||||
if (timerInterval > 0)
|
||||
startTimer (timerInterval);
|
||||
else if (timerInterval == 0)
|
||||
if (options.millisecondsBeforeSaving > 0)
|
||||
startTimer (options.millisecondsBeforeSaving);
|
||||
else if (options.millisecondsBeforeSaving == 0)
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File PropertiesFile::getDefaultAppSettingsFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
const bool commonToAllUsers)
|
||||
{
|
||||
// mustn't have illegal characters in this name..
|
||||
jassert (applicationName == File::createLegalFileName (applicationName));
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
File dir (commonToAllUsers ? "/Library/Preferences"
|
||||
: "~/Library/Preferences");
|
||||
|
||||
if (folderName.isNotEmpty())
|
||||
dir = dir.getChildFile (folderName);
|
||||
#elif JUCE_LINUX || JUCE_ANDROID
|
||||
const File dir ((commonToAllUsers ? "/var/" : "~/")
|
||||
+ (folderName.isNotEmpty() ? folderName
|
||||
: ("." + applicationName)));
|
||||
#elif JUCE_WINDOWS
|
||||
File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
|
||||
: File::userApplicationDataDirectory));
|
||||
|
||||
if (dir == File::nonexistent)
|
||||
return File::nonexistent;
|
||||
|
||||
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: applicationName);
|
||||
#endif
|
||||
|
||||
return dir.getChildFile (applicationName)
|
||||
.withFileExtension (fileNameSuffix);
|
||||
}
|
||||
|
||||
PropertiesFile* PropertiesFile::createDefaultAppPropertiesFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
const bool commonToAllUsers,
|
||||
const int millisecondsBeforeSaving,
|
||||
const int propertiesFileOptions,
|
||||
InterProcessLock* processLock_)
|
||||
{
|
||||
const File file (getDefaultAppSettingsFile (applicationName,
|
||||
fileNameSuffix,
|
||||
folderName,
|
||||
commonToAllUsers));
|
||||
|
||||
jassert (file != File::nonexistent);
|
||||
|
||||
if (file == File::nonexistent)
|
||||
return nullptr;
|
||||
|
||||
return new PropertiesFile (file, millisecondsBeforeSaving, propertiesFileOptions,processLock_);
|
||||
}
|
||||
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -53,44 +53,126 @@ class JUCE_API PropertiesFile : public PropertySet,
|
|||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum FileFormatOptions
|
||||
enum StorageFormat
|
||||
{
|
||||
ignoreCaseOfKeyNames = 1,
|
||||
storeAsBinary = 2,
|
||||
storeAsCompressedBinary = 4,
|
||||
storeAsXML = 8
|
||||
storeAsBinary,
|
||||
storeAsCompressedBinary,
|
||||
storeAsXML
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a PropertiesFile object.
|
||||
struct Options
|
||||
{
|
||||
/** Creates an empty Options structure.
|
||||
You'll need to fill-in the data memebers appropriately before using this structure.
|
||||
*/
|
||||
Options();
|
||||
|
||||
@param file the file to use
|
||||
@param millisecondsBeforeSaving 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 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.
|
||||
@param optionFlags a combination of the flags in the FileFormatOptions
|
||||
enum, which specify the type of file to save, and other
|
||||
options.
|
||||
@param processLock 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 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.
|
||||
*/
|
||||
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".
|
||||
|
||||
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 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,
|
||||
int millisecondsBeforeSaving,
|
||||
int optionFlags,
|
||||
InterProcessLock* processLock = nullptr);
|
||||
const Options& options);
|
||||
|
||||
/** Destructor.
|
||||
|
||||
When deleted, the file will first call saveIfNeeded() to flush any changes to disk.
|
||||
*/
|
||||
~PropertiesFile();
|
||||
|
|
@ -138,65 +220,22 @@ public:
|
|||
/** Returns the file that's being used. */
|
||||
File getFile() const { return file; }
|
||||
|
||||
//==============================================================================
|
||||
/** Handy utility to create a properties file in whatever the standard OS-specific
|
||||
location is for these things.
|
||||
|
||||
This uses getDefaultAppSettingsFile() to decide what file to create, then
|
||||
creates a PropertiesFile object with the specified properties. See
|
||||
getDefaultAppSettingsFile() and the class's constructor for descriptions of
|
||||
what the parameters do.
|
||||
|
||||
@see getDefaultAppSettingsFile
|
||||
*/
|
||||
static PropertiesFile* createDefaultAppPropertiesFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
bool commonToAllUsers,
|
||||
int millisecondsBeforeSaving,
|
||||
int propertiesFileOptions,
|
||||
InterProcessLock* processLock = nullptr);
|
||||
|
||||
/** Handy utility to choose a file in the standard OS-dependent location for application
|
||||
settings files.
|
||||
|
||||
So on a Mac, this will return a file called:
|
||||
~/Library/Preferences/[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 you pass an empty string as the folder name, it'll use the app name for this (or
|
||||
omit the folder name on the Mac).
|
||||
|
||||
If commonToAllUsers is true, then this will return the same file for all users of the
|
||||
computer, regardless of the current user. If it is false, the file will be specific to
|
||||
only the current user. Use this to choose whether you're saving settings that are common
|
||||
or user-specific.
|
||||
*/
|
||||
static File getDefaultAppSettingsFile (const String& applicationName,
|
||||
const String& fileNameSuffix,
|
||||
const String& folderName,
|
||||
bool commonToAllUsers);
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
virtual void propertyChanged();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
File file;
|
||||
int timerInterval;
|
||||
const int options;
|
||||
Options options;
|
||||
bool loadedOk, needsWriting;
|
||||
|
||||
InterProcessLock* processLock;
|
||||
typedef const ScopedPointer<InterProcessLock::ScopedLockType> ProcessScopedLock;
|
||||
InterProcessLock::ScopedLockType* createProcessLock() const;
|
||||
|
||||
void timerCallback();
|
||||
void initialise();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue