| @@ -131,56 +131,38 @@ void PluginListComponent::deleteKeyPressed (int lastRowSelected) | |||
| list.removeType (lastRowSelected); | |||
| } | |||
| void PluginListComponent::buttonClicked (Button* button) | |||
| void PluginListComponent::optionsMenuCallback (int result) | |||
| { | |||
| if (button == &optionsButton) | |||
| switch (result) | |||
| { | |||
| PopupMenu menu; | |||
| menu.addItem (1, TRANS("Clear list")); | |||
| menu.addItem (5, TRANS("Remove selected plugin from list"), listBox.getNumSelectedRows() > 0); | |||
| menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox.getNumSelectedRows() > 0); | |||
| menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); | |||
| menu.addSeparator(); | |||
| menu.addItem (2, TRANS("Sort alphabetically")); | |||
| menu.addItem (3, TRANS("Sort by category")); | |||
| menu.addItem (4, TRANS("Sort by manufacturer")); | |||
| menu.addSeparator(); | |||
| for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) | |||
| { | |||
| AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); | |||
| if (format->getDefaultLocationsToSearch().getNumPaths() > 0) | |||
| menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); | |||
| } | |||
| const int r = menu.showMenu (PopupMenu::Options().withTargetComponent (&optionsButton)); | |||
| if (r == 1) | |||
| { | |||
| case 1: | |||
| list.clear(); | |||
| } | |||
| else if (r == 2) | |||
| { | |||
| break; | |||
| case 2: | |||
| list.sort (KnownPluginList::sortAlphabetically); | |||
| } | |||
| else if (r == 3) | |||
| { | |||
| break; | |||
| case 3: | |||
| list.sort (KnownPluginList::sortByCategory); | |||
| } | |||
| else if (r == 4) | |||
| { | |||
| break; | |||
| case 4: | |||
| list.sort (KnownPluginList::sortByManufacturer); | |||
| } | |||
| else if (r == 5) | |||
| break; | |||
| case 5: | |||
| { | |||
| const SparseSet <int> selected (listBox.getSelectedRows()); | |||
| for (int i = list.getNumTypes(); --i >= 0;) | |||
| if (selected.contains (i)) | |||
| list.removeType (i); | |||
| break; | |||
| } | |||
| else if (r == 6) | |||
| case 6: | |||
| { | |||
| const PluginDescription* const desc = list.getType (listBox.getSelectedRow()); | |||
| @@ -189,22 +171,59 @@ void PluginListComponent::buttonClicked (Button* button) | |||
| if (File (desc->fileOrIdentifier).existsAsFile()) | |||
| File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); | |||
| } | |||
| break; | |||
| } | |||
| else if (r == 7) | |||
| { | |||
| case 7: | |||
| for (int i = list.getNumTypes(); --i >= 0;) | |||
| { | |||
| if (! AudioPluginFormatManager::getInstance()->doesPluginStillExist (*list.getType (i))) | |||
| { | |||
| list.removeType (i); | |||
| } | |||
| break; | |||
| default: | |||
| if (result != 0) | |||
| { | |||
| typeToScan = result - 10; | |||
| startTimer (1); | |||
| } | |||
| } | |||
| else if (r != 0) | |||
| break; | |||
| } | |||
| } | |||
| void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList) | |||
| { | |||
| if (pluginList != 0) | |||
| pluginList->optionsMenuCallback (result); | |||
| } | |||
| void PluginListComponent::buttonClicked (Button* button) | |||
| { | |||
| if (button == &optionsButton) | |||
| { | |||
| PopupMenu menu; | |||
| menu.addItem (1, TRANS("Clear list")); | |||
| menu.addItem (5, TRANS("Remove selected plugin from list"), listBox.getNumSelectedRows() > 0); | |||
| menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox.getNumSelectedRows() > 0); | |||
| menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); | |||
| menu.addSeparator(); | |||
| menu.addItem (2, TRANS("Sort alphabetically")); | |||
| menu.addItem (3, TRANS("Sort by category")); | |||
| menu.addItem (4, TRANS("Sort by manufacturer")); | |||
| menu.addSeparator(); | |||
| for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) | |||
| { | |||
| typeToScan = r - 10; | |||
| startTimer (1); | |||
| AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); | |||
| if (format->getDefaultLocationsToSearch().getNumPaths() > 0) | |||
| menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); | |||
| } | |||
| menu.showMenuAsync (PopupMenu::Options().withTargetComponent (&optionsButton), | |||
| ModalCallbackFunction::forComponent (optionsMenuStaticCallback, this)); | |||
| } | |||
| } | |||
| @@ -90,6 +90,8 @@ private: | |||
| int typeToScan; | |||
| void scanFor (AudioPluginFormat* format); | |||
| static void optionsMenuStaticCallback (int result, PluginListComponent*); | |||
| void optionsMenuCallback (int result); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListComponent); | |||
| }; | |||
| @@ -31,6 +31,15 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "juce_DynamicObject.h" | |||
| #include "../io/streams/juce_MemoryOutputStream.h" | |||
| enum VariantStreamMarkers | |||
| { | |||
| varMarker_Int = 1, | |||
| varMarker_BoolTrue = 2, | |||
| varMarker_BoolFalse = 3, | |||
| varMarker_Double = 4, | |||
| varMarker_String = 5, | |||
| varMarker_Int64 = 6 | |||
| }; | |||
| //============================================================================== | |||
| class var::VariantType | |||
| @@ -40,6 +49,7 @@ public: | |||
| virtual ~VariantType() {} | |||
| virtual int toInt (const ValueUnion&) const { return 0; } | |||
| virtual int64 toInt64 (const ValueUnion&) const { return 0; } | |||
| virtual double toDouble (const ValueUnion&) const { return 0; } | |||
| virtual const String toString (const ValueUnion&) const { return String::empty; } | |||
| virtual bool toBool (const ValueUnion&) const { return false; } | |||
| @@ -47,6 +57,7 @@ public: | |||
| virtual bool isVoid() const throw() { return false; } | |||
| virtual bool isInt() const throw() { return false; } | |||
| virtual bool isInt64() const throw() { return false; } | |||
| virtual bool isBool() const throw() { return false; } | |||
| virtual bool isDouble() const throw() { return false; } | |||
| virtual bool isString() const throw() { return false; } | |||
| @@ -79,6 +90,7 @@ public: | |||
| static const VariantType_Int instance; | |||
| int toInt (const ValueUnion& data) const { return data.intValue; }; | |||
| int64 toInt64 (const ValueUnion& data) const { return (int64) data.intValue; }; | |||
| double toDouble (const ValueUnion& data) const { return (double) data.intValue; } | |||
| const String toString (const ValueUnion& data) const { return String (data.intValue); } | |||
| bool toBool (const ValueUnion& data) const { return data.intValue != 0; } | |||
| @@ -92,11 +104,38 @@ public: | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| output.writeCompressedInt (5); | |||
| output.writeByte (1); | |||
| output.writeByte (varMarker_Int); | |||
| output.writeInt (data.intValue); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class var::VariantType_Int64 : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Int64() {} | |||
| static const VariantType_Int64 instance; | |||
| int toInt (const ValueUnion& data) const { return (int) data.int64Value; }; | |||
| int64 toInt64 (const ValueUnion& data) const { return data.int64Value; }; | |||
| double toDouble (const ValueUnion& data) const { return (double) data.int64Value; } | |||
| const String toString (const ValueUnion& data) const { return String (data.int64Value); } | |||
| bool toBool (const ValueUnion& data) const { return data.int64Value != 0; } | |||
| bool isInt64() const throw() { return true; } | |||
| bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const throw() | |||
| { | |||
| return otherType.toInt64 (otherData) == data.int64Value; | |||
| } | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| output.writeCompressedInt (9); | |||
| output.writeByte (varMarker_Int64); | |||
| output.writeInt64 (data.int64Value); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class var::VariantType_Double : public var::VariantType | |||
| { | |||
| @@ -105,6 +144,7 @@ public: | |||
| static const VariantType_Double instance; | |||
| int toInt (const ValueUnion& data) const { return (int) data.doubleValue; }; | |||
| int64 toInt64 (const ValueUnion& data) const { return (int64) data.doubleValue; }; | |||
| double toDouble (const ValueUnion& data) const { return data.doubleValue; } | |||
| const String toString (const ValueUnion& data) const { return String (data.doubleValue); } | |||
| bool toBool (const ValueUnion& data) const { return data.doubleValue != 0; } | |||
| @@ -118,7 +158,7 @@ public: | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| output.writeCompressedInt (9); | |||
| output.writeByte (4); | |||
| output.writeByte (varMarker_Double); | |||
| output.writeDouble (data.doubleValue); | |||
| } | |||
| }; | |||
| @@ -131,6 +171,7 @@ public: | |||
| static const VariantType_Bool instance; | |||
| int toInt (const ValueUnion& data) const { return data.boolValue ? 1 : 0; }; | |||
| int64 toInt64 (const ValueUnion& data) const { return data.boolValue ? 1 : 0; }; | |||
| double toDouble (const ValueUnion& data) const { return data.boolValue ? 1.0 : 0.0; } | |||
| const String toString (const ValueUnion& data) const { return String::charToString (data.boolValue ? '1' : '0'); } | |||
| bool toBool (const ValueUnion& data) const { return data.boolValue; } | |||
| @@ -144,7 +185,7 @@ public: | |||
| void writeToStream (const ValueUnion& data, OutputStream& output) const | |||
| { | |||
| output.writeCompressedInt (1); | |||
| output.writeByte (data.boolValue ? 2 : 3); | |||
| output.writeByte (data.boolValue ? varMarker_BoolTrue : varMarker_BoolFalse); | |||
| } | |||
| }; | |||
| @@ -159,6 +200,7 @@ public: | |||
| void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest.stringValue = new String (*source.stringValue); } | |||
| int toInt (const ValueUnion& data) const { return data.stringValue->getIntValue(); }; | |||
| int64 toInt64 (const ValueUnion& data) const { return data.stringValue->getLargeIntValue(); }; | |||
| double toDouble (const ValueUnion& data) const { return data.stringValue->getDoubleValue(); } | |||
| const String toString (const ValueUnion& data) const { return *data.stringValue; } | |||
| bool toBool (const ValueUnion& data) const { return data.stringValue->getIntValue() != 0 | |||
| @@ -175,7 +217,7 @@ public: | |||
| { | |||
| const int len = data.stringValue->getNumBytesAsUTF8() + 1; | |||
| output.writeCompressedInt (len + 1); | |||
| output.writeByte (5); | |||
| output.writeByte (varMarker_String); | |||
| HeapBlock<char> temp (len); | |||
| data.stringValue->copyToUTF8 (temp, len); | |||
| output.write (temp, len); | |||
| @@ -235,6 +277,7 @@ public: | |||
| //============================================================================== | |||
| const var::VariantType_Void var::VariantType_Void::instance; | |||
| const var::VariantType_Int var::VariantType_Int::instance; | |||
| const var::VariantType_Int64 var::VariantType_Int64::instance; | |||
| const var::VariantType_Bool var::VariantType_Bool::instance; | |||
| const var::VariantType_Double var::VariantType_Double::instance; | |||
| const var::VariantType_String var::VariantType_String::instance; | |||
| @@ -265,6 +308,11 @@ var::var (const int value_) throw() : type (&VariantType_Int::instance) | |||
| value.intValue = value_; | |||
| } | |||
| var::var (const int64 value_) throw() : type (&VariantType_Int64::instance) | |||
| { | |||
| value.intValue = value_; | |||
| } | |||
| var::var (const bool value_) throw() : type (&VariantType_Bool::instance) | |||
| { | |||
| value.boolValue = value_; | |||
| @@ -306,6 +354,7 @@ var::var (MethodFunction method_) throw() : type (&VariantType_Method::instance | |||
| //============================================================================== | |||
| bool var::isVoid() const throw() { return type->isVoid(); } | |||
| bool var::isInt() const throw() { return type->isInt(); } | |||
| bool var::isInt64() const throw() { return type->isInt64(); } | |||
| bool var::isBool() const throw() { return type->isBool(); } | |||
| bool var::isDouble() const throw() { return type->isDouble(); } | |||
| bool var::isString() const throw() { return type->isString(); } | |||
| @@ -313,6 +362,7 @@ bool var::isObject() const throw() { return type->isObject(); } | |||
| bool var::isMethod() const throw() { return type->isMethod(); } | |||
| var::operator int() const { return type->toInt (value); } | |||
| var::operator int64() const { return type->toInt64 (value); } | |||
| var::operator bool() const { return type->toBool (value); } | |||
| var::operator float() const { return (float) type->toDouble (value); } | |||
| var::operator double() const { return type->toDouble (value); } | |||
| @@ -329,6 +379,7 @@ void var::swapWith (var& other) throw() | |||
| var& var::operator= (const var& newValue) { type->cleanUp (value); type = newValue.type; type->createCopy (value, newValue.value); return *this; } | |||
| var& var::operator= (int newValue) { var v (newValue); swapWith (v); return *this; } | |||
| var& var::operator= (int64 newValue) { var v (newValue); swapWith (v); return *this; } | |||
| var& var::operator= (bool newValue) { var v (newValue); swapWith (v); return *this; } | |||
| var& var::operator= (double newValue) { var v (newValue); swapWith (v); return *this; } | |||
| var& var::operator= (const char* newValue) { var v (newValue); swapWith (v); return *this; } | |||
| @@ -367,11 +418,12 @@ const var var::readFromStream (InputStream& input) | |||
| { | |||
| switch (input.readByte()) | |||
| { | |||
| case 1: return var (input.readInt()); | |||
| case 2: return var (true); | |||
| case 3: return var (false); | |||
| case 4: return var (input.readDouble()); | |||
| case 5: | |||
| case varMarker_Int: return var (input.readInt()); | |||
| case varMarker_Int64: return var (input.readInt64()); | |||
| case varMarker_BoolTrue: return var (true); | |||
| case varMarker_BoolFalse: return var (false); | |||
| case varMarker_Double: return var (input.readDouble()); | |||
| case varMarker_String: | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.writeFromInputStream (input, numBytes - 1); | |||
| @@ -63,6 +63,7 @@ public: | |||
| var (const var& valueToCopy); | |||
| var (int value) throw(); | |||
| var (int64 value) throw(); | |||
| var (bool value) throw(); | |||
| var (double value) throw(); | |||
| var (const char* value); | |||
| @@ -73,6 +74,7 @@ public: | |||
| var& operator= (const var& valueToCopy); | |||
| var& operator= (int value); | |||
| var& operator= (int64 value); | |||
| var& operator= (bool value); | |||
| var& operator= (double value); | |||
| var& operator= (const char* value); | |||
| @@ -84,6 +86,7 @@ public: | |||
| void swapWith (var& other) throw(); | |||
| operator int() const; | |||
| operator int64() const; | |||
| operator bool() const; | |||
| operator float() const; | |||
| operator double() const; | |||
| @@ -93,6 +96,7 @@ public: | |||
| bool isVoid() const throw(); | |||
| bool isInt() const throw(); | |||
| bool isInt64() const throw(); | |||
| bool isBool() const throw(); | |||
| bool isDouble() const throw(); | |||
| bool isString() const throw(); | |||
| @@ -152,6 +156,8 @@ private: | |||
| friend class VariantType_Void; | |||
| class VariantType_Int; | |||
| friend class VariantType_Int; | |||
| class VariantType_Int64; | |||
| friend class VariantType_Int64; | |||
| class VariantType_Double; | |||
| friend class VariantType_Double; | |||
| class VariantType_Float; | |||
| @@ -168,6 +174,7 @@ private: | |||
| union ValueUnion | |||
| { | |||
| int intValue; | |||
| int64 int64Value; | |||
| bool boolValue; | |||
| double doubleValue; | |||
| String* stringValue; | |||
| @@ -117,10 +117,7 @@ const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) | |||
| bytesNeeded, &bytesRead, | |||
| &outputBufferSize, tempOut) == noErr) | |||
| { | |||
| result.preallocateStorage (bytesRead / sizeof (UniChar) + 2); | |||
| CharPointer_UTF32 dest (result.getCharPointer()); | |||
| dest.writeAll (CharPointer_UTF16 ((CharPointer_UTF16::CharType*) tempOut.getData())); | |||
| result = String (CharPointer_UTF16 ((CharPointer_UTF16::CharType*) tempOut.getData())); | |||
| } | |||
| DisposeUnicodeToTextInfo (&conversionInfo); | |||
| @@ -64,16 +64,12 @@ public: | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator== (const CharPointer_ASCII& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator!= (const CharPointer_ASCII& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| inline bool operator== (const CharPointer_ASCII& other) const throw() { return data == other.data; } | |||
| inline bool operator!= (const CharPointer_ASCII& other) const throw() { return data != other.data; } | |||
| inline bool operator<= (const CharPointer_ASCII& other) const throw() { return data <= other.data; } | |||
| inline bool operator< (const CharPointer_ASCII& other) const throw() { return data < other.data; } | |||
| inline bool operator>= (const CharPointer_ASCII& other) const throw() { return data >= other.data; } | |||
| inline bool operator> (const CharPointer_ASCII& other) const throw() { return data > other.data; } | |||
| /** Returns the address that this pointer is pointing to. */ | |||
| inline CharType* getAddress() const throw() { return data; } | |||
| @@ -171,6 +167,12 @@ public: | |||
| return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); | |||
| } | |||
| /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ | |||
| size_t lengthUpTo (const CharPointer_ASCII& end) const throw() | |||
| { | |||
| return CharacterFunctions::lengthUpTo (*this, end); | |||
| } | |||
| /** Returns the number of bytes that are used to represent this string. | |||
| This includes the terminating null character. | |||
| */ | |||
| @@ -65,16 +65,12 @@ public: | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator== (const CharPointer_UTF16& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator!= (const CharPointer_UTF16& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| inline bool operator== (const CharPointer_UTF16& other) const throw() { return data == other.data; } | |||
| inline bool operator!= (const CharPointer_UTF16& other) const throw() { return data != other.data; } | |||
| inline bool operator<= (const CharPointer_UTF16& other) const throw() { return data <= other.data; } | |||
| inline bool operator< (const CharPointer_UTF16& other) const throw() { return data < other.data; } | |||
| inline bool operator>= (const CharPointer_UTF16& other) const throw() { return data >= other.data; } | |||
| inline bool operator> (const CharPointer_UTF16& other) const throw() { return data > other.data; } | |||
| /** Returns the address that this pointer is pointing to. */ | |||
| inline CharType* getAddress() const throw() { return data; } | |||
| @@ -203,6 +199,12 @@ public: | |||
| return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); | |||
| } | |||
| /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ | |||
| size_t lengthUpTo (const CharPointer_UTF16& end) const throw() | |||
| { | |||
| return CharacterFunctions::lengthUpTo (*this, end); | |||
| } | |||
| /** Returns the number of bytes that are used to represent this string. | |||
| This includes the terminating null character. | |||
| */ | |||
| @@ -61,16 +61,12 @@ public: | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator== (const CharPointer_UTF32& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator!= (const CharPointer_UTF32& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| inline bool operator== (const CharPointer_UTF32& other) const throw() { return data == other.data; } | |||
| inline bool operator!= (const CharPointer_UTF32& other) const throw() { return data != other.data; } | |||
| inline bool operator<= (const CharPointer_UTF32& other) const throw() { return data <= other.data; } | |||
| inline bool operator< (const CharPointer_UTF32& other) const throw() { return data < other.data; } | |||
| inline bool operator>= (const CharPointer_UTF32& other) const throw() { return data >= other.data; } | |||
| inline bool operator> (const CharPointer_UTF32& other) const throw() { return data > other.data; } | |||
| /** Returns the address that this pointer is pointing to. */ | |||
| inline CharType* getAddress() const throw() { return data; } | |||
| @@ -175,6 +171,12 @@ public: | |||
| return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); | |||
| } | |||
| /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ | |||
| size_t lengthUpTo (const CharPointer_UTF32& end) const throw() | |||
| { | |||
| return CharacterFunctions::lengthUpTo (*this, end); | |||
| } | |||
| /** Returns the number of bytes that are used to represent this string. | |||
| This includes the terminating null character. | |||
| */ | |||
| @@ -60,16 +60,12 @@ public: | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator== (const CharPointer_UTF8& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| /** This is a pointer comparison, it doesn't compare the actual text. */ | |||
| inline bool operator!= (const CharPointer_UTF8& other) const throw() | |||
| { | |||
| return data == other.data; | |||
| } | |||
| inline bool operator== (const CharPointer_UTF8& other) const throw() { return data == other.data; } | |||
| inline bool operator!= (const CharPointer_UTF8& other) const throw() { return data != other.data; } | |||
| inline bool operator<= (const CharPointer_UTF8& other) const throw() { return data <= other.data; } | |||
| inline bool operator< (const CharPointer_UTF8& other) const throw() { return data < other.data; } | |||
| inline bool operator>= (const CharPointer_UTF8& other) const throw() { return data >= other.data; } | |||
| inline bool operator> (const CharPointer_UTF8& other) const throw() { return data > other.data; } | |||
| /** Returns the address that this pointer is pointing to. */ | |||
| inline CharType* getAddress() const throw() { return data; } | |||
| @@ -243,6 +239,12 @@ public: | |||
| return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); | |||
| } | |||
| /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ | |||
| size_t lengthUpTo (const CharPointer_UTF8& end) const throw() | |||
| { | |||
| return CharacterFunctions::lengthUpTo (*this, end); | |||
| } | |||
| /** Returns the number of bytes that are used to represent this string. | |||
| This includes the terminating null character. | |||
| */ | |||
| @@ -272,17 +272,26 @@ public: | |||
| //============================================================================== | |||
| template <typename CharPointerType> | |||
| static size_t lengthUpTo (const CharPointerType& text, const size_t maxCharsToCount) throw() | |||
| static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) throw() | |||
| { | |||
| size_t len = 0; | |||
| CharPointerType t (text); | |||
| while (len < maxCharsToCount && t.getAndAdvance() != 0) | |||
| while (len < maxCharsToCount && text.getAndAdvance() != 0) | |||
| ++len; | |||
| return len; | |||
| } | |||
| template <typename CharPointerType> | |||
| static size_t lengthUpTo (CharPointerType start, const CharPointerType& end) throw() | |||
| { | |||
| size_t len = 0; | |||
| while (start < end && start.getAndAdvance() != 0) | |||
| ++len; | |||
| return len; | |||
| } | |||
| template <typename DestCharPointerType, typename SrcCharPointerType> | |||
| static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) throw() | |||
| @@ -100,6 +100,21 @@ public: | |||
| return dest; | |||
| } | |||
| template <class CharPointer> | |||
| static const CharPointerType createFromCharPointer (const CharPointer& start, const CharPointer& end) | |||
| { | |||
| if (start.getAddress() == 0 || start.isEmpty()) | |||
| return getEmpty(); | |||
| size_t numChars = start.lengthUpTo (end); | |||
| if (numChars == 0) | |||
| return getEmpty(); | |||
| const CharPointerType dest (createUninitialised (numChars)); | |||
| CharPointerType (dest).writeWithCharLimit (start, (int) (numChars + 1)); | |||
| return dest; | |||
| } | |||
| static CharPointerType createFromFixedLength (const juce_wchar* const src, const size_t numChars) | |||
| { | |||
| CharPointerType dest (createUninitialised (numChars)); | |||
| @@ -325,6 +340,11 @@ String::String (const CharPointer_UTF32& t, const size_t maxChars) | |||
| { | |||
| } | |||
| String::String (const CharPointer_UTF32& start, const CharPointer_UTF32& end) | |||
| : text (StringHolder::createFromCharPointer (start, end)) | |||
| { | |||
| } | |||
| String::String (const CharPointer_ASCII& t) | |||
| : text (StringHolder::createFromCharPointer (t)) | |||
| { | |||
| @@ -347,8 +367,9 @@ String::String (const wchar_t* const t, size_t maxChars) | |||
| const String String::charToString (const juce_wchar character) | |||
| { | |||
| String result (Preallocation (1)); | |||
| result.text[0] = character; | |||
| result.text[1] = 0; | |||
| CharPointerType t (result.text); | |||
| t.write (character); | |||
| t.writeNull(); | |||
| return result; | |||
| } | |||
| @@ -540,22 +561,22 @@ const juce_wchar String::operator[] (int index) const throw() | |||
| int String::hashCode() const throw() | |||
| { | |||
| const juce_wchar* t = text; | |||
| CharPointerType t (text); | |||
| int result = 0; | |||
| while (*t != (juce_wchar) 0) | |||
| result = 31 * result + *t++; | |||
| while (! t.isEmpty()) | |||
| result = 31 * result + t.getAndAdvance(); | |||
| return result; | |||
| } | |||
| int64 String::hashCode64() const throw() | |||
| { | |||
| const juce_wchar* t = text; | |||
| CharPointerType t (text); | |||
| int64 result = 0; | |||
| while (*t != (juce_wchar) 0) | |||
| result = 101 * result + *t++; | |||
| while (! t.isEmpty()) | |||
| result = 101 * result + t.getAndAdvance(); | |||
| return result; | |||
| } | |||
| @@ -703,7 +724,6 @@ void String::append (const String& textToAppend, size_t maxCharsToTake) | |||
| String& String::operator+= (const juce_wchar* const t) | |||
| { | |||
| appendCharPointer (CharPointer_UTF32 (t)); | |||
| return *this; | |||
| } | |||
| @@ -884,16 +904,7 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const NewLine&) | |||
| //============================================================================== | |||
| int String::indexOfChar (const juce_wchar character) const throw() | |||
| { | |||
| const juce_wchar* t = text; | |||
| for (;;) | |||
| { | |||
| if (*t == character) | |||
| return (int) (t - text); | |||
| if (*t++ == 0) | |||
| return -1; | |||
| } | |||
| return text.indexOf (character); | |||
| } | |||
| int String::lastIndexOfChar (const juce_wchar character) const throw() | |||
| @@ -913,21 +924,8 @@ int String::indexOf (const String& t) const throw() | |||
| int String::indexOfChar (const int startIndex, | |||
| const juce_wchar character) const throw() | |||
| { | |||
| if (startIndex > 0 && startIndex >= length()) | |||
| return -1; | |||
| const juce_wchar* t = text + jmax (0, startIndex); | |||
| for (;;) | |||
| { | |||
| if (*t == character) | |||
| return (int) (t - text); | |||
| if (*t == 0) | |||
| return -1; | |||
| ++t; | |||
| } | |||
| const int i = (text + startIndex).indexOf (character); | |||
| return i < 0 ? -1 : (i + startIndex); | |||
| } | |||
| int String::indexOfAnyOf (const String& charactersToLookFor, | |||
| @@ -1058,18 +1056,7 @@ bool String::contains (const String& other) const throw() | |||
| bool String::containsChar (const juce_wchar character) const throw() | |||
| { | |||
| const juce_wchar* t = text; | |||
| for (;;) | |||
| { | |||
| if (*t == 0) | |||
| return false; | |||
| if (*t == character) | |||
| return true; | |||
| ++t; | |||
| } | |||
| return text.indexOf (character) >= 0; | |||
| } | |||
| bool String::containsIgnoreCase (const String& t) const throw() | |||
| @@ -1210,15 +1197,11 @@ const String String::repeatedString (const String& stringToRepeat, int numberOfT | |||
| if (numberOfTimesToRepeat <= 0) | |||
| return String::empty; | |||
| const int len = stringToRepeat.length(); | |||
| String result (Preallocation (len * numberOfTimesToRepeat + 1)); | |||
| String result (Preallocation (stringToRepeat.length() * numberOfTimesToRepeat + 1)); | |||
| CharPointerType n (result.text); | |||
| while (--numberOfTimesToRepeat >= 0) | |||
| { | |||
| StringHolder::copyChars (n, stringToRepeat.text, len); | |||
| n += len; | |||
| } | |||
| n.writeAll (stringToRepeat.text); | |||
| return result; | |||
| } | |||
| @@ -1602,57 +1585,63 @@ const String String::quoted (const juce_wchar quoteCharacter) const | |||
| } | |||
| //============================================================================== | |||
| const String String::trim() const | |||
| static String::CharPointerType findTrimmedEnd (const String::CharPointerType& start, String::CharPointerType end) | |||
| { | |||
| if (isEmpty()) | |||
| return empty; | |||
| int start = 0; | |||
| while ((text + start).isWhitespace()) | |||
| ++start; | |||
| while (end > start) | |||
| { | |||
| if (! (--end).isWhitespace()) | |||
| { | |||
| ++end; | |||
| break; | |||
| } | |||
| } | |||
| const int len = length(); | |||
| int end = len - 1; | |||
| return end; | |||
| } | |||
| while ((end >= start) && (text + end).isWhitespace()) | |||
| --end; | |||
| const String String::trim() const | |||
| { | |||
| if (isNotEmpty()) | |||
| { | |||
| CharPointerType start (text.findEndOfWhitespace()); | |||
| ++end; | |||
| const CharPointerType end (start.findTerminatingNull()); | |||
| CharPointerType trimmedEnd (findTrimmedEnd (start, end)); | |||
| if (end <= start) | |||
| return empty; | |||
| else if (start > 0 || end < len) | |||
| return String (text + start, end - start); | |||
| if (trimmedEnd <= start) | |||
| return empty; | |||
| else if (text < start || trimmedEnd < end) | |||
| return String (start, trimmedEnd); | |||
| } | |||
| return *this; | |||
| } | |||
| const String String::trimStart() const | |||
| { | |||
| if (isEmpty()) | |||
| return empty; | |||
| CharPointerType t (text.findEndOfWhitespace()); | |||
| if (isNotEmpty()) | |||
| { | |||
| const CharPointerType t (text.findEndOfWhitespace()); | |||
| if (t == text) | |||
| return *this; | |||
| if (t != text) | |||
| return String (t); | |||
| } | |||
| return String (t); | |||
| return *this; | |||
| } | |||
| const String String::trimEnd() const | |||
| { | |||
| if (isEmpty()) | |||
| return empty; | |||
| CharPointerType endT (text); | |||
| endT = endT.findTerminatingNull() - 1; | |||
| if (isNotEmpty()) | |||
| { | |||
| const CharPointerType end (text.findTerminatingNull()); | |||
| CharPointerType trimmedEnd (findTrimmedEnd (text, end)); | |||
| while ((endT.getAddress() >= text) && endT.isWhitespace()) | |||
| --endT; | |||
| if (trimmedEnd < end) | |||
| return String (text, trimmedEnd); | |||
| } | |||
| return String (text, 1 + (int) (endT.getAddress() - text)); | |||
| return *this; | |||
| } | |||
| const String String::trimCharactersAtStart (const String& charactersToTrim) const | |||
| @@ -1667,20 +1656,25 @@ const String String::trimCharactersAtStart (const String& charactersToTrim) cons | |||
| const String String::trimCharactersAtEnd (const String& charactersToTrim) const | |||
| { | |||
| if (isEmpty()) | |||
| return empty; | |||
| if (isNotEmpty()) | |||
| { | |||
| const CharPointerType end (text.findTerminatingNull()); | |||
| CharPointerType trimmedEnd (end); | |||
| const int len = length(); | |||
| const juce_wchar* endT = text + (len - 1); | |||
| int numToRemove = 0; | |||
| while (trimmedEnd > text) | |||
| { | |||
| if (! charactersToTrim.containsChar (*--trimmedEnd)) | |||
| { | |||
| ++trimmedEnd; | |||
| break; | |||
| } | |||
| } | |||
| while (numToRemove < len && charactersToTrim.containsChar (*endT)) | |||
| { | |||
| ++numToRemove; | |||
| --endT; | |||
| if (trimmedEnd < end) | |||
| return String (text, trimmedEnd); | |||
| } | |||
| return numToRemove > 0 ? String (text, len - numToRemove) : *this; | |||
| return *this; | |||
| } | |||
| //============================================================================== | |||
| @@ -1734,33 +1728,32 @@ const String String::removeCharacters (const String& charactersToRemove) const | |||
| const String String::initialSectionContainingOnly (const String& permittedCharacters) const | |||
| { | |||
| int i = 0; | |||
| CharPointerType t (text); | |||
| for (;;) | |||
| while (! t.isEmpty()) | |||
| { | |||
| if (! permittedCharacters.containsChar (text[i])) | |||
| break; | |||
| if (! permittedCharacters.containsChar (*t)) | |||
| return String (text, t); | |||
| ++i; | |||
| ++t; | |||
| } | |||
| return substring (0, i); | |||
| return *this; | |||
| } | |||
| const String String::initialSectionNotContaining (const String& charactersToStopAt) const | |||
| { | |||
| const juce_wchar* const t = text; | |||
| int i = 0; | |||
| CharPointerType t (text); | |||
| while (t[i] != 0) | |||
| while (! t.isEmpty()) | |||
| { | |||
| if (charactersToStopAt.containsChar (t[i])) | |||
| return String (text, i); | |||
| if (charactersToStopAt.containsChar (*t)) | |||
| return String (text, t); | |||
| ++i; | |||
| ++t; | |||
| } | |||
| return empty; | |||
| return *this; | |||
| } | |||
| bool String::containsOnly (const String& chars) const throw() | |||
| @@ -1776,10 +1769,10 @@ bool String::containsOnly (const String& chars) const throw() | |||
| bool String::containsAnyOf (const String& chars) const throw() | |||
| { | |||
| const juce_wchar* t = text; | |||
| CharPointerType t (text); | |||
| while (*t != 0) | |||
| if (chars.containsChar (*t++)) | |||
| while (! t.isEmpty()) | |||
| if (chars.containsChar (t.getAndAdvance())) | |||
| return true; | |||
| return false; | |||
| @@ -1892,40 +1885,50 @@ double String::getDoubleValue() const throw() | |||
| static const char* const hexDigits = "0123456789abcdef"; | |||
| const String String::toHexString (const int number) | |||
| template <typename Type> | |||
| struct HexConverter | |||
| { | |||
| juce_wchar buffer[32]; | |||
| juce_wchar* const end = buffer + 32; | |||
| juce_wchar* t = end; | |||
| *--t = 0; | |||
| unsigned int v = (unsigned int) number; | |||
| do | |||
| static const String hexToString (Type v) | |||
| { | |||
| *--t = (juce_wchar) hexDigits [v & 15]; | |||
| v >>= 4; | |||
| juce_wchar buffer[32]; | |||
| juce_wchar* const end = buffer + 32; | |||
| juce_wchar* t = end; | |||
| *--t = 0; | |||
| } while (v != 0); | |||
| do | |||
| { | |||
| *--t = (juce_wchar) hexDigits [(int) (v & 15)]; | |||
| v >>= 4; | |||
| return String (t, (int) (((char*) end) - (char*) t) - 1); | |||
| } | |||
| } while (v != 0); | |||
| const String String::toHexString (const int64 number) | |||
| { | |||
| juce_wchar buffer[32]; | |||
| juce_wchar* const end = buffer + 32; | |||
| juce_wchar* t = end; | |||
| *--t = 0; | |||
| uint64 v = (uint64) number; | |||
| return String (t, (int) (end - t) - 1); | |||
| } | |||
| do | |||
| static Type stringToHex (String::CharPointerType t) throw() | |||
| { | |||
| *--t = (juce_wchar) hexDigits [(int) (v & 15)]; | |||
| v >>= 4; | |||
| Type result = 0; | |||
| while (! t.isEmpty()) | |||
| { | |||
| const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); | |||
| if (hexValue >= 0) | |||
| result = (result << 4) | hexValue; | |||
| } | |||
| return result; | |||
| } | |||
| }; | |||
| } while (v != 0); | |||
| const String String::toHexString (const int number) | |||
| { | |||
| return HexConverter <unsigned int>::hexToString ((unsigned int) number); | |||
| } | |||
| return String (t, (int) (((char*) end) - (char*) t)); | |||
| const String String::toHexString (const int64 number) | |||
| { | |||
| return HexConverter <uint64>::hexToString ((uint64) number); | |||
| } | |||
| const String String::toHexString (const short number) | |||
| @@ -1962,34 +1965,12 @@ const String String::toHexString (const unsigned char* data, const int size, con | |||
| int String::getHexValue32() const throw() | |||
| { | |||
| int result = 0; | |||
| CharPointerType t (text); | |||
| while (! t.isEmpty()) | |||
| { | |||
| const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); | |||
| if (hexValue >= 0) | |||
| result = (result << 4) | hexValue; | |||
| } | |||
| return result; | |||
| return HexConverter <int>::stringToHex (text); | |||
| } | |||
| int64 String::getHexValue64() const throw() | |||
| { | |||
| int64 result = 0; | |||
| CharPointerType t (text); | |||
| while (! t.isEmpty()) | |||
| { | |||
| const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); | |||
| if (hexValue >= 0) | |||
| result = (result << 4) | hexValue; | |||
| } | |||
| return result; | |||
| return HexConverter <int64>::stringToHex (text); | |||
| } | |||
| //============================================================================== | |||
| @@ -2005,8 +1986,10 @@ const String String::createStringFromData (const void* const data_, const int si | |||
| { | |||
| return charToString ((char) data[0]); | |||
| } | |||
| else if ((data[0] == (uint8) CharPointer_UTF16::byteOrderMarkBE1 && data[1] == (uint8) CharPointer_UTF16::byteOrderMarkBE2) | |||
| || (data[0] == (uint8) CharPointer_UTF16::byteOrderMarkLE1 && data[1] == (uint8) CharPointer_UTF16::byteOrderMarkLE1)) | |||
| else if ((data[0] == (uint8) CharPointer_UTF16::byteOrderMarkBE1 | |||
| && data[1] == (uint8) CharPointer_UTF16::byteOrderMarkBE2) | |||
| || (data[0] == (uint8) CharPointer_UTF16::byteOrderMarkLE1 | |||
| && data[1] == (uint8) CharPointer_UTF16::byteOrderMarkLE1)) | |||
| { | |||
| const bool bigEndian = (data[0] == (uint8) CharPointer_UTF16::byteOrderMarkBE1); | |||
| const int numChars = size / 2 - 1; | |||
| @@ -2454,7 +2437,9 @@ public: | |||
| expect (s5.removeCharacters ("1wordxya") == " 2 3"); | |||
| expectEquals (s5.removeCharacters (String::empty), s5); | |||
| expect (s5.initialSectionContainingOnly ("word") == L"word"); | |||
| expect (String ("word").initialSectionContainingOnly ("word") == L"word"); | |||
| expectEquals (s5.initialSectionNotContaining (String ("xyz ")), String ("word")); | |||
| expectEquals (s5.initialSectionNotContaining (String (";[:'/")), s5); | |||
| expect (! s5.isQuotedString()); | |||
| expect (s5.quoted().isQuotedString()); | |||
| expect (! s5.quoted().unquoted().isQuotedString()); | |||
| @@ -123,6 +123,9 @@ public: | |||
| /** Creates a string from a UTF-32 character string */ | |||
| String (const CharPointer_UTF32& text, size_t maxChars); | |||
| /** Creates a string from a UTF-32 character string */ | |||
| String (const CharPointer_UTF32& start, const CharPointer_UTF32& end); | |||
| /** Creates a string from an ASCII character string */ | |||
| String (const CharPointer_ASCII& text); | |||
| @@ -772,7 +775,8 @@ public: | |||
| /** Returns a section from the start of the string that only contains a certain set of characters. | |||
| This returns the leftmost section of the string, up to (and not including) the | |||
| first character that occurs in the string passed in. | |||
| first character that occurs in the string passed in. (If none of the specified | |||
| characters are found in the string, the return value will just be the original string). | |||
| */ | |||
| const String initialSectionNotContaining (const String& charactersToStopAt) const; | |||