From 74469aaa83a0d1969b5f3c82a264004d8b236f9b Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Wed, 15 Jun 2011 16:16:33 +0100 Subject: [PATCH] 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. --- .../Source/Utility/jucer_StoredSettings.cpp | 28 +- .../audio plugin host/Source/HostStartup.cpp | 11 +- .../src/utility/jucer_StoredSettings.cpp | 28 +- .../src/utility/jucer_StoredSettings.h | 2 +- juce_amalgamated.cpp | 267 ++++++++++-------- juce_amalgamated.h | 211 ++++++++------ .../juce_ApplicationProperties.cpp | 44 ++- src/application/juce_ApplicationProperties.h | 16 +- src/containers/juce_PropertySet.cpp | 9 + src/containers/juce_PropertySet.h | 5 + src/core/juce_StandardHeader.h | 2 +- src/native/common/juce_posix_SharedCode.h | 14 +- .../juce_win32_DirectShowComponent.cpp | 2 +- src/utilities/juce_PropertiesFile.cpp | 173 +++++++----- src/utilities/juce_PropertiesFile.h | 189 ++++++++----- 15 files changed, 568 insertions(+), 433 deletions(-) diff --git a/extras/Introjucer/Source/Utility/jucer_StoredSettings.cpp b/extras/Introjucer/Source/Utility/jucer_StoredSettings.cpp index 39962cce2d..6609953df1 100644 --- a/extras/Introjucer/Source/Utility/jucer_StoredSettings.cpp +++ b/extras/Introjucer/Source/Utility/jucer_StoredSettings.cpp @@ -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")); diff --git a/extras/audio plugin host/Source/HostStartup.cpp b/extras/audio plugin host/Source/HostStartup.cpp index 46cd8541b6..179b3a0b76 100644 --- a/extras/audio plugin host/Source/HostStartup.cpp +++ b/extras/audio plugin host/Source/HostStartup.cpp @@ -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(); diff --git a/extras/the jucer/src/utility/jucer_StoredSettings.cpp b/extras/the jucer/src/utility/jucer_StoredSettings.cpp index 560b31ee46..d7adc126e1 100644 --- a/extras/the jucer/src/utility/jucer_StoredSettings.cpp +++ b/extras/the jucer/src/utility/jucer_StoredSettings.cpp @@ -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 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")); diff --git a/extras/the jucer/src/utility/jucer_StoredSettings.h b/extras/the jucer/src/utility/jucer_StoredSettings.h index 4401402206..0c65e5d023 100644 --- a/extras/the jucer/src/utility/jucer_StoredSettings.h +++ b/extras/the jucer/src/utility/jucer_StoredSettings.h @@ -53,7 +53,7 @@ public: Array swatchColours; private: - PropertiesFile* props; + ScopedPointer props; }; diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index beb6cfc446..bf69c09963 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -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 fileStream (f.createInputStream()); + ScopedPointer 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 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); diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 7093485eca..026204a810 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -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 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 userProps, commonProps; - - String appName, fileSuffix, folderName; - int msBeforeSaving, options; int commonSettingsAreReadOnly; - InterProcessLock* processLock; void openFiles(); diff --git a/src/application/juce_ApplicationProperties.cpp b/src/application/juce_ApplicationProperties.cpp index d153cafd45..0ef49124d3 100644 --- a/src/application/juce_ApplicationProperties.cpp +++ b/src/application/juce_ApplicationProperties.cpp @@ -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); } diff --git a/src/application/juce_ApplicationProperties.h b/src/application/juce_ApplicationProperties.h index 94170eabc3..2ffbf9f4eb 100644 --- a/src/application/juce_ApplicationProperties.h +++ b/src/application/juce_ApplicationProperties.h @@ -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 userProps, commonProps; - - String appName, fileSuffix, folderName; - int msBeforeSaving, options; int commonSettingsAreReadOnly; - InterProcessLock* processLock; void openFiles(); diff --git a/src/containers/juce_PropertySet.cpp b/src/containers/juce_PropertySet.cpp index 5398dc72bc..629e0cda9a 100644 --- a/src/containers/juce_PropertySet.cpp +++ b/src/containers/juce_PropertySet.cpp @@ -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); diff --git a/src/containers/juce_PropertySet.h b/src/containers/juce_PropertySet.h index 7b2bc4163f..5d19869c37 100644 --- a/src/containers/juce_PropertySet.h +++ b/src/containers/juce_PropertySet.h @@ -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. diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 88557a4285..65aee36a42 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -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. diff --git a/src/native/common/juce_posix_SharedCode.h b/src/native/common/juce_posix_SharedCode.h index 7106bebbc0..18ffc8aeb9 100644 --- a/src/native/common/juce_posix_SharedCode.h +++ b/src/native/common/juce_posix_SharedCode.h @@ -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); diff --git a/src/native/windows/juce_win32_DirectShowComponent.cpp b/src/native/windows/juce_win32_DirectShowComponent.cpp index 7c98a2c511..3a01b1a2a0 100644 --- a/src/native/windows/juce_win32_DirectShowComponent.cpp +++ b/src/native/windows/juce_win32_DirectShowComponent.cpp @@ -896,7 +896,7 @@ void DirectShowComponent::play() void DirectShowComponent::stop() { if (videoLoaded) - context->stop(); + context->pause(); } bool DirectShowComponent::isPlaying() const diff --git a/src/utilities/juce_PropertiesFile.cpp b/src/utilities/juce_PropertiesFile.cpp index e6bcc8b4ab..8ff140c15c 100644 --- a/src/utilities/juce_PropertiesFile.cpp +++ b/src/utilities/juce_PropertiesFile.cpp @@ -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 fileStream (f.createInputStream()); + ScopedPointer 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 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 diff --git a/src/utilities/juce_PropertiesFile.h b/src/utilities/juce_PropertiesFile.h index c08b917acd..5706f44228 100644 --- a/src/utilities/juce_PropertiesFile.h +++ b/src/utilities/juce_PropertiesFile.h @@ -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 ProcessScopedLock; InterProcessLock::ScopedLockType* createProcessLock() const; void timerCallback(); + void initialise(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile); };