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.tags/2021-05-28
| @@ -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; | |||
| { | |||
| PropertiesFile::Options options; | |||
| options.applicationName = "Jucer"; | |||
| options.filenameSuffix = "settings"; | |||
| options.osxLibrarySubFolder = "Preferences"; | |||
| props = PropertiesFile::createDefaultAppPropertiesFile ("Jucer", | |||
| "settings", | |||
| String::empty, | |||
| false, 3000, | |||
| PropertiesFile::storeAsXML); | |||
| 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) | |||
| { | |||
| // 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); | |||
| } | |||
| 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 | |||
| 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. | |||
| @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. | |||
| struct Options | |||
| { | |||
| /** Creates an empty Options structure. | |||
| You'll need to fill-in the data memebers appropriately before using this structure. | |||
| */ | |||
| Options(); | |||
| /** The name of your application - this is used to help generate the path and filename | |||
| at which the properties file will be stored. */ | |||
| String applicationName; | |||
| /** The suffix to use for your properties file. | |||
| It doesn't really matter what this is - you may want to use ".settings" or | |||
| ".properties" or something. | |||
| */ | |||
| 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) | |||
| { | |||
| // 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); | |||
| } | |||
| 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 | |||
| 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. | |||
| @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. | |||
| struct Options | |||
| { | |||
| /** Creates an empty Options structure. | |||
| You'll need to fill-in the data memebers appropriately before using this structure. | |||
| */ | |||
| Options(); | |||
| /** The name of your application - this is used to help generate the path and filename | |||
| at which the properties file will be stored. */ | |||
| String applicationName; | |||
| /** The suffix to use for your properties file. | |||
| It doesn't really matter what this is - you may want to use ".settings" or | |||
| ".properties" or something. | |||
| */ | |||
| 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); | |||
| }; | |||