diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 819184e9a..a232ec988 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -345,7 +345,7 @@ namespace FloatVectorHelpers Range result (Mode::min (mn), Mode::max (mx)); - num &= 3; + num &= (Mode::numParallel - 1); src += Mode::numParallel; for (int i = 0; i < num; ++i) diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 501fedf43..86f2e8199 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -201,9 +201,10 @@ void AudioProcessor::updateHostDisplay() l->audioProcessorChanged (this); } -String AudioProcessor::getParameterLabel (int) const { return String(); } -bool AudioProcessor::isParameterAutomatable (int) const { return true; } -bool AudioProcessor::isMetaParameter (int) const { return false; } +String AudioProcessor::getParameterLabel (int) const { return String(); } +bool AudioProcessor::isParameterOrientationInverted (int) const { return false; } +bool AudioProcessor::isParameterAutomatable (int) const { return true; } +bool AudioProcessor::isMetaParameter (int) const { return false; } void AudioProcessor::suspendProcessing (const bool shouldBeSuspended) { diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h index 7ae9dd418..92542b9b2 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -431,6 +431,11 @@ public: */ virtual String getParameterLabel (int index) const; + /** This can be overridden to tell the host that particular parameters operate in the + reverse direction. (Not all plugin formats or hosts will actually use this information). + */ + virtual bool isParameterOrientationInverted (int index) const; + /** The host will call this method to change the value of one of the filter's parameters. The host may call this at any time, including during the audio processing @@ -459,16 +464,13 @@ public: void setParameterNotifyingHost (int parameterIndex, float newValue); /** Returns true if the host can automate this parameter. - By default, this returns true for all parameters. */ virtual bool isParameterAutomatable (int parameterIndex) const; /** Should return true if this parameter is a "meta" parameter. - A meta-parameter is a parameter that changes other params. It is used by some hosts (e.g. AudioUnit hosts). - By default this returns false. */ virtual bool isMetaParameter (int parameterIndex) const; diff --git a/source/modules/juce_core/containers/juce_DynamicObject.cpp b/source/modules/juce_core/containers/juce_DynamicObject.cpp index 739f71589..38f5c5f08 100644 --- a/source/modules/juce_core/containers/juce_DynamicObject.cpp +++ b/source/modules/juce_core/containers/juce_DynamicObject.cpp @@ -85,16 +85,9 @@ void DynamicObject::clear() void DynamicObject::cloneAllProperties() { - for (LinkedListPointer* i = &(properties.values);;) - { - if (NamedValueSet::NamedValue* const v = i->get()) - { - v->value = v->value.clone(); - i = &(v->nextListItem); - } - else - break; - } + for (int i = properties.size(); --i >= 0;) + if (var* v = properties.getVarPointerAt (i)) + *v = v->clone(); } DynamicObject::Ptr DynamicObject::clone() @@ -110,32 +103,27 @@ void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const if (! allOnOneLine) out << newLine; - for (LinkedListPointer* i = &(properties.values);;) + const int numValues = properties.size(); + + for (int i = 0; i < numValues; ++i) { - if (NamedValueSet::NamedValue* const v = i->get()) + if (! allOnOneLine) + JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); + + out << '"'; + JSONFormatter::writeString (out, properties.getName (i)); + out << "\": "; + JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine); + + if (i < numValues - 1) { - if (! allOnOneLine) - JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); - - out << '"'; - JSONFormatter::writeString (out, v->name); - out << "\": "; - JSONFormatter::write (out, v->value, indentLevel + JSONFormatter::indentSize, allOnOneLine); - - if (v->nextListItem.get() != nullptr) - { - if (allOnOneLine) - out << ", "; - else - out << ',' << newLine; - } - else if (! allOnOneLine) - out << newLine; - - i = &(v->nextListItem); + if (allOnOneLine) + out << ", "; + else + out << ',' << newLine; } - else - break; + else if (! allOnOneLine) + out << newLine; } if (! allOnOneLine) diff --git a/source/modules/juce_core/containers/juce_NamedValueSet.cpp b/source/modules/juce_core/containers/juce_NamedValueSet.cpp index 0d866b1b8..c4e198d0f 100644 --- a/source/modules/juce_core/containers/juce_NamedValueSet.cpp +++ b/source/modules/juce_core/containers/juce_NamedValueSet.cpp @@ -26,53 +26,37 @@ ============================================================================== */ -NamedValueSet::NamedValue::NamedValue() noexcept +struct NamedValueSet::NamedValue { -} - -inline NamedValueSet::NamedValue::NamedValue (Identifier n, const var& v) - : name (n), value (v) -{ -} - -NamedValueSet::NamedValue::NamedValue (const NamedValue& other) - : name (other.name), value (other.value) -{ -} - -NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (const NamedValueSet::NamedValue& other) -{ - name = other.name; - value = other.value; - return *this; -} + NamedValue() noexcept {} + NamedValue (Identifier n, const var& v) : name (n), value (v) {} + NamedValue (const NamedValue& other) : name (other.name), value (other.value) {} + + #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + NamedValue (NamedValue&& other) noexcept + : name (static_cast (other.name)), + value (static_cast (other.value)) + { + } -#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS -NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept - : nextListItem (static_cast&&> (other.nextListItem)), - name (static_cast (other.name)), - value (static_cast (other.value)) -{ -} + NamedValue (Identifier n, var&& v) : name (n), value (static_cast (v)) + { + } -inline NamedValueSet::NamedValue::NamedValue (Identifier n, var&& v) - : name (n), value (static_cast (v)) -{ -} + NamedValue& operator= (NamedValue&& other) noexcept + { + name = static_cast (other.name); + value = static_cast (other.value); + return *this; + } + #endif -NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept -{ - nextListItem = static_cast&&> (other.nextListItem); - name = static_cast (other.name); - value = static_cast (other.value); - return *this; -} -#endif + bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; } + bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); } -bool NamedValueSet::NamedValue::operator== (const NamedValueSet::NamedValue& other) const noexcept -{ - return name == other.name && value == other.value; -} + Identifier name; + var value; +}; //============================================================================== NamedValueSet::NamedValueSet() noexcept @@ -80,20 +64,20 @@ NamedValueSet::NamedValueSet() noexcept } NamedValueSet::NamedValueSet (const NamedValueSet& other) + : values (other.values) { - values.addCopyOfList (other.values); } NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) { clear(); - values.addCopyOfList (other.values); + values = other.values; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept - : values (static_cast &&> (other.values)) + : values (static_cast &&> (other.values)) { } @@ -111,24 +95,12 @@ NamedValueSet::~NamedValueSet() void NamedValueSet::clear() { - values.deleteAll(); + values.clear(); } bool NamedValueSet::operator== (const NamedValueSet& other) const { - const NamedValue* i1 = values; - const NamedValue* i2 = other.values; - - while (i1 != nullptr && i2 != nullptr) - { - if (! (*i1 == *i2)) - return false; - - i1 = i1->nextListItem; - i2 = i2->nextListItem; - } - - return true; + return values == other.values; } bool NamedValueSet::operator!= (const NamedValueSet& other) const @@ -141,16 +113,15 @@ int NamedValueSet::size() const noexcept return values.size(); } -const var& NamedValueSet::operator[] (Identifier name) const +const var& NamedValueSet::operator[] (const Identifier& name) const { - for (NamedValue* i = values; i != nullptr; i = i->nextListItem) - if (i->name == name) - return i->value; + if (const var* v = getVarPointer (name)) + return *v; return var::null; } -var NamedValueSet::getWithDefault (Identifier name, const var& defaultReturnValue) const +var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const { if (const var* const v = getVarPointer (name)) return *v; @@ -158,9 +129,9 @@ var NamedValueSet::getWithDefault (Identifier name, const var& defaultReturnValu return defaultReturnValue; } -var* NamedValueSet::getVarPointer (Identifier name) const noexcept +var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept { - for (NamedValue* i = values; i != nullptr; i = i->nextListItem) + for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) if (i->name == name) return &(i->value); @@ -170,145 +141,121 @@ var* NamedValueSet::getVarPointer (Identifier name) const noexcept #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS bool NamedValueSet::set (Identifier name, var&& newValue) { - LinkedListPointer* i = &values; - - while (i->get() != nullptr) + if (var* const v = getVarPointer (name)) { - NamedValue* const v = i->get(); - - if (v->name == name) - { - if (v->value.equalsWithSameType (newValue)) - return false; - - v->value = static_cast (newValue); - return true; - } + if (v->equalsWithSameType (newValue)) + return false; - i = &(v->nextListItem); + *v = static_cast (newValue); + return true; } - i->insertNext (new NamedValue (name, static_cast (newValue))); + values.add (NamedValue (name, static_cast (newValue))); return true; } #endif bool NamedValueSet::set (Identifier name, const var& newValue) { - LinkedListPointer* i = &values; - - while (i->get() != nullptr) + if (var* const v = getVarPointer (name)) { - NamedValue* const v = i->get(); - - if (v->name == name) - { - if (v->value.equalsWithSameType (newValue)) - return false; - - v->value = newValue; - return true; - } + if (v->equalsWithSameType (newValue)) + return false; - i = &(v->nextListItem); + *v = newValue; + return true; } - i->insertNext (new NamedValue (name, newValue)); + values.add (NamedValue (name, newValue)); return true; } -bool NamedValueSet::contains (Identifier name) const +bool NamedValueSet::contains (const Identifier& name) const { return getVarPointer (name) != nullptr; } -int NamedValueSet::indexOf (Identifier name) const noexcept +int NamedValueSet::indexOf (const Identifier& name) const noexcept { - int index = 0; - - for (NamedValue* i = values; i != nullptr; i = i->nextListItem) - { - if (i->name == name) - return index; + const int numValues = values.size(); - ++index; - } + for (int i = 0; i < numValues; ++i) + if (values.getReference(i).name == name) + return i; return -1; } -bool NamedValueSet::remove (Identifier name) +bool NamedValueSet::remove (const Identifier& name) { - LinkedListPointer* i = &values; + const int numValues = values.size(); - for (;;) + for (int i = 0; i < numValues; ++i) { - NamedValue* const v = i->get(); - - if (v == nullptr) - break; - - if (v->name == name) + if (values.getReference(i).name == name) { - delete i->removeNext(); + values.remove (i); return true; } - - i = &(v->nextListItem); } return false; } -Identifier NamedValueSet::getName (const int index) const +Identifier NamedValueSet::getName (const int index) const noexcept { - const NamedValue* const v = values[index]; - jassert (v != nullptr); - return v->name; + if (isPositiveAndBelow (index, values.size())) + return values.getReference (index).name; + + jassertfalse; + return Identifier(); } -const var& NamedValueSet::getValueAt (const int index) const +const var& NamedValueSet::getValueAt (const int index) const noexcept { - const NamedValue* const v = values[index]; - jassert (v != nullptr); - return v->value; + if (isPositiveAndBelow (index, values.size())) + return values.getReference (index).value; + + jassertfalse; + return var::null; } -void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) +var* NamedValueSet::getVarPointerAt (int index) const noexcept { - clear(); - LinkedListPointer::Appender appender (values); + if (isPositiveAndBelow (index, values.size())) + return &(values.getReference (index).value); + + return nullptr; +} - const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator.. +void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) +{ + values.clearQuick(); - for (int i = 0; i < numAtts; ++i) + for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem) { - const String& name = xml.getAttributeName (i); - const String& value = xml.getAttributeValue (i); - - if (name.startsWith ("base64:")) + if (att->name.toString().startsWith ("base64:")) { MemoryBlock mb; - if (mb.fromBase64Encoding (value)) + if (mb.fromBase64Encoding (att->value)) { - appender.append (new NamedValue (name.substring (7), var (mb))); + values.add (NamedValue (att->name.toString().substring (7), var (mb))); continue; } } - appender.append (new NamedValue (name, var (value))); + values.add (NamedValue (att->name, var (att->value))); } } void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const { - for (NamedValue* i = values; i != nullptr; i = i->nextListItem) + for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) { if (const MemoryBlock* mb = i->value.getBinaryData()) { - xml.setAttribute ("base64:" + i->name.toString(), - mb->toBase64Encoding()); + xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding()); } else { diff --git a/source/modules/juce_core/containers/juce_NamedValueSet.h b/source/modules/juce_core/containers/juce_NamedValueSet.h index 1ecc7948e..021632630 100644 --- a/source/modules/juce_core/containers/juce_NamedValueSet.h +++ b/source/modules/juce_core/containers/juce_NamedValueSet.h @@ -67,12 +67,12 @@ public: If the name isn't found, this will return a void variant. @see getProperty */ - const var& operator[] (Identifier name) const; + const var& operator[] (const Identifier& name) const; /** Tries to return the named value, but if no such value is found, this will instead return the supplied default value. */ - var getWithDefault (Identifier name, const var& defaultReturnValue) const; + var getWithDefault (const Identifier& name, const var& defaultReturnValue) const; /** Changes or adds a named value. @returns true if a value was changed or added; false if the @@ -89,39 +89,43 @@ public: #endif /** Returns true if the set contains an item with the specified name. */ - bool contains (Identifier name) const; + bool contains (const Identifier& name) const; /** Removes a value from the set. @returns true if a value was removed; false if there was no value with the name that was given. */ - bool remove (Identifier name); + bool remove (const Identifier& name); /** Returns the name of the value at a given index. The index must be between 0 and size() - 1. */ - Identifier getName (int index) const; + Identifier getName (int index) const noexcept; + + /** Returns a pointer to the var that holds a named value, or null if there is + no value with this name. + + Do not use this method unless you really need access to the internal var object + for some reason - for normal reading and writing always prefer operator[]() and set(). + */ + var* getVarPointer (const Identifier& name) const noexcept; /** Returns the value of the item at a given index. The index must be between 0 and size() - 1. */ - const var& getValueAt (int index) const; + const var& getValueAt (int index) const noexcept; + + /** Returns the value of the item at a given index. + The index must be between 0 and size() - 1, or this will return a nullptr + */ + var* getVarPointerAt (int index) const noexcept; /** Returns the index of the given name, or -1 if it's not found. */ - int indexOf (Identifier name) const noexcept; + int indexOf (const Identifier& name) const noexcept; /** Removes all values. */ void clear(); - //============================================================================== - /** Returns a pointer to the var that holds a named value, or null if there is - no value with this name. - - Do not use this method unless you really need access to the internal var object - for some reason - for normal reading and writing always prefer operator[]() and set(). - */ - var* getVarPointer (Identifier name) const noexcept; - //============================================================================== /** Sets properties to the values of all of an XML element's attributes. */ void setFromXmlAttributes (const XmlElement& xml); @@ -133,32 +137,8 @@ public: private: //============================================================================== - class NamedValue - { - public: - NamedValue() noexcept; - NamedValue (const NamedValue&); - NamedValue (Identifier, const var&); - NamedValue& operator= (const NamedValue&); - #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - NamedValue (NamedValue&&) noexcept; - NamedValue (Identifier, var&&); - NamedValue& operator= (NamedValue&&) noexcept; - #endif - bool operator== (const NamedValue&) const noexcept; - - LinkedListPointer nextListItem; - Identifier name; - var value; - - private: - JUCE_LEAK_DETECTOR (NamedValue) - }; - - friend class LinkedListPointer; - LinkedListPointer values; - - friend class DynamicObject; + struct NamedValue; + Array values; }; diff --git a/source/modules/juce_core/javascript/juce_JSON.cpp b/source/modules/juce_core/javascript/juce_JSON.cpp index ebf2e470f..5a0f7f7c0 100644 --- a/source/modules/juce_core/javascript/juce_JSON.cpp +++ b/source/modules/juce_core/javascript/juce_JSON.cpp @@ -243,9 +243,9 @@ private: if (r.failed()) return r; - const String propertyName (propertyNameVar.toString()); + const Identifier propertyName (propertyNameVar.toString()); - if (propertyName.isNotEmpty()) + if (propertyName.isValid()) { t = t.findEndOfWhitespace(); oldT = t; diff --git a/source/modules/juce_core/native/juce_mac_SystemStats.mm b/source/modules/juce_core/native/juce_mac_SystemStats.mm index 01e333dca..2d49ae698 100644 --- a/source/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/source/modules/juce_core/native/juce_mac_SystemStats.mm @@ -241,35 +241,41 @@ String SystemStats::getDisplayLanguage() } //============================================================================== -class HiResCounterHandler +/* NB: these are kept outside the HiResCounterInfo struct and initialised to 1 to avoid + division-by-zero errors if some other static constructor calls us before this file's + static constructors have had a chance to fill them in correctly.. +*/ +static uint64 hiResCounterNumerator = 0, hiResCounterDenominator = 1; + +class HiResCounterInfo { public: - HiResCounterHandler() + HiResCounterInfo() { mach_timebase_info_data_t timebase; (void) mach_timebase_info (&timebase); if (timebase.numer % 1000000 == 0) { - numerator = timebase.numer / 1000000; - denominator = timebase.denom; + hiResCounterNumerator = timebase.numer / 1000000; + hiResCounterDenominator = timebase.denom; } else { - numerator = timebase.numer; - denominator = timebase.denom * (uint64) 1000000; + hiResCounterNumerator = timebase.numer; + hiResCounterDenominator = timebase.denom * (uint64) 1000000; } highResTimerFrequency = (timebase.denom * (uint64) 1000000000) / timebase.numer; - highResTimerToMillisecRatio = numerator / (double) denominator; + highResTimerToMillisecRatio = hiResCounterNumerator / (double) hiResCounterDenominator; } - inline uint32 millisecondsSinceStartup() const noexcept + uint32 millisecondsSinceStartup() const noexcept { - return (uint32) ((mach_absolute_time() * numerator) / denominator); + return (uint32) ((mach_absolute_time() * hiResCounterNumerator) / hiResCounterDenominator); } - inline double getMillisecondCounterHiRes() const noexcept + double getMillisecondCounterHiRes() const noexcept { return mach_absolute_time() * highResTimerToMillisecRatio; } @@ -277,15 +283,14 @@ public: int64 highResTimerFrequency; private: - uint64 numerator, denominator; double highResTimerToMillisecRatio; }; -static HiResCounterHandler hiResCounterHandler; +static HiResCounterInfo hiResCounterInfo; -uint32 juce_millisecondsSinceStartup() noexcept { return hiResCounterHandler.millisecondsSinceStartup(); } -double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); } -int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.highResTimerFrequency; } +uint32 juce_millisecondsSinceStartup() noexcept { return hiResCounterInfo.millisecondsSinceStartup(); } +double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterInfo.getMillisecondCounterHiRes(); } +int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterInfo.highResTimerFrequency; } int64 Time::getHighResolutionTicks() noexcept { return (int64) mach_absolute_time(); } bool Time::setSystemTimeToThisTime() const diff --git a/source/modules/juce_core/text/juce_Identifier.cpp b/source/modules/juce_core/text/juce_Identifier.cpp index c52ad5f24..bac4dd58a 100644 --- a/source/modules/juce_core/text/juce_Identifier.cpp +++ b/source/modules/juce_core/text/juce_Identifier.cpp @@ -26,21 +26,10 @@ ============================================================================== */ -StringPool& Identifier::getPool() -{ - static StringPool pool; - return pool; -} +Identifier::Identifier() noexcept {} +Identifier::~Identifier() noexcept {} -Identifier::Identifier() noexcept - : name (nullptr) -{ -} - -Identifier::Identifier (const Identifier& other) noexcept - : name (other.name) -{ -} +Identifier::Identifier (const Identifier& other) noexcept : name (other.name) {} Identifier& Identifier::operator= (const Identifier other) noexcept { @@ -49,20 +38,27 @@ Identifier& Identifier::operator= (const Identifier other) noexcept } Identifier::Identifier (const String& nm) - : name (Identifier::getPool().getPooledString (nm)) + : name (StringPool::getGlobalPool().getPooledString (nm)) { + /* An Identifier string must be suitable for use as a script variable or XML + attribute, so it can only contain this limited set of characters.. */ + jassert (isValidIdentifier (toString())); } -Identifier::Identifier (const char* const nm) - : name (Identifier::getPool().getPooledString (nm)) +Identifier::Identifier (const char* nm) + : name (StringPool::getGlobalPool().getPooledString (nm)) { /* An Identifier string must be suitable for use as a script variable or XML attribute, so it can only contain this limited set of characters.. */ jassert (isValidIdentifier (toString())); } -Identifier::~Identifier() +Identifier::Identifier (String::CharPointerType start, String::CharPointerType end) + : name (StringPool::getGlobalPool().getPooledString (start, end)) { + /* An Identifier string must be suitable for use as a script variable or XML + attribute, so it can only contain this limited set of characters.. */ + jassert (isValidIdentifier (toString())); } Identifier Identifier::null; diff --git a/source/modules/juce_core/text/juce_Identifier.h b/source/modules/juce_core/text/juce_Identifier.h index 14a6d628f..f60eec8d4 100644 --- a/source/modules/juce_core/text/juce_Identifier.h +++ b/source/modules/juce_core/text/juce_Identifier.h @@ -34,9 +34,9 @@ /** Represents a string identifier, designed for accessing properties by name. - Identifier objects are very light and fast to copy, but slower to initialise - from a string, so it's much faster to keep a static identifier object to refer - to frequently-used names, rather than constructing them each time you need it. + Comparing two Identifier objects is very fast (an O(1) operation), but creating + them can be slower than just using a String directly, so the optimal way to use them + is to keep some static Identifier objects for the things you use often. @see NamedValueSet, ValueTree */ @@ -58,6 +58,12 @@ public: */ Identifier (const String& name); + /** Creates an identifier with a specified name. + Because this name may need to be used in contexts such as script variables or XML + tags, it must only contain ascii letters and digits, or the underscore character. + */ + Identifier (String::CharPointerType nameStart, String::CharPointerType nameEnd); + /** Creates a copy of another identifier. */ Identifier (const Identifier& other) noexcept; @@ -65,37 +71,37 @@ public: Identifier& operator= (const Identifier other) noexcept; /** Destructor */ - ~Identifier(); + ~Identifier() noexcept; /** Compares two identifiers. This is a very fast operation. */ - inline bool operator== (Identifier other) const noexcept { return name == other.name; } + inline bool operator== (Identifier other) const noexcept { return name.getCharPointer() == other.name.getCharPointer(); } /** Compares two identifiers. This is a very fast operation. */ - inline bool operator!= (Identifier other) const noexcept { return name != other.name; } + inline bool operator!= (Identifier other) const noexcept { return name.getCharPointer() != other.name.getCharPointer(); } /** Compares the identifier with a string. */ - inline bool operator== (StringRef other) const noexcept { return name.compare (other.text) == 0; } + inline bool operator== (StringRef other) const noexcept { return name == other; } /** Compares the identifier with a string. */ - inline bool operator!= (StringRef other) const noexcept { return name.compare (other.text) != 0; } + inline bool operator!= (StringRef other) const noexcept { return name != other; } /** Returns this identifier as a string. */ - String toString() const { return name; } + const String& toString() const noexcept { return name; } /** Returns this identifier's raw string pointer. */ - operator String::CharPointerType() const noexcept { return name; } + operator String::CharPointerType() const noexcept { return name.getCharPointer(); } /** Returns this identifier's raw string pointer. */ - String::CharPointerType getCharPointer() const noexcept { return name; } + String::CharPointerType getCharPointer() const noexcept { return name.getCharPointer(); } /** Returns this identifier as a StringRef. */ operator StringRef() const noexcept { return name; } /** Returns true if this Identifier is not null */ - bool isValid() const noexcept { return name.getAddress() != nullptr; } + bool isValid() const noexcept { return name.isNotEmpty(); } /** Returns true if this Identifier is null */ - bool isNull() const noexcept { return name.getAddress() == nullptr; } + bool isNull() const noexcept { return name.isEmpty(); } /** A null identifier. */ static Identifier null; @@ -106,12 +112,8 @@ public: */ static bool isValidIdentifier (const String& possibleIdentifier) noexcept; - private: - //============================================================================== - String::CharPointerType name; - - static StringPool& getPool(); + String name; }; diff --git a/source/modules/juce_core/text/juce_String.cpp b/source/modules/juce_core/text/juce_String.cpp index 66d27733c..81e234e63 100644 --- a/source/modules/juce_core/text/juce_String.cpp +++ b/source/modules/juce_core/text/juce_String.cpp @@ -180,6 +180,11 @@ public: release (bufferFromText (text)); } + static inline int getReferenceCount (const CharPointerType text) noexcept + { + return bufferFromText (text)->refCount.get() + 1; + } + //============================================================================== static CharPointerType makeUniqueWithByteSize (const CharPointerType text, size_t numBytes) { @@ -285,7 +290,7 @@ String& String::operator= (String&& other) noexcept } #endif -inline String::PreallocationBytes::PreallocationBytes (const size_t numBytes_) : numBytes (numBytes_) {} +inline String::PreallocationBytes::PreallocationBytes (const size_t num) noexcept : numBytes (num) {} String::String (const PreallocationBytes& preallocationSize) : text (StringHolder::createUninitialisedBytes (preallocationSize.numBytes + sizeof (CharPointerType::CharType))) @@ -297,6 +302,11 @@ void String::preallocateBytes (const size_t numBytesNeeded) text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); } +int String::getReferenceCount() const noexcept +{ + return StringHolder::getReferenceCount (text); +} + //============================================================================== String::String (const char* const t) : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t))) diff --git a/source/modules/juce_core/text/juce_String.h b/source/modules/juce_core/text/juce_String.h index 7b1c17bc8..1827d544d 100644 --- a/source/modules/juce_core/text/juce_String.h +++ b/source/modules/juce_core/text/juce_String.h @@ -1218,6 +1218,11 @@ public: String convertToPrecomposedUnicode() const; #endif + /** Returns the number of String objects which are currently sharing the same internal + data as this one. + */ + int getReferenceCount() const noexcept; + private: //============================================================================== CharPointerType text; @@ -1225,7 +1230,7 @@ private: //============================================================================== struct PreallocationBytes { - explicit PreallocationBytes (size_t); + explicit PreallocationBytes (size_t) noexcept; size_t numBytes; }; diff --git a/source/modules/juce_core/text/juce_StringPool.cpp b/source/modules/juce_core/text/juce_StringPool.cpp index 6841c47d8..f8fd5a786 100644 --- a/source/modules/juce_core/text/juce_StringPool.cpp +++ b/source/modules/juce_core/text/juce_StringPool.cpp @@ -26,88 +26,131 @@ ============================================================================== */ -StringPool::StringPool() noexcept {} -StringPool::~StringPool() {} +static const int minNumberOfStringsForGarbageCollection = 300; +static const uint32 garbageCollectionInterval = 30000; -namespace StringPoolHelpers + +StringPool::StringPool() noexcept : lastGarbageCollectionTime (0) {} +StringPool::~StringPool() {} + +struct StartEndString { - template - String::CharPointerType getPooledStringFromArray (Array& strings, - StringType newString, - const CriticalSection& lock) - { - const ScopedLock sl (lock); - int start = 0; - int end = strings.size(); + StartEndString (String::CharPointerType s, String::CharPointerType e) noexcept : start (s), end (e) {} + operator String() const { return String (start, end); } - for (;;) - { - if (start >= end) - { - jassert (start <= end); - strings.insert (start, newString); - return strings.getReference (start).getCharPointer(); - } + String::CharPointerType start, end; +}; + +static int compareStrings (const String& s1, const String& s2) noexcept { return s1.compare (s2); } +static int compareStrings (CharPointer_UTF8 s1, const String& s2) noexcept { return s1.compare (s2.getCharPointer()); } + +static int compareStrings (const StartEndString& string1, const String& string2) noexcept +{ + String::CharPointerType s1 (string1.start), s2 (string2.getCharPointer()); - const String& startString = strings.getReference (start); + for (;;) + { + const int c1 = s1 < string1.end ? (int) s1.getAndAdvance() : 0; + const int c2 = (int) s2.getAndAdvance(); + const int diff = c1 - c2; - if (startString == newString) - return startString.getCharPointer(); + if (diff != 0) return diff < 0 ? -1 : 1; + if (c1 == 0) break; + } - const int halfway = (start + end) >> 1; + return 0; +} - if (halfway == start) - { - if (startString.compare (newString) < 0) - ++start; +template +static String addPooledString (Array& strings, const NewStringType& newString) +{ + int start = 0; + int end = strings.size(); - strings.insert (start, newString); - return strings.getReference (start).getCharPointer(); - } + while (start < end) + { + const String& startString = strings.getReference (start); + const int startComp = compareStrings (newString, startString); - const int comp = strings.getReference (halfway).compare (newString); + if (startComp == 0) + return startString; - if (comp == 0) - return strings.getReference (halfway).getCharPointer(); + const int halfway = (start + end) / 2; - if (comp < 0) - start = halfway; - else - end = halfway; + if (halfway == start) + { + if (startComp > 0) + ++start; + + break; } + + const String& halfwayString = strings.getReference (halfway); + const int halfwayComp = compareStrings (newString, halfwayString); + + if (halfwayComp == 0) + return halfwayString; + + if (halfwayComp > 0) + start = halfway; + else + end = halfway; } + + strings.insert (start, newString); + return strings.getReference (start); } -String::CharPointerType StringPool::getPooledString (const String& s) +String StringPool::getPooledString (const char* const newString) { - if (s.isEmpty()) - return String().getCharPointer(); + if (newString == nullptr || *newString == 0) + return String(); - return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); + const ScopedLock sl (lock); + garbageCollectIfNeeded(); + return addPooledString (strings, CharPointer_UTF8 (newString)); } -String::CharPointerType StringPool::getPooledString (const char* const s) +String StringPool::getPooledString (String::CharPointerType start, String::CharPointerType end) { - if (s == nullptr || *s == 0) - return String().getCharPointer(); + if (start.isEmpty() || start == end) + return String(); - return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); + const ScopedLock sl (lock); + garbageCollectIfNeeded(); + return addPooledString (strings, StartEndString (start, end)); } -String::CharPointerType StringPool::getPooledString (const wchar_t* const s) +String StringPool::getPooledString (const String& newString) { - if (s == nullptr || *s == 0) - return String().getCharPointer(); + if (newString.isEmpty()) + return String(); - return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); + const ScopedLock sl (lock); + garbageCollectIfNeeded(); + return addPooledString (strings, newString); } -int StringPool::size() const noexcept +void StringPool::garbageCollectIfNeeded() { - return strings.size(); + if (strings.size() > minNumberOfStringsForGarbageCollection + && Time::getApproximateMillisecondCounter() > lastGarbageCollectionTime + garbageCollectionInterval) + garbageCollect(); +} + +void StringPool::garbageCollect() +{ + const ScopedLock sl (lock); + + for (int i = strings.size(); --i >= 0;) + if (strings.getReference(i).getReferenceCount() == 1) + strings.remove (i); + + lastGarbageCollectionTime = Time::getApproximateMillisecondCounter(); } -String::CharPointerType StringPool::operator[] (const int index) const noexcept +StringPool& StringPool::getGlobalPool() noexcept { - return strings [index].getCharPointer(); + static StringPool pool; + return pool; } diff --git a/source/modules/juce_core/text/juce_StringPool.h b/source/modules/juce_core/text/juce_StringPool.h index fc4dde327..0aae600ac 100644 --- a/source/modules/juce_core/text/juce_StringPool.h +++ b/source/modules/juce_core/text/juce_StringPool.h @@ -52,40 +52,37 @@ public: ~StringPool(); //============================================================================== - /** Returns a pointer to a copy of the string that is passed in. - - The pool will always return the same pointer when asked for a string that matches it. - The pool will own all the pointers that it returns, deleting them when the pool itself - is deleted. + /** Returns a pointer to a shared copy of the string that is passed in. + The pool will always return the same String object when asked for a string that matches it. */ - String::CharPointerType getPooledString (const String& original); + String getPooledString (const String& original); /** Returns a pointer to a copy of the string that is passed in. - - The pool will always return the same pointer when asked for a string that matches it. - The pool will own all the pointers that it returns, deleting them when the pool itself - is deleted. + The pool will always return the same String object when asked for a string that matches it. */ - String::CharPointerType getPooledString (const char* original); + String getPooledString (const char* original); /** Returns a pointer to a copy of the string that is passed in. - - The pool will always return the same pointer when asked for a string that matches it. - The pool will own all the pointers that it returns, deleting them when the pool itself - is deleted. + The pool will always return the same String object when asked for a string that matches it. */ - String::CharPointerType getPooledString (const wchar_t* original); + String getPooledString (String::CharPointerType start, String::CharPointerType end); //============================================================================== - /** Returns the number of strings in the pool. */ - int size() const noexcept; + /** Scans the pool, and removes any strings that are unreferenced. + You don't generally need to call this - it'll be called automatically when the pool grows + large enough to warrant it. + */ + void garbageCollect(); - /** Returns one of the strings in the pool, by index. */ - String::CharPointerType operator[] (int index) const noexcept; + /** Returns a shared global pool which is used for things like Identifiers, XML parsing. */ + static StringPool& getGlobalPool() noexcept; private: - Array strings; + Array strings; CriticalSection lock; + uint32 lastGarbageCollectionTime; + + void garbageCollectIfNeeded(); }; diff --git a/source/modules/juce_core/xml/juce_XmlDocument.cpp b/source/modules/juce_core/xml/juce_XmlDocument.cpp index 3ccebd0e8..2eab527a2 100644 --- a/source/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/source/modules/juce_core/xml/juce_XmlDocument.cpp @@ -408,7 +408,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) } } - node = new XmlElement (String (input, endOfToken)); + node = new XmlElement (input, endOfToken); input = endOfToken; LinkedListPointer::Appender attributeAppender (node->attributes); @@ -458,8 +458,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (nextChar == '"' || nextChar == '\'') { XmlElement::XmlAttributeNode* const newAtt - = new XmlElement::XmlAttributeNode (String (attNameStart, attNameEnd), - String::empty); + = new XmlElement::XmlAttributeNode (attNameStart, attNameEnd); readQuotedString (newAtt->value); attributeAppender.append (newAtt); diff --git a/source/modules/juce_core/xml/juce_XmlElement.cpp b/source/modules/juce_core/xml/juce_XmlElement.cpp index 886c98d12..469f382a4 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.cpp +++ b/source/modules/juce_core/xml/juce_XmlElement.cpp @@ -32,7 +32,7 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) n { } -XmlElement::XmlAttributeNode::XmlAttributeNode (const String& n, const String& v) noexcept +XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept : name (n), value (v) { #if JUCE_DEBUG @@ -42,15 +42,16 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const String& n, const String& v #endif } -bool XmlElement::XmlAttributeNode::hasName (StringRef nameToMatch) const noexcept +XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd) + : name (nameStart, nameEnd) { - return name.equalsIgnoreCase (nameToMatch); } //============================================================================== -XmlElement::XmlElement (const String& tag) noexcept - : tagName (tag) +static void sanityCheckTagName (const String& tag) { + (void) tag; + // the tag name mustn't be empty, or it'll look like a text element! jassert (tag.containsNonWhitespaceChars()) @@ -58,6 +59,36 @@ XmlElement::XmlElement (const String& tag) noexcept jassert (! tag.containsAnyOf (" <>/&(){}")); } +XmlElement::XmlElement (const String& tag) + : tagName (StringPool::getGlobalPool().getPooledString (tag)) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (const char* tag) + : tagName (StringPool::getGlobalPool().getPooledString (tag)) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (StringRef tag) + : tagName (StringPool::getGlobalPool().getPooledString (tag.text.getAddress())) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (const Identifier& tag) + : tagName (tag.toString()) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd) + : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd)) +{ + sanityCheckTagName (tagName); +} + XmlElement::XmlElement (int /*dummy*/) noexcept { } @@ -407,7 +438,7 @@ int XmlElement::getNumAttributes() const noexcept const String& XmlElement::getAttributeName (const int index) const noexcept { if (const XmlAttributeNode* const att = attributes [index]) - return att->name; + return att->name.toString(); return String::empty; } @@ -423,7 +454,7 @@ const String& XmlElement::getAttributeValue (const int index) const noexcept XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept { for (XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) + if (att->name == attributeName) return att; return nullptr; @@ -495,7 +526,7 @@ bool XmlElement::compareAttribute (StringRef attributeName, } //============================================================================== -void XmlElement::setAttribute (const String& attributeName, const String& value) +void XmlElement::setAttribute (const Identifier& attributeName, const String& value) { if (attributes == nullptr) { @@ -505,7 +536,7 @@ void XmlElement::setAttribute (const String& attributeName, const String& value) { for (XmlAttributeNode* att = attributes; ; att = att->nextListItem) { - if (att->hasName (attributeName)) + if (att->name == attributeName) { att->value = value; break; @@ -520,23 +551,23 @@ void XmlElement::setAttribute (const String& attributeName, const String& value) } } -void XmlElement::setAttribute (const String& attributeName, const int number) +void XmlElement::setAttribute (const Identifier& attributeName, const int number) { setAttribute (attributeName, String (number)); } -void XmlElement::setAttribute (const String& attributeName, const double number) +void XmlElement::setAttribute (const Identifier& attributeName, const double number) { setAttribute (attributeName, String (number, 20)); } -void XmlElement::removeAttribute (const String& attributeName) noexcept +void XmlElement::removeAttribute (const Identifier& attributeName) noexcept { for (LinkedListPointer* att = &attributes; att->get() != nullptr; att = &(att->get()->nextListItem)) { - if (att->get()->hasName (attributeName)) + if (att->get()->name == attributeName) { delete att->removeNext(); break; @@ -615,7 +646,7 @@ void XmlElement::prependChildElement (XmlElement* newNode) noexcept } } -XmlElement* XmlElement::createNewChildElement (const String& childTagName) +XmlElement* XmlElement::createNewChildElement (StringRef childTagName) { XmlElement* const newElement = new XmlElement (childTagName); addChildElement (newElement); diff --git a/source/modules/juce_core/xml/juce_XmlElement.h b/source/modules/juce_core/xml/juce_XmlElement.h index 8fc3800d2..4ab4d0886 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.h +++ b/source/modules/juce_core/xml/juce_XmlElement.h @@ -144,20 +144,32 @@ class JUCE_API XmlElement public: //============================================================================== /** Creates an XmlElement with this tag name. */ - explicit XmlElement (const String& tagName) noexcept; + explicit XmlElement (const String& tagName); + + /** Creates an XmlElement with this tag name. */ + explicit XmlElement (const char* tagName); + + /** Creates an XmlElement with this tag name. */ + explicit XmlElement (const Identifier& tagName); + + /** Creates an XmlElement with this tag name. */ + explicit XmlElement (StringRef tagName); + + /** Creates an XmlElement with this tag name. */ + XmlElement (String::CharPointerType tagNameBegin, String::CharPointerType tagNameEnd); /** Creates a (deep) copy of another element. */ - XmlElement (const XmlElement& other); + XmlElement (const XmlElement&); /** Creates a (deep) copy of another element. */ - XmlElement& operator= (const XmlElement& other); + XmlElement& operator= (const XmlElement&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - XmlElement (XmlElement&& other) noexcept; - XmlElement& operator= (XmlElement&& other) noexcept; + XmlElement (XmlElement&&) noexcept; + XmlElement& operator= (XmlElement&&) noexcept; #endif - /** Deleting an XmlElement will also delete all its child elements. */ + /** Deleting an XmlElement will also delete all of its child elements. */ ~XmlElement() noexcept; //============================================================================== @@ -303,13 +315,11 @@ public: bool hasAttribute (StringRef attributeName) const noexcept; /** Returns the value of a named attribute. - @param attributeName the name of the attribute to look up */ const String& getStringAttribute (StringRef attributeName) const noexcept; /** Returns the value of a named attribute. - @param attributeName the name of the attribute to look up @param defaultReturnValue a value to return if the element doesn't have an attribute with this name @@ -377,7 +387,7 @@ public: @param newValue the value to set it to @see removeAttribute */ - void setAttribute (const String& attributeName, const String& newValue); + void setAttribute (const Identifier& attributeName, const String& newValue); /** Adds a named attribute to the element, setting it to an integer value. @@ -391,7 +401,7 @@ public: @param attributeName the name of the attribute to set @param newValue the value to set it to */ - void setAttribute (const String& attributeName, int newValue); + void setAttribute (const Identifier& attributeName, int newValue); /** Adds a named attribute to the element, setting it to a floating-point value. @@ -405,14 +415,14 @@ public: @param attributeName the name of the attribute to set @param newValue the value to set it to */ - void setAttribute (const String& attributeName, double newValue); + void setAttribute (const Identifier& attributeName, double newValue); /** Removes a named attribute from the element. @param attributeName the name of the attribute to remove @see removeAllAttributes */ - void removeAttribute (const String& attributeName) noexcept; + void removeAttribute (const Identifier& attributeName) noexcept; /** Removes all attributes from this element. */ void removeAllAttributes() noexcept; @@ -555,7 +565,7 @@ public: XmlElement* newElement = myParentElement->createNewChildElement ("foobar"); @endcode */ - XmlElement* createNewChildElement (const String& tagName); + XmlElement* createNewChildElement (StringRef tagName); /** Replaces one of this element's children with another node. @@ -712,25 +722,26 @@ private: struct XmlAttributeNode { XmlAttributeNode (const XmlAttributeNode&) noexcept; - XmlAttributeNode (const String& name, const String& value) noexcept; + XmlAttributeNode (const Identifier&, const String&) noexcept; + XmlAttributeNode (String::CharPointerType, String::CharPointerType); LinkedListPointer nextListItem; - String name, value; - - bool hasName (StringRef) const noexcept; + Identifier name; + String value; private: - XmlAttributeNode& operator= (const XmlAttributeNode&); + XmlAttributeNode& operator= (const XmlAttributeNode&) JUCE_DELETED_FUNCTION; }; friend class XmlDocument; - friend class LinkedListPointer ; - friend class LinkedListPointer ; - friend class LinkedListPointer ::Appender; - - LinkedListPointer nextListItem; - LinkedListPointer firstChildElement; - LinkedListPointer attributes; + friend class LinkedListPointer; + friend class LinkedListPointer; + friend class LinkedListPointer::Appender; + friend class NamedValueSet; + + LinkedListPointer nextListItem; + LinkedListPointer firstChildElement; + LinkedListPointer attributes; String tagName; XmlElement (int) noexcept; @@ -740,6 +751,11 @@ private: void reorderChildElements (XmlElement**, int) noexcept; XmlAttributeNode* getAttribute (StringRef) const noexcept; + // Sigh.. L"" or _T("") string literals are problematic in general, and really inappropriate + // for XML tags. Use a UTF-8 encoded literal instead, or if you're really determined to use + // UTF-16, cast it to a String and use the other constructor. + XmlElement (const wchar_t*) JUCE_DELETED_FUNCTION; + JUCE_LEAK_DETECTOR (XmlElement) }; diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.cpp b/source/modules/juce_data_structures/values/juce_ValueTree.cpp index a9e3893b4..f77622498 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/source/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -409,7 +409,7 @@ public: XmlElement* createXml() const { - XmlElement* const xml = new XmlElement (type.toString()); + XmlElement* const xml = new XmlElement (type); properties.copyToXmlAttributes (*xml); // (NB: it's faster to add nodes to XML elements in reverse order) diff --git a/source/modules/juce_events/messages/juce_Initialisation.h b/source/modules/juce_events/messages/juce_Initialisation.h index 1b33fd7d8..51993bf13 100644 --- a/source/modules/juce_events/messages/juce_Initialisation.h +++ b/source/modules/juce_events/messages/juce_Initialisation.h @@ -55,13 +55,17 @@ JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI(); /** A utility object that helps you initialise and shutdown Juce correctly using an RAII pattern. - When an instance of this class is created, it calls initialiseJuce_GUI(), - and when it's deleted, it calls shutdownJuce_GUI(), which lets you easily - make sure that these functions are matched correctly. + When the first instance of this class is created, it calls initialiseJuce_GUI(), + and when the last instance is deleted, it calls shutdownJuce_GUI(), so that you + can easily be sure that as long as at least one instance of the class exists, the + library will be initialised. This class is particularly handy to use at the beginning of a console app's main() function, because it'll take care of shutting down whenever you return from the main() call. + + Be careful with your threading though - to be safe, you should always make sure + that these objects are created and deleted on the message thread. */ class JUCE_API ScopedJuceInitialiser_GUI { diff --git a/source/modules/juce_events/messages/juce_MessageManager.cpp b/source/modules/juce_events/messages/juce_MessageManager.cpp index 2f38a1c4c..9383d50b4 100644 --- a/source/modules/juce_events/messages/juce_MessageManager.cpp +++ b/source/modules/juce_events/messages/juce_MessageManager.cpp @@ -334,5 +334,7 @@ JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI() } } -ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { initialiseJuce_GUI(); } -ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { shutdownJuce_GUI(); } +static int numScopedInitInstances = 0; + +ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); } +ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); } diff --git a/source/modules/juce_gui_basics/widgets/juce_TreeView.h b/source/modules/juce_gui_basics/widgets/juce_TreeView.h index dc46c0411..587cd8e32 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TreeView.h +++ b/source/modules/juce_gui_basics/widgets/juce_TreeView.h @@ -744,7 +744,7 @@ public: void setIndentSize (int newIndentSize); /** Searches the tree for an item with the specified identifier. - The identifer string must have been created by calling TreeViewItem::getItemIdentifierString(). + The identifier string must have been created by calling TreeViewItem::getItemIdentifierString(). If no such item exists, this will return false. If the item is found, all of its items will be automatically opened. */