| @@ -20,6 +20,8 @@ Changelist for version 1.44 | |||
| - added MultiDocumentPanel::createNewDocumentWindow() method to allow creation of custom document windows in a MultiDocumentPanel | |||
| - added a Thread::getCurrentThread() method | |||
| - added an option to MessageManagerLock that can check for thread termination, to avoid deadlocks. | |||
| - new method: PropertySet::setFallbackPropertySet() | |||
| - some simplifications to ApplicationProperties, because of problems it was causing when there were read-only common property files. | |||
| ============================================================================== | |||
| Changelist for version 1.43 | |||
| @@ -47,7 +47,8 @@ ApplicationProperties::ApplicationProperties() throw() | |||
| : userProps (0), | |||
| commonProps (0), | |||
| msBeforeSaving (3000), | |||
| options (PropertiesFile::storeAsBinary) | |||
| options (PropertiesFile::storeAsBinary), | |||
| commonSettingsAreReadOnly (0) | |||
| { | |||
| } | |||
| @@ -75,9 +76,8 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings, | |||
| const bool testCommonSettings, | |||
| const bool showWarningDialogOnFailure) | |||
| { | |||
| const bool userOk = (! testUserSettings) || getUserSettings()->save(); | |||
| const bool commonOk = (! testCommonSettings) | |||
| || (userProps != getCommonSettings() && getCommonSettings()->save()); | |||
| const bool userOk = (! testUserSettings) || getUserSettings()->save(); | |||
| const bool commonOk = (! testCommonSettings) || getCommonSettings (false)->save(); | |||
| if (! (userOk && commonOk)) | |||
| { | |||
| @@ -85,26 +85,16 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings, | |||
| { | |||
| String filenames; | |||
| if (! userOk) | |||
| filenames << "\n" << getUserSettings()->getFile().getFullPathName(); | |||
| if (userProps != 0 && ! userOk) | |||
| filenames << '\n' << userProps->getFile().getFullPathName(); | |||
| if (! commonOk) | |||
| { | |||
| PropertiesFile* const realCommonProps | |||
| = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, | |||
| true, msBeforeSaving, options); | |||
| if (realCommonProps != 0) | |||
| { | |||
| filenames << "\n" << realCommonProps->getFile().getFullPathName(); | |||
| delete realCommonProps; | |||
| } | |||
| } | |||
| if (commonProps != 0 && ! commonOk) | |||
| filenames << '\n' << commonProps->getFile().getFullPathName(); | |||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||
| appName + 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") | |||
| + appName + 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.")); | |||
| } | |||
| @@ -116,45 +106,46 @@ bool ApplicationProperties::testWriteAccess (const bool testUserSettings, | |||
| } | |||
| //============================================================================== | |||
| PropertiesFile* ApplicationProperties::getUserSettings() throw() | |||
| void ApplicationProperties::openFiles() throw() | |||
| { | |||
| if (userProps == 0) | |||
| { | |||
| // 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 (appName.isNotEmpty()); | |||
| if (appName.isNotEmpty()) | |||
| if (appName.isNotEmpty()) | |||
| { | |||
| if (userProps == 0) | |||
| userProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, | |||
| false, msBeforeSaving, options); | |||
| if (userProps == 0) | |||
| { | |||
| // create an emergency properties object to avoid returning a null pointer.. | |||
| userProps = new PropertiesFile (File::nonexistent, msBeforeSaving, options); | |||
| } | |||
| if (commonProps == 0) | |||
| commonProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, | |||
| true, msBeforeSaving, options); | |||
| userProps->setFallbackPropertySet (commonProps); | |||
| } | |||
| } | |||
| PropertiesFile* ApplicationProperties::getUserSettings() throw() | |||
| { | |||
| if (userProps == 0) | |||
| openFiles(); | |||
| return userProps; | |||
| } | |||
| PropertiesFile* ApplicationProperties::getCommonSettings() throw() | |||
| PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly) throw() | |||
| { | |||
| if (commonProps == 0) | |||
| { | |||
| // You need to call setStorageParameters() before trying to get hold of the | |||
| // properties! | |||
| jassert (appName.isNotEmpty()); | |||
| openFiles(); | |||
| if (appName.isNotEmpty()) | |||
| commonProps = PropertiesFile::createDefaultAppPropertiesFile (appName, fileSuffix, folderName, | |||
| true, msBeforeSaving, options); | |||
| if (commonProps == 0 || ! commonProps->save()) | |||
| { | |||
| delete commonProps; | |||
| commonProps = getUserSettings(); | |||
| } | |||
| if (returnUserPropsIfReadOnly) | |||
| { | |||
| if (commonSettingsAreReadOnly == 0) | |||
| commonSettingsAreReadOnly = commonProps->save() ? -1 : 1; | |||
| if (commonSettingsAreReadOnly > 0) | |||
| return userProps; | |||
| } | |||
| return commonProps; | |||
| @@ -163,17 +154,13 @@ PropertiesFile* ApplicationProperties::getCommonSettings() throw() | |||
| bool ApplicationProperties::saveIfNeeded() | |||
| { | |||
| return (userProps == 0 || userProps->saveIfNeeded()) | |||
| && (commonProps == 0 || commonProps == userProps || commonProps->saveIfNeeded()); | |||
| && (commonProps == 0 || commonProps->saveIfNeeded()); | |||
| } | |||
| void ApplicationProperties::closeFiles() | |||
| { | |||
| delete userProps; | |||
| if (commonProps != userProps) | |||
| delete commonProps; | |||
| userProps = commonProps = 0; | |||
| deleteAndZero (userProps); | |||
| deleteAndZero (commonProps); | |||
| } | |||
| @@ -106,7 +106,12 @@ public: | |||
| //============================================================================== | |||
| /** Returns the user settings file. | |||
| The first time this is called, it will create and load the file. | |||
| The first time this is called, it will create and load the properties file. | |||
| Note that when you search the user PropertiesFile for a value that it doesn't contain, | |||
| the common settings are used as a second-chance place to look. This is done via the | |||
| PropertySet::setFallbackPropertySet() method - by default the common settings are set | |||
| to the fallback for the user settings. | |||
| @see getCommonSettings | |||
| */ | |||
| @@ -114,14 +119,19 @@ public: | |||
| /** Returns the common settings file. | |||
| The first time this is called, it will create and load the file. | |||
| If the common settings file doesn't have write access but the user one does, | |||
| then this may return the same PropertiesFile object as getUserSettings(). | |||
| The first time this is called, it will create and load the properties file. | |||
| @param returnUserPropsIfReadOnly if this is true, and the common properties file is | |||
| read-only (e.g. because the user doesn't have permission to write | |||
| to shared files), then this will return the user settings instead, | |||
| (like getUserSettings() would do). This is handy if you'd like to | |||
| write a value to the common settings, but if that's no possible, | |||
| then you'd rather write to the user settings than none at all. | |||
| If returnUserPropsIfReadOnly is false, this method will always return | |||
| the common settings, even if any changes to them can't be saved. | |||
| @see getUserSettings | |||
| */ | |||
| PropertiesFile* getCommonSettings() throw(); | |||
| PropertiesFile* getCommonSettings (const bool returnUserPropsIfReadOnly) throw(); | |||
| //============================================================================== | |||
| /** Saves both files if they need to be saved. | |||
| @@ -138,6 +148,7 @@ public: | |||
| */ | |||
| void closeFiles(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| @@ -148,9 +159,12 @@ private: | |||
| String appName, fileSuffix, folderName; | |||
| int msBeforeSaving, options; | |||
| int commonSettingsAreReadOnly; | |||
| ApplicationProperties (const ApplicationProperties&); | |||
| const ApplicationProperties& operator= (const ApplicationProperties&); | |||
| void openFiles() throw(); | |||
| }; | |||
| @@ -144,13 +144,23 @@ bool PropertiesFile::saveIfNeeded() | |||
| return (! needsWriting) || save(); | |||
| } | |||
| bool PropertiesFile::needsToBeSaved() const throw() | |||
| { | |||
| const ScopedLock sl (getLock()); | |||
| return needsWriting; | |||
| } | |||
| bool PropertiesFile::save() | |||
| { | |||
| const ScopedLock sl (getLock()); | |||
| stopTimer(); | |||
| if (file == File::nonexistent || file.isDirectory()) | |||
| if (file == File::nonexistent | |||
| || file.isDirectory() | |||
| || ! file.getParentDirectory().createDirectory()) | |||
| return false; | |||
| if ((options & storeAsXML) != 0) | |||
| @@ -292,7 +302,9 @@ PropertiesFile* PropertiesFile::createDefaultAppPropertiesFile (const String& ap | |||
| folderName, | |||
| commonToAllUsers)); | |||
| if (file == File::nonexistent || ! file.getParentDirectory().createDirectory()) | |||
| jassert (file != File::nonexistent); | |||
| if (file == File::nonexistent) | |||
| return 0; | |||
| return new PropertiesFile (file, millisecondsBeforeSaving, propertiesFileOptions); | |||
| @@ -114,6 +114,11 @@ public: | |||
| */ | |||
| bool save(); | |||
| /** Returns true if the properties have been altered since the last time they were | |||
| saved. | |||
| */ | |||
| bool needsToBeSaved() const throw(); | |||
| //============================================================================== | |||
| /** Returns the file that's being used. */ | |||
| const File getFile() const throw(); | |||
| @@ -225,12 +225,16 @@ public: | |||
| If generateTooltip is true, then the button's tooltip will be automatically | |||
| generated based on the name of this command and its current shortcut key. | |||
| @see addShortcut | |||
| @see addShortcut, getCommandID | |||
| */ | |||
| void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse, | |||
| const int commandID, | |||
| const bool generateTooltip); | |||
| /** Returns the command ID that was set by setCommandToTrigger(). | |||
| */ | |||
| int getCommandID() const throw() { return commandID; } | |||
| //============================================================================== | |||
| /** Assigns a shortcut key to trigger the button. | |||
| @@ -45,24 +45,6 @@ static const int titleH = 24; | |||
| static const int iconWidth = 80; | |||
| //============================================================================== | |||
| class AlertWindowTextButton : public TextButton | |||
| { | |||
| public: | |||
| AlertWindowTextButton (const String& text) | |||
| : TextButton (text, String::empty) | |||
| { | |||
| setWantsKeyboardFocus (true); | |||
| setMouseClickGrabsKeyboardFocus (false); | |||
| } | |||
| int returnValue; | |||
| private: | |||
| AlertWindowTextButton (const AlertWindowTextButton&); | |||
| const AlertWindowTextButton& operator= (const AlertWindowTextButton&); | |||
| }; | |||
| //============================================================================== | |||
| class AlertWindowTextEditor : public TextEditor | |||
| { | |||
| @@ -89,13 +71,13 @@ public: | |||
| void returnPressed() | |||
| { | |||
| // pass these up the component hierarchy to be trigger the buttons | |||
| Component::keyPressed (KeyPress (KeyPress::returnKey, 0, T('\n'))); | |||
| getParentComponent()->keyPressed (KeyPress (KeyPress::returnKey, 0, T('\n'))); | |||
| } | |||
| void escapePressed() | |||
| { | |||
| // pass these up the component hierarchy to be trigger the buttons | |||
| Component::keyPressed (KeyPress (KeyPress::escapeKey, 0, 0)); | |||
| getParentComponent()->keyPressed (KeyPress (KeyPress::escapeKey, 0, 0)); | |||
| } | |||
| private: | |||
| @@ -134,8 +116,6 @@ AlertWindow::AlertWindow (const String& title, | |||
| lookAndFeelChanged(); | |||
| constrainer.setMinimumOnscreenAmounts (0x10000, 0x10000, 0x10000, 0x10000); | |||
| setWantsKeyboardFocus (getNumChildComponents() == 0); | |||
| } | |||
| AlertWindow::~AlertWindow() | |||
| @@ -172,12 +152,12 @@ void AlertWindow::buttonClicked (Button* button) | |||
| { | |||
| for (int i = 0; i < buttons.size(); i++) | |||
| { | |||
| AlertWindowTextButton* const c = (AlertWindowTextButton*) buttons[i]; | |||
| TextButton* const c = (TextButton*) buttons[i]; | |||
| if (button->getName() == c->getName()) | |||
| { | |||
| if (c->getParentComponent() != 0) | |||
| c->getParentComponent()->exitModalState (c->returnValue); | |||
| c->getParentComponent()->exitModalState (c->getCommandID()); | |||
| break; | |||
| } | |||
| @@ -190,9 +170,11 @@ void AlertWindow::addButton (const String& name, | |||
| const KeyPress& shortcutKey1, | |||
| const KeyPress& shortcutKey2) | |||
| { | |||
| AlertWindowTextButton* const b = new AlertWindowTextButton (name); | |||
| TextButton* const b = new TextButton (name, String::empty); | |||
| b->returnValue = returnValue; | |||
| b->setWantsKeyboardFocus (true); | |||
| b->setMouseClickGrabsKeyboardFocus (false); | |||
| b->setCommandToTrigger (0, returnValue, false); | |||
| b->addShortcut (shortcutKey1); | |||
| b->addShortcut (shortcutKey2); | |||
| b->addButtonListener (this); | |||
| @@ -286,6 +268,7 @@ public: | |||
| setCaretVisible (false); | |||
| setScrollbarsShown (true); | |||
| lookAndFeelChanged(); | |||
| setWantsKeyboardFocus (false); | |||
| setFont (font); | |||
| setText (message, false); | |||
| @@ -441,14 +424,14 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) | |||
| int buttonW = 40; | |||
| int i; | |||
| for (i = 0; i < buttons.size(); ++i) | |||
| buttonW += 16 + ((const AlertWindowTextButton*) buttons[i])->getWidth(); | |||
| buttonW += 16 + ((const TextButton*) buttons[i])->getWidth(); | |||
| w = jmax (buttonW, w); | |||
| h += (textBoxes.size() + comboBoxes.size() + progressBars.size()) * 50; | |||
| if (buttons.size() > 0) | |||
| h += 20 + ((AlertWindowTextButton*) buttons[0])->getHeight(); | |||
| h += 20 + ((TextButton*) buttons[0])->getHeight(); | |||
| for (i = customComps.size(); --i >= 0;) | |||
| { | |||
| @@ -499,14 +482,14 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) | |||
| int totalWidth = -spacer; | |||
| for (i = buttons.size(); --i >= 0;) | |||
| totalWidth += ((AlertWindowTextButton*) buttons[i])->getWidth() + spacer; | |||
| totalWidth += ((TextButton*) buttons[i])->getWidth() + spacer; | |||
| int x = (w - totalWidth) / 2; | |||
| int y = (int) (getHeight() * 0.95f); | |||
| for (i = 0; i < buttons.size(); ++i) | |||
| { | |||
| AlertWindowTextButton* const c = (AlertWindowTextButton*) buttons[i]; | |||
| TextButton* const c = (TextButton*) buttons[i]; | |||
| int ny = proportionOfHeight (0.95f) - c->getHeight(); | |||
| c->setTopLeftPosition (x, ny); | |||
| if (ny < y) | |||
| @@ -544,6 +527,8 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) | |||
| y += h + 10; | |||
| } | |||
| } | |||
| setWantsKeyboardFocus (getNumChildComponents() == 0); | |||
| } | |||
| bool AlertWindow::containsAnyExtraComponents() const | |||
| @@ -569,7 +554,7 @@ bool AlertWindow::keyPressed (const KeyPress& key) | |||
| { | |||
| for (int i = buttons.size(); --i >= 0;) | |||
| { | |||
| AlertWindowTextButton* const b = (AlertWindowTextButton*) buttons[i]; | |||
| TextButton* const b = (TextButton*) buttons[i]; | |||
| if (b->isRegisteredForShortcut (key)) | |||
| { | |||
| @@ -585,7 +570,7 @@ bool AlertWindow::keyPressed (const KeyPress& key) | |||
| } | |||
| else if (key.isKeyCode (KeyPress::returnKey) && buttons.size() == 1) | |||
| { | |||
| ((AlertWindowTextButton*) buttons.getFirst())->triggerClick(); | |||
| ((TextButton*) buttons.getFirst())->triggerClick(); | |||
| return true; | |||
| } | |||
| @@ -42,6 +42,7 @@ BEGIN_JUCE_NAMESPACE | |||
| //============================================================================== | |||
| PropertySet::PropertySet (const bool ignoreCaseOfKeyNames) throw() | |||
| : properties (ignoreCaseOfKeyNames), | |||
| fallbackProperties (0), | |||
| ignoreCaseOfKeys (ignoreCaseOfKeyNames) | |||
| { | |||
| } | |||
| @@ -51,11 +52,17 @@ PropertySet::~PropertySet() | |||
| } | |||
| const String PropertySet::getValue (const String& keyName, | |||
| const String& defaultReturnValue) const throw() | |||
| const String& defaultValue) const throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| return properties.getValue (keyName, defaultReturnValue); | |||
| const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); | |||
| if (index >= 0) | |||
| return properties.getAllValues() [index]; | |||
| return fallbackProperties != 0 ? fallbackProperties->getValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| int PropertySet::getIntValue (const String& keyName, | |||
| @@ -67,7 +74,8 @@ int PropertySet::getIntValue (const String& keyName, | |||
| if (index >= 0) | |||
| return properties.getAllValues() [index].getIntValue(); | |||
| return defaultValue; | |||
| return fallbackProperties != 0 ? fallbackProperties->getIntValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| double PropertySet::getDoubleValue (const String& keyName, | |||
| @@ -79,7 +87,8 @@ double PropertySet::getDoubleValue (const String& keyName, | |||
| if (index >= 0) | |||
| return properties.getAllValues()[index].getDoubleValue(); | |||
| return defaultValue; | |||
| return fallbackProperties != 0 ? fallbackProperties->getDoubleValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| bool PropertySet::getBoolValue (const String& keyName, | |||
| @@ -91,7 +100,8 @@ bool PropertySet::getBoolValue (const String& keyName, | |||
| if (index >= 0) | |||
| return properties.getAllValues() [index].getIntValue() != 0; | |||
| return defaultValue; | |||
| return fallbackProperties != 0 ? fallbackProperties->getBoolValue (keyName, defaultValue) | |||
| : defaultValue; | |||
| } | |||
| XmlElement* PropertySet::getXmlValue (const String& keyName) const | |||
| @@ -162,6 +172,12 @@ bool PropertySet::containsKey (const String& keyName) const throw() | |||
| return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys); | |||
| } | |||
| void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) throw() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| fallbackProperties = fallbackProperties_; | |||
| } | |||
| void PropertySet::propertyChanged() | |||
| { | |||
| } | |||
| @@ -63,6 +63,10 @@ public: | |||
| //============================================================================== | |||
| /** Returns one of the properties as a string. | |||
| If the value isn't found in this set, then this will look for it in a fallback | |||
| property set (if you've specified one with the setFallbackPropertySet() method), | |||
| and if it can't find one there, it'll return the default value passed-in. | |||
| @param keyName the name of the property to retrieve | |||
| @param defaultReturnValue a value to return if the named property doesn't actually exist | |||
| */ | |||
| @@ -71,6 +75,10 @@ public: | |||
| /** Returns one of the properties as an integer. | |||
| If the value isn't found in this set, then this will look for it in a fallback | |||
| property set (if you've specified one with the setFallbackPropertySet() method), | |||
| and if it can't find one there, it'll return the default value passed-in. | |||
| @param keyName the name of the property to retrieve | |||
| @param defaultReturnValue a value to return if the named property doesn't actually exist | |||
| */ | |||
| @@ -79,6 +87,10 @@ public: | |||
| /** Returns one of the properties as an double. | |||
| If the value isn't found in this set, then this will look for it in a fallback | |||
| property set (if you've specified one with the setFallbackPropertySet() method), | |||
| and if it can't find one there, it'll return the default value passed-in. | |||
| @param keyName the name of the property to retrieve | |||
| @param defaultReturnValue a value to return if the named property doesn't actually exist | |||
| */ | |||
| @@ -90,6 +102,10 @@ public: | |||
| The result will be true if the string found for this key name can be parsed as a non-zero | |||
| integer. | |||
| If the value isn't found in this set, then this will look for it in a fallback | |||
| property set (if you've specified one with the setFallbackPropertySet() method), | |||
| and if it can't find one there, it'll return the default value passed-in. | |||
| @param keyName the name of the property to retrieve | |||
| @param defaultReturnValue a value to return if the named property doesn't actually exist | |||
| */ | |||
| @@ -101,6 +117,10 @@ public: | |||
| The result will a new XMLElement object that the caller must delete. If may return 0 if the | |||
| key isn't found, or if the entry contains an string that isn't valid XML. | |||
| If the value isn't found in this set, then this will look for it in a fallback | |||
| property set (if you've specified one with the setFallbackPropertySet() method), | |||
| and if it can't find one there, it'll return the default value passed-in. | |||
| @param keyName the name of the property to retrieve | |||
| */ | |||
| XmlElement* getXmlValue (const String& keyName) const; | |||
| @@ -167,6 +187,25 @@ public: | |||
| /** Returns the lock used when reading or writing to this set */ | |||
| const CriticalSection& getLock() const throw() { return lock; } | |||
| //============================================================================== | |||
| /** Sets up a second PopertySet that will be used to look up any values that aren't | |||
| set in this one. | |||
| If you set this up to be a pointer to a second property set, then whenever one | |||
| of the getValue() methods fails to find an entry in this set, it will look up that | |||
| value in the fallback set, and if it finds it, it will return that. | |||
| Make sure that you don't delete the fallback set while it's still being used by | |||
| another set! To remove the fallback set, just call this method with a null pointer. | |||
| @see getFallbackPropertySet | |||
| */ | |||
| void setFallbackPropertySet (PropertySet* fallbackProperties) throw(); | |||
| /** Returns the fallback property set. | |||
| @see setFallbackPropertySet | |||
| */ | |||
| PropertySet* getFallbackPropertySet() const throw() { return fallbackProperties; } | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| @@ -182,8 +221,9 @@ protected: | |||
| private: | |||
| //============================================================================== | |||
| StringPairArray properties; | |||
| bool ignoreCaseOfKeys; | |||
| PropertySet* fallbackProperties; | |||
| CriticalSection lock; | |||
| bool ignoreCaseOfKeys; | |||
| PropertySet (const PropertySet&); | |||
| const PropertySet& operator= (const PropertySet&); | |||