From 1b05a7d46db68fa0c1d0ad63210f316df51d0088 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Wed, 26 Jan 2011 19:49:36 +0000 Subject: [PATCH] New classes CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32, complete refactoring of CharacterFunctions class and updates to the internals of String methods. Removal of String::operator+= (unsigned int) because of clashes with wide-char types that use unsigned int. Made core classes compatible with Android. Minor fixes to ListBox and AudioDeviceManager. --- Builds/MacOSX/Juce.xcodeproj/project.pbxproj | 6 + Builds/VisualStudio2005/Juce.vcproj | 3 + Builds/VisualStudio2008/Juce.vcproj | 3 + Builds/VisualStudio2008_DLL/Juce.vcproj | 3 + Builds/VisualStudio2010/Juce.vcxproj | 3 + Builds/VisualStudio2010/Juce.vcxproj.filters | 9 + Builds/iOS/Juce.xcodeproj/project.pbxproj | 6 + Juce.jucer | 6 + amalgamation/juce_amalgamated_template.cpp | 12 +- extras/juce demo/Source/demos/WidgetsDemo.cpp | 2 +- juce_amalgamated.cpp | 4412 ++++++++++++----- juce_amalgamated.h | 1588 +++++- src/audio/devices/juce_AudioDeviceManager.cpp | 15 +- src/audio/devices/juce_AudioDeviceManager.h | 2 +- src/containers/juce_SparseSet.h | 7 +- src/core/juce_StandardHeader.h | 7 +- src/core/juce_TargetPlatform.h | 18 +- .../juce_CPlusPlusCodeTokeniser.cpp | 44 +- src/gui/components/controls/juce_ListBox.cpp | 2 +- src/io/network/juce_Socket.cpp | 4 +- src/io/network/juce_URL.cpp | 10 +- src/juce_core_includes.h | 9 + src/maths/juce_MathsFunctions.h | 6 +- src/memory/juce_Atomic.h | 22 +- src/memory/juce_ByteOrder.h | 8 +- src/native/common/juce_posix_SharedCode.h | 17 +- src/native/linux/juce_linux_Network.cpp | 2 +- src/native/windows/juce_win32_FileChooser.cpp | 2 +- src/native/windows/juce_win32_Misc.cpp | 4 +- .../juce_win32_QuickTimeMovieComponent.cpp | 2 +- src/text/juce_CharPointer_UTF16.h | 343 ++ src/text/juce_CharPointer_UTF32.h | 340 ++ src/text/juce_CharPointer_UTF8.h | 441 ++ src/text/juce_CharacterFunctions.cpp | 739 +-- src/text/juce_CharacterFunctions.h | 456 +- src/text/juce_String.cpp | 591 +-- src/text/juce_String.h | 24 +- src/text/juce_StringArray.cpp | 6 +- src/text/juce_XmlDocument.cpp | 104 +- src/text/juce_XmlDocument.h | 2 +- src/text/juce_XmlElement.cpp | 2 +- src/utilities/juce_PropertiesFile.cpp | 13 +- 42 files changed, 6825 insertions(+), 2470 deletions(-) create mode 100644 src/text/juce_CharPointer_UTF16.h create mode 100644 src/text/juce_CharPointer_UTF32.h create mode 100644 src/text/juce_CharPointer_UTF8.h diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj index 9e59ff9712..5ba34c1684 100644 --- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj @@ -999,6 +999,9 @@ 8E8BE2F1C182E418BBA6903C = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_win32_Windowing.cpp; path = ../../src/native/windows/juce_win32_Windowing.cpp; sourceTree = SOURCE_ROOT; }; 76E2084D2148068F9138A816 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_CharacterFunctions.cpp; path = ../../src/text/juce_CharacterFunctions.cpp; sourceTree = SOURCE_ROOT; }; 33F16EE4F38C9B76E7FAEF78 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharacterFunctions.h; path = ../../src/text/juce_CharacterFunctions.h; sourceTree = SOURCE_ROOT; }; + 4007410FACA2F865FD8EF769 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_UTF8.h; path = ../../src/text/juce_CharPointer_UTF8.h; sourceTree = SOURCE_ROOT; }; + 663746215E9BA6C761172B85 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_UTF16.h; path = ../../src/text/juce_CharPointer_UTF16.h; sourceTree = SOURCE_ROOT; }; + C3FD9D93626F80A45F9B6DDE = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_UTF32.h; path = ../../src/text/juce_CharPointer_UTF32.h; sourceTree = SOURCE_ROOT; }; 8273A206FB309671284959DD = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Identifier.cpp; path = ../../src/text/juce_Identifier.cpp; sourceTree = SOURCE_ROOT; }; BF888BC540B64D5C61E46A34 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Identifier.h; path = ../../src/text/juce_Identifier.h; sourceTree = SOURCE_ROOT; }; 4A97C8D2FF6454DDD3AF4BE5 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_LocalisedStrings.cpp; path = ../../src/text/juce_LocalisedStrings.cpp; sourceTree = SOURCE_ROOT; }; @@ -1803,6 +1806,9 @@ C6B52BCD0CB1B809D6DE555A = { isa = PBXGroup; children = ( 76E2084D2148068F9138A816, 33F16EE4F38C9B76E7FAEF78, + 4007410FACA2F865FD8EF769, + 663746215E9BA6C761172B85, + C3FD9D93626F80A45F9B6DDE, 8273A206FB309671284959DD, BF888BC540B64D5C61E46A34, 4A97C8D2FF6454DDD3AF4BE5, diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj index 3cdbd69660..fcca9b0494 100644 --- a/Builds/VisualStudio2005/Juce.vcproj +++ b/Builds/VisualStudio2005/Juce.vcproj @@ -937,6 +937,9 @@ + + + diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj index a79b41bb79..4c156b8154 100644 --- a/Builds/VisualStudio2008/Juce.vcproj +++ b/Builds/VisualStudio2008/Juce.vcproj @@ -937,6 +937,9 @@ + + + diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj index 3ed6b501fc..7209adf05c 100644 --- a/Builds/VisualStudio2008_DLL/Juce.vcproj +++ b/Builds/VisualStudio2008_DLL/Juce.vcproj @@ -939,6 +939,9 @@ + + + diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj index d191c12aa2..5cf3c98421 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj +++ b/Builds/VisualStudio2010/Juce.vcxproj @@ -757,6 +757,9 @@ + + + diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters index b0d60c44d4..176d340eda 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj.filters +++ b/Builds/VisualStudio2010/Juce.vcxproj.filters @@ -2202,6 +2202,15 @@ Juce\Source\text + + Juce\Source\text + + + Juce\Source\text + + + Juce\Source\text + Juce\Source\text diff --git a/Builds/iOS/Juce.xcodeproj/project.pbxproj b/Builds/iOS/Juce.xcodeproj/project.pbxproj index e8d8910038..7e7f336b0b 100644 --- a/Builds/iOS/Juce.xcodeproj/project.pbxproj +++ b/Builds/iOS/Juce.xcodeproj/project.pbxproj @@ -999,6 +999,9 @@ 8E8BE2F1C182E418BBA6903C = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_win32_Windowing.cpp; path = ../../src/native/windows/juce_win32_Windowing.cpp; sourceTree = SOURCE_ROOT; }; 76E2084D2148068F9138A816 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_CharacterFunctions.cpp; path = ../../src/text/juce_CharacterFunctions.cpp; sourceTree = SOURCE_ROOT; }; 33F16EE4F38C9B76E7FAEF78 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharacterFunctions.h; path = ../../src/text/juce_CharacterFunctions.h; sourceTree = SOURCE_ROOT; }; + 4007410FACA2F865FD8EF769 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_UTF8.h; path = ../../src/text/juce_CharPointer_UTF8.h; sourceTree = SOURCE_ROOT; }; + 663746215E9BA6C761172B85 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_UTF16.h; path = ../../src/text/juce_CharPointer_UTF16.h; sourceTree = SOURCE_ROOT; }; + C3FD9D93626F80A45F9B6DDE = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_UTF32.h; path = ../../src/text/juce_CharPointer_UTF32.h; sourceTree = SOURCE_ROOT; }; 8273A206FB309671284959DD = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Identifier.cpp; path = ../../src/text/juce_Identifier.cpp; sourceTree = SOURCE_ROOT; }; BF888BC540B64D5C61E46A34 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Identifier.h; path = ../../src/text/juce_Identifier.h; sourceTree = SOURCE_ROOT; }; 4A97C8D2FF6454DDD3AF4BE5 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_LocalisedStrings.cpp; path = ../../src/text/juce_LocalisedStrings.cpp; sourceTree = SOURCE_ROOT; }; @@ -1803,6 +1806,9 @@ C6B52BCD0CB1B809D6DE555A = { isa = PBXGroup; children = ( 76E2084D2148068F9138A816, 33F16EE4F38C9B76E7FAEF78, + 4007410FACA2F865FD8EF769, + 663746215E9BA6C761172B85, + C3FD9D93626F80A45F9B6DDE, 8273A206FB309671284959DD, BF888BC540B64D5C61E46A34, 4A97C8D2FF6454DDD3AF4BE5, diff --git a/Juce.jucer b/Juce.jucer index acb720ca2e..9667fd5628 100644 --- a/Juce.jucer +++ b/Juce.jucer @@ -1435,6 +1435,12 @@ resource="0" file="src/text/juce_CharacterFunctions.cpp"/> + + + setBounds (10, 25, 200, 24); textEditor1->setText ("single-line text box"); - TextEditor* textEditor2 = new TextEditor ("password", (tchar) 0x2022); + TextEditor* textEditor2 = new TextEditor ("password", (juce_wchar) 0x2022); page->addAndMakeVisible (textEditor2); textEditor2->setBounds (10, 55, 200, 24); textEditor2->setText ("password"); diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 2e4b2562b9..95701be110 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -60,7 +60,7 @@ #define JUCE_WINDOWS 1 #elif defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 -#elif defined(__APPLE_CPP__) || defined(__APPLE_CC__) +#elif defined (__APPLE_CPP__) || defined(__APPLE_CC__) #include // (needed to find out what platform we're using) #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR @@ -69,6 +69,9 @@ #else #define JUCE_MAC 1 #endif +#elif defined (JUCE_ANDROID) + #undef JUCE_ANDROID + #define JUCE_ANDROID 1 #else #error "Unknown platform!" #endif @@ -137,15 +140,19 @@ #endif -#if JUCE_LINUX +#if JUCE_LINUX || JUCE_ANDROID #ifdef _DEBUG #define JUCE_DEBUG 1 #endif // Allow override for big-endian Linux platforms - #ifndef JUCE_BIG_ENDIAN + #if defined (__LITTLE_ENDIAN__) || ! defined (JUCE_BIG_ENDIAN) #define JUCE_LITTLE_ENDIAN 1 + #undef JUCE_BIG_ENDIAN + #else + #undef JUCE_LITTLE_ENDIAN + #define JUCE_BIG_ENDIAN 1 #endif #if defined (__LP64__) || defined (_LP64) @@ -154,7 +161,9 @@ #define JUCE_32BIT 1 #endif - #define JUCE_INTEL 1 + #if __MMX__ || __SSE__ || __amd64__ + #define JUCE_INTEL 1 + #endif #endif // Compiler type macros. @@ -918,6 +927,35 @@ protected: /*** End of inlined file: juce_mac_NativeIncludes.h ***/ + #elif JUCE_ANDROID + +/*** Start of inlined file: juce_android_NativeIncludes.h ***/ +#ifndef __JUCE_ANDROID_NATIVEINCLUDES_JUCEHEADER__ +#define __JUCE_ANDROID_NATIVEINCLUDES_JUCEHEADER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if JUCE_OPENGL + #include +#endif + +#endif // __JUCE_ANDROID_NATIVEINCLUDES_JUCEHEADER__ +/*** End of inlined file: juce_android_NativeIncludes.h ***/ + + #else #error "Unknown platform!" #endif @@ -8874,7 +8912,7 @@ END_JUCE_NAMESPACE #endif #else - #if JUCE_LINUX + #if JUCE_LINUX || JUCE_ANDROID #include #include #include @@ -8892,7 +8930,7 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE -#if defined (JUCE_LINUX) || defined (JUCE_MAC) || defined (JUCE_IOS) +#if JUCE_LINUX || JUCE_MAC || JUCE_IOS || JUCE_ANDROID typedef socklen_t juce_socklen_t; #else typedef int juce_socklen_t; @@ -9562,7 +9600,7 @@ namespace URLHelpers int i = 0; while (CharacterFunctions::isLetterOrDigit (url[i]) - || CharacterFunctions::indexOfChar (L"+-.", url[i], false) >= 0) + || url[i] == '+' || url[i] == '-' || url[i] == '.') ++i; return url[i] == ':' ? i + 1 : 0; @@ -9620,7 +9658,7 @@ namespace URLHelpers // just a short text attachment, so use simple url encoding.. headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " - << (unsigned int) postData.getSize() << "\r\n"; + << (int) postData.getSize() << "\r\n"; } } } @@ -9847,8 +9885,8 @@ const String URL::removeEscapeChars (const String& s) const String URL::addEscapeChars (const String& s, const bool isParameter) { - const char* const legalChars = isParameter ? "_-.*!'()" - : ",$_-.*!'()"; + const CharPointer_UTF8 legalChars (isParameter ? "_-.*!'()" + : ",$_-.*!'()"); Array utf8 (s.toUTF8(), s.getNumBytesAsUTF8()); @@ -9857,7 +9895,7 @@ const String URL::addEscapeChars (const String& s, const bool isParameter) const char c = utf8.getUnchecked(i); if (! (CharacterFunctions::isLetterOrDigit (c) - || CharacterFunctions::indexOfChar (legalChars, c, false) >= 0)) + || legalChars.indexOf ((juce_wchar) c) >= 0)) { if (c == ' ') { @@ -11044,705 +11082,41 @@ END_JUCE_NAMESPACE #pragma warning (disable: 4514 4996) #endif -#include +#if ! JUCE_ANDROID + #include +#endif + #include #include BEGIN_JUCE_NAMESPACE -int CharacterFunctions::length (const char* const s) throw() -{ - return (int) strlen (s); -} - -int CharacterFunctions::length (const juce_wchar* const s) throw() -{ - return (int) wcslen (s); -} - -void CharacterFunctions::copy (char* dest, const char* src, const int maxChars) throw() -{ - strncpy (dest, src, maxChars); -} - -void CharacterFunctions::copy (juce_wchar* dest, const juce_wchar* src, int maxChars) throw() -{ - wcsncpy (dest, src, maxChars); -} - -void CharacterFunctions::copy (juce_wchar* dest, const char* src, const int maxChars) throw() -{ - mbstowcs (dest, src, maxChars); -} - -void CharacterFunctions::copy (char* dest, const juce_wchar* src, const int maxChars) throw() -{ - wcstombs (dest, src, maxChars); -} - -int CharacterFunctions::bytesRequiredForCopy (const juce_wchar* src) throw() -{ - return (int) wcstombs (0, src, 0); -} - -void CharacterFunctions::append (char* dest, const char* src) throw() -{ - strcat (dest, src); -} - -void CharacterFunctions::append (juce_wchar* dest, const juce_wchar* src) throw() -{ - wcscat (dest, src); -} - -int CharacterFunctions::compare (const char* const s1, const char* const s2) throw() -{ - return strcmp (s1, s2); -} - -int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - return wcscmp (s1, s2); -} - -int CharacterFunctions::compare (const char* const s1, const char* const s2, const int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - return strncmp (s1, s2, maxChars); -} - -int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - return wcsncmp (s1, s2, maxChars); -} - -int CharacterFunctions::compare (const juce_wchar* s1, const char* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - - for (;;) - { - const int diff = (int) (*s1 - (juce_wchar) (unsigned char) *s2); - - if (diff != 0) - return diff; - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -} - -int CharacterFunctions::compare (const char* s1, const juce_wchar* s2) throw() -{ - return -compare (s2, s1); -} - -int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return stricmp (s1, s2); -#else - return strcasecmp (s1, s2); -#endif -} - -int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return _wcsicmp (s1, s2); -#else - for (;;) - { - if (*s1 != *s2) - { - const int diff = toUpperCase (*s1) - toUpperCase (*s2); - - if (diff != 0) - return diff < 0 ? -1 : 1; - } - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -#endif -} - -int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const char* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - - for (;;) - { - if (*s1 != *s2) - { - const int diff = toUpperCase (*s1) - toUpperCase (*s2); - - if (diff != 0) - return diff < 0 ? -1 : 1; - } - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -} - -int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2, const int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return strnicmp (s1, s2, maxChars); -#else - return strncasecmp (s1, s2, maxChars); -#endif -} - -int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return _wcsnicmp (s1, s2, maxChars); -#else - while (--maxChars >= 0) - { - if (*s1 != *s2) - { - const int diff = toUpperCase (*s1) - toUpperCase (*s2); - - if (diff != 0) - return diff < 0 ? -1 : 1; - } - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -#endif -} - -const char* CharacterFunctions::find (const char* const haystack, const char* const needle) throw() -{ - return strstr (haystack, needle); -} - -const juce_wchar* CharacterFunctions::find (const juce_wchar* haystack, const juce_wchar* const needle) throw() -{ - return wcsstr (haystack, needle); -} - -int CharacterFunctions::indexOfChar (const char* const haystack, const char needle, const bool ignoreCase) throw() -{ - if (haystack != 0) - { - int i = 0; - - if (ignoreCase) - { - const char n1 = toLowerCase (needle); - const char n2 = toUpperCase (needle); - - if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search - { - while (haystack[i] != 0) - { - if (haystack[i] == n1 || haystack[i] == n2) - return i; - - ++i; - } - - return -1; - } - - jassert (n1 == needle); - } - - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - } - - return -1; -} - -int CharacterFunctions::indexOfChar (const juce_wchar* const haystack, const juce_wchar needle, const bool ignoreCase) throw() -{ - if (haystack != 0) - { - int i = 0; - - if (ignoreCase) - { - const juce_wchar n1 = toLowerCase (needle); - const juce_wchar n2 = toUpperCase (needle); - - if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search - { - while (haystack[i] != 0) - { - if (haystack[i] == n1 || haystack[i] == n2) - return i; - - ++i; - } - - return -1; - } - - jassert (n1 == needle); - } - - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - } - - return -1; -} - -int CharacterFunctions::indexOfCharFast (const char* const haystack, const char needle) throw() -{ - jassert (haystack != 0); - - int i = 0; - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - - return -1; -} - -int CharacterFunctions::indexOfCharFast (const juce_wchar* const haystack, const juce_wchar needle) throw() -{ - jassert (haystack != 0); - - int i = 0; - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - - return -1; -} - -int CharacterFunctions::getIntialSectionContainingOnly (const char* const text, const char* const allowedChars) throw() -{ - return allowedChars == 0 ? 0 : (int) strspn (text, allowedChars); -} - -int CharacterFunctions::getIntialSectionContainingOnly (const juce_wchar* const text, const juce_wchar* const allowedChars) throw() -{ - if (allowedChars == 0) - return 0; - - int i = 0; - - for (;;) - { - if (indexOfCharFast (allowedChars, text[i]) < 0) - break; - - ++i; - } - - return i; -} - -int CharacterFunctions::ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw() -{ - return (int) strftime (dest, maxChars, format, tm); -} - -int CharacterFunctions::ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw() -{ - return (int) wcsftime (dest, maxChars, format, tm); -} - -int CharacterFunctions::getIntValue (const char* const s) throw() -{ - return atoi (s); -} - -int CharacterFunctions::getIntValue (const juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - return _wtoi (s); -#else - int v = 0; - - while (isWhitespace (*s)) - ++s; - - const bool isNeg = *s == '-'; - if (isNeg) - ++s; - - for (;;) - { - const wchar_t c = *s++; - - if (c >= '0' && c <= '9') - v = v * 10 + (int) (c - '0'); - else - break; - } - - return isNeg ? -v : v; -#endif -} - -int64 CharacterFunctions::getInt64Value (const char* s) throw() -{ -#if JUCE_LINUX - return atoll (s); -#elif JUCE_WINDOWS - return _atoi64 (s); -#else - int64 v = 0; - - while (isWhitespace (*s)) - ++s; - - const bool isNeg = *s == '-'; - if (isNeg) - ++s; - - for (;;) - { - const char c = *s++; - - if (c >= '0' && c <= '9') - v = v * 10 + (int64) (c - '0'); - else - break; - } - - return isNeg ? -v : v; -#endif -} - -int64 CharacterFunctions::getInt64Value (const juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - return _wtoi64 (s); -#else - int64 v = 0; - - while (isWhitespace (*s)) - ++s; - - const bool isNeg = *s == '-'; - if (isNeg) - ++s; - - for (;;) - { - const juce_wchar c = *s++; - - if (c >= '0' && c <= '9') - v = v * 10 + (int64) (c - '0'); - else - break; - } - - return isNeg ? -v : v; -#endif -} - -namespace -{ - double juce_mulexp10 (const double value, int exponent) throw() - { - if (exponent == 0) - return value; - - if (value == 0) - return 0; - - const bool negative = (exponent < 0); - if (negative) - exponent = -exponent; - - double result = 1.0, power = 10.0; - for (int bit = 1; exponent != 0; bit <<= 1) - { - if ((exponent & bit) != 0) - { - exponent ^= bit; - result *= power; - if (exponent == 0) - break; - } - power *= power; - } - - return negative ? (value / result) : (value * result); - } - - template - double juce_atof (const CharType* const original) throw() - { - double result[3] = { 0, 0, 0 }, accumulator[2] = { 0, 0 }; - int exponentAdjustment[2] = { 0, 0 }, exponentAccumulator[2] = { -1, -1 }; - int exponent = 0, decPointIndex = 0, digit = 0; - int lastDigit = 0, numSignificantDigits = 0; - bool isNegative = false, digitsFound = false; - const int maxSignificantDigits = 15 + 2; - - const CharType* s = original; - while (CharacterFunctions::isWhitespace (*s)) - ++s; - - switch (*s) - { - case '-': isNegative = true; // fall-through.. - case '+': ++s; - } - - if (*s == 'n' || *s == 'N' || *s == 'i' || *s == 'I') - return atof (String (original).toUTF8()); // Let the c library deal with NAN and INF - - for (;;) - { - if (CharacterFunctions::isDigit (*s)) - { - lastDigit = digit; - digit = *s++ - '0'; - digitsFound = true; - - if (decPointIndex != 0) - exponentAdjustment[1]++; - - if (numSignificantDigits == 0 && digit == 0) - continue; - - if (++numSignificantDigits > maxSignificantDigits) - { - if (digit > 5) - ++accumulator [decPointIndex]; - else if (digit == 5 && (lastDigit & 1) != 0) - ++accumulator [decPointIndex]; - - if (decPointIndex > 0) - exponentAdjustment[1]--; - else - exponentAdjustment[0]++; - - while (CharacterFunctions::isDigit (*s)) - { - ++s; - if (decPointIndex == 0) - exponentAdjustment[0]++; - } - } - else - { - const double maxAccumulatorValue = (double) ((std::numeric_limits::max() - 9) / 10); - if (accumulator [decPointIndex] > maxAccumulatorValue) - { - result [decPointIndex] = juce_mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex]) - + accumulator [decPointIndex]; - accumulator [decPointIndex] = 0; - exponentAccumulator [decPointIndex] = 0; - } - - accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit; - exponentAccumulator [decPointIndex]++; - } - } - else if (decPointIndex == 0 && *s == '.') - { - ++s; - decPointIndex = 1; - - if (numSignificantDigits > maxSignificantDigits) - { - while (CharacterFunctions::isDigit (*s)) - ++s; - break; - } - } - else - { - break; - } - } - - result[0] = juce_mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0]; - - if (decPointIndex != 0) - result[1] = juce_mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1]; - - if ((*s == 'e' || *s == 'E') && digitsFound) - { - bool negativeExponent = false; - - switch (*++s) - { - case '-': negativeExponent = true; // fall-through.. - case '+': ++s; - } - - while (CharacterFunctions::isDigit (*s)) - exponent = (exponent * 10) + (*s++ - '0'); - - if (negativeExponent) - exponent = -exponent; - } - - double r = juce_mulexp10 (result[0], exponent + exponentAdjustment[0]); - if (decPointIndex != 0) - r += juce_mulexp10 (result[1], exponent - exponentAdjustment[1]); - - return isNegative ? -r : r; - } -} - -double CharacterFunctions::getDoubleValue (const char* const s) throw() -{ - return juce_atof (s); -} - -double CharacterFunctions::getDoubleValue (const juce_wchar* const s) throw() -{ - return juce_atof (s); -} - -char CharacterFunctions::toUpperCase (const char character) throw() -{ - return (char) toupper (character); -} - juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) throw() { return towupper (character); } -void CharacterFunctions::toUpperCase (char* s) throw() -{ -#if JUCE_WINDOWS - strupr (s); -#else - while (*s != 0) - { - *s = toUpperCase (*s); - ++s; - } -#endif -} - -void CharacterFunctions::toUpperCase (juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - _wcsupr (s); -#else - while (*s != 0) - { - *s = toUpperCase (*s); - ++s; - } -#endif -} - -bool CharacterFunctions::isUpperCase (const char character) throw() +juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) throw() { - return isupper (character) != 0; + return towlower (character); } bool CharacterFunctions::isUpperCase (const juce_wchar character) throw() { -#if JUCE_WINDOWS + #if JUCE_WINDOWS return iswupper (character) != 0; -#else + #else return toLowerCase (character) != character; -#endif -} - -char CharacterFunctions::toLowerCase (const char character) throw() -{ - return (char) tolower (character); -} - -juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) throw() -{ - return towlower (character); -} - -void CharacterFunctions::toLowerCase (char* s) throw() -{ -#if JUCE_WINDOWS - strlwr (s); -#else - while (*s != 0) - { - *s = toLowerCase (*s); - ++s; - } -#endif -} - -void CharacterFunctions::toLowerCase (juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - _wcslwr (s); -#else - while (*s != 0) - { - *s = toLowerCase (*s); - ++s; - } -#endif -} - -bool CharacterFunctions::isLowerCase (const char character) throw() -{ - return islower (character) != 0; + #endif } bool CharacterFunctions::isLowerCase (const juce_wchar character) throw() { -#if JUCE_WINDOWS + #if JUCE_WINDOWS return iswlower (character) != 0; -#else + #else return toUpperCase (character) != character; -#endif + #endif } bool CharacterFunctions::isWhitespace (const char character) throw() @@ -11805,10 +11179,56 @@ int CharacterFunctions::getHexDigitValue (const juce_wchar digit) throw() return -1; } +int CharacterFunctions::ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw() +{ + return (int) strftime (dest, maxChars, format, tm); +} + +int CharacterFunctions::ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw() +{ + #if JUCE_ANDROID + HeapBlock tempDest; + tempDest.calloc (maxChars + 2); + int result = ftime (tempDest.getData(), maxChars, String (format).toUTF8(), tm); + CharPointer_UTF32 (dest).copyAndAdvance (CharPointer_UTF8 (tempDest.getData())); + return result; + #else + return (int) wcsftime (dest, maxChars, format, tm); + #endif +} + #if JUCE_MSVC #pragma warning (pop) #endif +double CharacterFunctions::mulexp10 (const double value, int exponent) throw() +{ + if (exponent == 0) + return value; + + if (value == 0) + return 0; + + const bool negative = (exponent < 0); + if (negative) + exponent = -exponent; + + double result = 1.0, power = 10.0; + for (int bit = 1; exponent != 0; bit <<= 1) + { + if ((exponent & bit) != 0) + { + exponent ^= bit; + result *= power; + if (exponent == 0) + break; + } + power *= power; + } + + return negative ? (value / result) : (value * result); +} + END_JUCE_NAMESPACE /*** End of inlined file: juce_CharacterFunctions.cpp ***/ @@ -11947,17 +11367,13 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_String.cpp ***/ #if JUCE_MSVC #pragma warning (push) - #pragma warning (disable: 4514) + #pragma warning (disable: 4514 4996) #endif #include BEGIN_JUCE_NAMESPACE -#if JUCE_MSVC - #pragma warning (pop) -#endif - #if defined (JUCE_STRINGS_ARE_UNICODE) && ! JUCE_STRINGS_ARE_UNICODE #error "JUCE_STRINGS_ARE_UNICODE is deprecated! All strings are now unicode by default." #endif @@ -11991,7 +11407,7 @@ public: static juce_wchar* createCopy (const char* const src, const size_t numChars) { juce_wchar* const dest = createUninitialised (numChars); - CharacterFunctions::copy (dest, src, (int) numChars); + CharPointer_UTF32 (dest).copyAndAdvanceUpToNumChars (CharPointer_UTF8 (src), numChars); dest [numChars] = 0; return dest; } @@ -12144,7 +11560,7 @@ String::String (const String& stringToCopy, const size_t charsToAllocate) String::String (const char* const t) { if (t != 0 && *t != 0) - text = StringHolder::createCopy (t, CharacterFunctions::length (t)); + text = StringHolder::createCopy (t, CharPointer_UTF8 (t).length()); else text = StringHolder::getEmpty(); } @@ -12152,7 +11568,7 @@ String::String (const char* const t) String::String (const juce_wchar* const t) { if (t != 0 && *t != 0) - text = StringHolder::createCopy (t, CharacterFunctions::length (t)); + text = StringHolder::createCopy (t, CharPointer_UTF32 (t).length()); else text = StringHolder::getEmpty(); } @@ -12271,21 +11687,21 @@ namespace NumberToStringConverters return dp; } - juce_wchar* doubleToString (juce_wchar* buffer, int numChars, double n, int numDecPlaces, size_t& len) throw() + char* doubleToString (char* buffer, int numChars, double n, int numDecPlaces, size_t& len) throw() { if (numDecPlaces > 0 && n > -1.0e20 && n < 1.0e20) { - juce_wchar* const end = buffer + numChars; - juce_wchar* t = end; + char* const end = buffer + numChars; + char* t = end; int64 v = (int64) (pow (10.0, numDecPlaces) * std::abs (n) + 0.5); - *--t = (juce_wchar) 0; + *--t = (char) 0; while (numDecPlaces >= 0 || v > 0) { if (numDecPlaces == 0) - *--t = getDecimalPoint(); + *--t = (char) getDecimalPoint(); - *--t = (juce_wchar) ('0' + (v % 10)); + *--t = (char) ('0' + (v % 10)); v /= 10; --numDecPlaces; @@ -12299,15 +11715,7 @@ namespace NumberToStringConverters } else { - #if JUCE_WINDOWS - #if JUCE_VC7_OR_EARLIER || JUCE_MINGW - len = _snwprintf (buffer, numChars, L"%.9g", n); - #else - len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n); - #endif - #else - len = swprintf (buffer, numChars, L"%.9g", n); - #endif + len = sprintf (buffer, "%.9g", n); return buffer; } } @@ -12363,23 +11771,23 @@ String::String (const uint64 number) String::String (const float number, const int numberOfDecimalPlaces) { - juce_wchar buffer [48]; + char buffer [48]; size_t len; - juce_wchar* start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); - createInternal (start, len); + char* const start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); + text = StringHolder::createCopy (start, len); } String::String (const double number, const int numberOfDecimalPlaces) { - juce_wchar buffer [48]; + char buffer [48]; size_t len; - juce_wchar* start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), number, numberOfDecimalPlaces, len); - createInternal (start, len); + char* const start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), number, numberOfDecimalPlaces, len); + text = StringHolder::createCopy (start, len); } int String::length() const throw() { - return CharacterFunctions::length (text); + return CharPointer_UTF32 (text).length(); } int String::hashCode() const throw() @@ -12456,59 +11864,61 @@ JUCE_API bool JUCE_CALLTYPE operator<= (const String& string1, const String& str bool String::equalsIgnoreCase (const juce_wchar* t) const throw() { - return t != 0 ? CharacterFunctions::compareIgnoreCase (text, t) == 0 + return t != 0 ? CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF32 (t)) == 0 : isEmpty(); } bool String::equalsIgnoreCase (const char* t) const throw() { - return t != 0 ? CharacterFunctions::compareIgnoreCase (text, t) == 0 + return t != 0 ? CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF8 (t)) == 0 : isEmpty(); } bool String::equalsIgnoreCase (const String& other) const throw() { return text == other.text - || CharacterFunctions::compareIgnoreCase (text, other.text) == 0; + || CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF32 (other.text)) == 0; } int String::compare (const String& other) const throw() { - return (text == other.text) ? 0 : CharacterFunctions::compare (text, other.text); + return (text == other.text) ? 0 : CharPointer_UTF32 (text).compare (CharPointer_UTF32 (other.text)); } int String::compare (const char* other) const throw() { - return other == 0 ? isEmpty() : CharacterFunctions::compare (text, other); + return other == 0 ? isEmpty() : CharPointer_UTF32 (text).compare (CharPointer_UTF8 (other)); } int String::compare (const juce_wchar* other) const throw() { - return other == 0 ? isEmpty() : CharacterFunctions::compare (text, other); + return other == 0 ? isEmpty() : CharPointer_UTF32 (text).compare (CharPointer_UTF32 (other)); } int String::compareIgnoreCase (const String& other) const throw() { - return (text == other.text) ? 0 : CharacterFunctions::compareIgnoreCase (text, other.text); + return (text == other.text) ? 0 : CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF32 (other.text)); } int String::compareLexicographically (const String& other) const throw() { - const juce_wchar* s1 = text; - while (*s1 != 0 && ! CharacterFunctions::isLetterOrDigit (*s1)) + CharPointer_UTF32 s1 (text); + + while (! (s1.isEmpty() || s1.isLetterOrDigit())) ++s1; - const juce_wchar* s2 = other.text; - while (*s2 != 0 && ! CharacterFunctions::isLetterOrDigit (*s2)) + CharPointer_UTF32 s2 (other.text); + + while (! (s2.isEmpty() || s2.isLetterOrDigit())) ++s2; - return CharacterFunctions::compareIgnoreCase (s1, s2); + return s1.compareIgnoreCase (s2); } String& String::operator+= (const juce_wchar* const t) { if (t != 0) - appendInternal (t, CharacterFunctions::length (t)); + appendInternal (t, CharPointer_UTF32 (t).length()); return *this; } @@ -12544,15 +11954,6 @@ String& String::operator+= (const int number) return *this; } -String& String::operator+= (const unsigned int number) -{ - juce_wchar buffer [16]; - juce_wchar* const end = buffer + numElementsInArray (buffer); - juce_wchar* const start = NumberToStringConverters::uintToString (end, number); - appendInternal (start, (int) (end - start)); - return *this; -} - void String::append (const juce_wchar* const other, const int howMany) { if (howMany > 0) @@ -12648,21 +12049,11 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const int number) return string1 += number; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const unsigned int number) -{ - return string1 += number; -} - JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const long number) { return string1 += (int) number; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const unsigned long number) -{ - return string1 += (unsigned int) number; -} - JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const float number) { return string1 += String (number); @@ -12714,8 +12105,7 @@ int String::lastIndexOfChar (const juce_wchar character) const throw() int String::indexOf (const String& t) const throw() { - const juce_wchar* const r = CharacterFunctions::find (text, t.text); - return r == 0 ? -1 : (int) (r - text); + return t.isEmpty() ? 0 : CharPointer_UTF32 (text).indexOf (CharPointer_UTF32 (t.text)); } int String::indexOfChar (const int startIndex, @@ -12745,13 +12135,16 @@ int String::indexOfAnyOf (const String& charactersToLookFor, if (startIndex > 0 && startIndex >= length()) return -1; - const juce_wchar* t = text + jmax (0, startIndex); + CharPointer_UTF32 t (text); + int i = jmax (0, startIndex); + t += i; - while (*t != 0) + while (! t.isEmpty()) { - if (CharacterFunctions::indexOfChar (charactersToLookFor.text, *t, ignoreCase) >= 0) - return (int) (t - text); + if (CharPointer_UTF32 (charactersToLookFor.text).indexOf (*t, ignoreCase) >= 0) + return i; + ++i; ++t; } @@ -12763,22 +12156,21 @@ int String::indexOf (const int startIndex, const String& other) const throw() if (startIndex > 0 && startIndex >= length()) return -1; - const juce_wchar* const found = CharacterFunctions::find (text + jmax (0, startIndex), other.text); - - return found == 0 ? -1 : (int) (found - text); + int i = CharPointer_UTF32 (text + jmax (0, startIndex)).indexOf (CharPointer_UTF32 (other.text)); + return i >= 0 ? i + startIndex : -1; } int String::indexOfIgnoreCase (const String& other) const throw() { - if (other.isNotEmpty()) - { - const int len = other.length(); - const int end = length() - len; + if (other.isEmpty()) + return 0; - for (int i = 0; i <= end; ++i) - if (CharacterFunctions::compareIgnoreCase (text + i, other.text, len) == 0) - return i; - } + const int len = other.length(); + const int end = length() - len; + + for (int i = 0; i <= end; ++i) + if (CharPointer_UTF32 (text + i).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), len) == 0) + return i; return -1; } @@ -12791,7 +12183,7 @@ int String::indexOfIgnoreCase (const int startIndex, const String& other) const const int end = length() - len; for (int i = jmax (0, startIndex); i <= end; ++i) - if (CharacterFunctions::compareIgnoreCase (text + i, other.text, len) == 0) + if (CharPointer_UTF32 (text + i).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), len) == 0) return i; } @@ -12811,9 +12203,10 @@ int String::lastIndexOf (const String& other) const throw() while (i >= 0) { - if (CharacterFunctions::compare (n--, other.text, len) == 0) + if (CharPointer_UTF32 (n).compareUpTo (CharPointer_UTF32 (other.text), len) == 0) return i; + --n; --i; } } @@ -12835,9 +12228,10 @@ int String::lastIndexOfIgnoreCase (const String& other) const throw() while (i >= 0) { - if (CharacterFunctions::compareIgnoreCase (n--, other.text, len) == 0) + if (CharPointer_UTF32 (n).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), len) == 0) return i; + --n; --i; } } @@ -12849,7 +12243,7 @@ int String::lastIndexOfIgnoreCase (const String& other) const throw() int String::lastIndexOfAnyOf (const String& charactersToLookFor, const bool ignoreCase) const throw() { for (int i = length(); --i >= 0;) - if (CharacterFunctions::indexOfChar (charactersToLookFor.text, text[i], ignoreCase) >= 0) + if (CharPointer_UTF32 (charactersToLookFor.text).indexOf (text[i], ignoreCase) >= 0) return i; return -1; @@ -12885,18 +12279,16 @@ int String::indexOfWholeWord (const String& word) const throw() { if (word.isNotEmpty()) { + CharPointer_UTF32 t (text); const int wordLen = word.length(); - const int end = length() - wordLen; - const juce_wchar* t = text; + const int end = t.length() - wordLen; for (int i = 0; i <= end; ++i) { - if (CharacterFunctions::compare (t, word.text, wordLen) == 0 - && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) - && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) - { + if (t.compareUpTo (CharPointer_UTF32 (word.text), wordLen) == 0 + && (i == 0 || ! (t - 1).isLetterOrDigit()) + && ! (t + wordLen).isLetterOrDigit()) return i; - } ++t; } @@ -12909,18 +12301,16 @@ int String::indexOfWholeWordIgnoreCase (const String& word) const throw() { if (word.isNotEmpty()) { + CharPointer_UTF32 t (text); const int wordLen = word.length(); - const int end = length() - wordLen; - const juce_wchar* t = text; + const int end = t.length() - wordLen; for (int i = 0; i <= end; ++i) { - if (CharacterFunctions::compareIgnoreCase (t, word.text, wordLen) == 0 - && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) - && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) - { + if (t.compareIgnoreCaseUpTo (CharPointer_UTF32 (word.text), wordLen) == 0 + && (i == 0 || ! (t + -1).isLetterOrDigit()) + && ! (t + wordLen).isLetterOrDigit()) return i; - } ++t; } @@ -12941,43 +12331,43 @@ bool String::containsWholeWordIgnoreCase (const String& wordToLookFor) const thr namespace WildCardHelpers { - int indexOfMatch (const juce_wchar* const wildcard, - const juce_wchar* const test, + int indexOfMatch (CharPointer_UTF32 wildcard, + CharPointer_UTF32 test, const bool ignoreCase) throw() { int start = 0; - while (test [start] != 0) + while (! test.isEmpty()) { - int i = 0; + CharPointer_UTF32 t (test); + CharPointer_UTF32 w (wildcard); for (;;) { - const juce_wchar wc = wildcard [i]; - const juce_wchar c = test [i + start]; + const juce_wchar wc = *w; + const juce_wchar tc = *t; - if (wc == c - || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) - || (wc == '?' && c != 0)) + if (wc == tc + || (ignoreCase && w.toLowerCase() == t.toLowerCase()) + || (wc == '?' && tc != 0)) { if (wc == 0) return start; - ++i; + ++t; + ++w; } else { - if (wc == '*' && (wildcard [i + 1] == 0 - || indexOfMatch (wildcard + i + 1, test + start + i, ignoreCase) >= 0)) - { + if (wc == '*' && (w[1] == 0 || indexOfMatch (w + 1, t, ignoreCase) >= 0)) return start; - } break; } } ++start; + ++test; } return -1; @@ -12986,26 +12376,27 @@ namespace WildCardHelpers bool String::matchesWildcard (const String& wildcard, const bool ignoreCase) const throw() { - int i = 0; + CharPointer_UTF32 w (wildcard.text); + CharPointer_UTF32 t (text); for (;;) { - const juce_wchar wc = wildcard.text [i]; - const juce_wchar c = text [i]; + const juce_wchar wc = *w; + const juce_wchar tc = *t; - if (wc == c - || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) - || (wc == '?' && c != 0)) + if (wc == tc + || (ignoreCase && w.toLowerCase() == t.toLowerCase()) + || (wc == '?' && tc != 0)) { if (wc == 0) return true; - ++i; + ++w; + ++t; } else { - return wc == '*' && (wildcard [i + 1] == 0 - || WildCardHelpers::indexOfMatch (wildcard.text + i + 1, text + i, ignoreCase) >= 0); + return wc == '*' && (w[1] == 0 || WildCardHelpers::indexOfMatch (w + 1, t, ignoreCase) >= 0); } } } @@ -13181,12 +12572,12 @@ const String String::replaceCharacters (const String& charactersToReplace, bool String::startsWith (const String& other) const throw() { - return CharacterFunctions::compare (text, other.text, other.length()) == 0; + return CharPointer_UTF32 (text).compareUpTo (CharPointer_UTF32 (other.text), other.length()) == 0; } bool String::startsWithIgnoreCase (const String& other) const throw() { - return CharacterFunctions::compareIgnoreCase (text, other.text, other.length()) == 0; + return CharPointer_UTF32 (text).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), other.length()) == 0; } bool String::startsWithChar (const juce_wchar character) const throw() @@ -13210,7 +12601,7 @@ bool String::endsWith (const String& other) const throw() const int otherLen = other.length(); return thisLen >= otherLen - && CharacterFunctions::compare (text + thisLen - otherLen, other.text) == 0; + && CharPointer_UTF32 (text + thisLen - otherLen).compare (CharPointer_UTF32 (other.text)) == 0; } bool String::endsWithIgnoreCase (const String& other) const throw() @@ -13219,20 +12610,48 @@ bool String::endsWithIgnoreCase (const String& other) const throw() const int otherLen = other.length(); return thisLen >= otherLen - && CharacterFunctions::compareIgnoreCase (text + thisLen - otherLen, other.text) == 0; + && CharPointer_UTF32 (text + thisLen - otherLen).compareIgnoreCase (CharPointer_UTF32 (other.text)) == 0; } const String String::toUpperCase() const { - String result (*this, size_t()); - CharacterFunctions::toUpperCase (result.text); + String result (Preallocation (this->length())); + + CharPointer_UTF32 dest (result.text); + CharPointer_UTF32 src (text); + + for (;;) + { + const juce_wchar c = src.toUpperCase(); + dest.write (c); + + if (c == 0) + break; + + ++src; + } + return result; } const String String::toLowerCase() const { - String result (*this, size_t()); - CharacterFunctions::toLowerCase (result.text); + String result (Preallocation (this->length())); + + CharPointer_UTF32 dest (result.text); + CharPointer_UTF32 src (text); + + for (;;) + { + const juce_wchar c = src.toLowerCase(); + dest.write (c); + + if (c == 0) + break; + + ++src; + } + return result; } @@ -13388,13 +12807,13 @@ const String String::trim() const int start = 0; - while (CharacterFunctions::isWhitespace (text [start])) + while (CharPointer_UTF32 (text + start).isWhitespace()) ++start; const int len = length(); int end = len - 1; - while ((end >= start) && CharacterFunctions::isWhitespace (text [end])) + while ((end >= start) && CharPointer_UTF32 (text + end).isWhitespace()) --end; ++end; @@ -13412,15 +12831,15 @@ const String String::trimStart() const if (isEmpty()) return empty; - const juce_wchar* t = text; + CharPointer_UTF32 t (text); - while (CharacterFunctions::isWhitespace (*t)) + while (t.isWhitespace()) ++t; - if (t == text) + if (t.getAddress() == text) return *this; - return String (t); + return String (t.getAddress()); } const String String::trimEnd() const @@ -13428,12 +12847,13 @@ const String String::trimEnd() const if (isEmpty()) return empty; - const juce_wchar* endT = text + (length() - 1); + CharPointer_UTF32 endT (text); + endT = endT.findTerminatingNull() - 1; - while ((endT >= text) && CharacterFunctions::isWhitespace (*endT)) + while ((endT.getAddress() >= text) && endT.isWhitespace()) --endT; - return String (text, (int) (++endT - text)); + return String (text, 1 + (int) (endT.getAddress() - text)); } const String String::trimCharactersAtStart (const String& charactersToTrim) const @@ -13539,10 +12959,10 @@ const String String::initialSectionNotContaining (const String& charactersToStop bool String::containsOnly (const String& chars) const throw() { - const juce_wchar* t = text; + CharPointer_UTF32 t (text); - while (*t != 0) - if (! chars.containsChar (*t++)) + while (! t.isEmpty()) + if (! chars.containsChar (t.getAndAdvance())) return false; return true; @@ -13561,12 +12981,16 @@ bool String::containsAnyOf (const String& chars) const throw() bool String::containsNonWhitespaceChars() const throw() { - const juce_wchar* t = text; + CharPointer_UTF32 t (text); - while (*t != 0) - if (! CharacterFunctions::isWhitespace (*t++)) + while (! t.isEmpty()) + { + if (! t.isWhitespace()) return true; + ++t; + } + return false; } @@ -13597,6 +13021,11 @@ const String String::formatted (const juce_wchar* const pf, ... ) #if JUCE_MSVC #pragma warning (pop) #endif +#elif JUCE_ANDROID + HeapBlock temp (bufferSize); + const int num = (int) vsnprintf (temp.getData(), bufferSize - 1, String (pf).toUTF8(), args); + if (num > 0) + CharPointer_UTF32 (result.text).copyAndAdvance (CharPointer_UTF8 (temp.getData())); #else const int num = (int) vswprintf (result.text, bufferSize - 1, pf, args); #endif @@ -13617,28 +13046,27 @@ const String String::formatted (const juce_wchar* const pf, ... ) int String::getIntValue() const throw() { - return CharacterFunctions::getIntValue (text); + return CharPointer_UTF32 (text).getIntValue32(); } int String::getTrailingIntValue() const throw() { int n = 0; int mult = 1; - const juce_wchar* t = text + length(); + CharPointer_UTF32 t (text); + t = t.findTerminatingNull(); - while (--t >= text) + while ((--t).getAddress() >= text) { - const juce_wchar c = *t; - - if (! CharacterFunctions::isDigit (c)) + if (! t.isDigit()) { - if (c == '-') + if (*t == '-') n = -n; break; } - n += mult * (c - '0'); + n += mult * (*t - '0'); mult *= 10; } @@ -13647,20 +13075,20 @@ int String::getTrailingIntValue() const throw() int64 String::getLargeIntValue() const throw() { - return CharacterFunctions::getInt64Value (text); + return CharPointer_UTF32 (text).getIntValue64(); } float String::getFloatValue() const throw() { - return (float) CharacterFunctions::getDoubleValue (text); + return (float) getDoubleValue(); } double String::getDoubleValue() const throw() { - return CharacterFunctions::getDoubleValue (text); + return CharPointer_UTF32 (text).getDoubleValue(); } -static const juce_wchar* const hexDigits = JUCE_T("0123456789abcdef"); +static const char* const hexDigits = "0123456789abcdef"; const String String::toHexString (const int number) { @@ -13672,7 +13100,7 @@ const String String::toHexString (const int number) do { - *--t = hexDigits [v & 15]; + *--t = (juce_wchar) hexDigits [v & 15]; v >>= 4; } while (v != 0); @@ -13690,7 +13118,7 @@ const String String::toHexString (const int64 number) do { - *--t = hexDigits [(int) (v & 15)]; + *--t = (juce_wchar) hexDigits [(int) (v & 15)]; v >>= 4; } while (v != 0); @@ -13718,8 +13146,8 @@ const String String::toHexString (const unsigned char* data, const int size, con for (int i = 0; i < size; ++i) { - *d++ = hexDigits [(*data) >> 4]; - *d++ = hexDigits [(*data) & 0xf]; + *d++ = (juce_wchar) hexDigits [(*data) >> 4]; + *d++ = (juce_wchar) hexDigits [(*data) & 0xf]; ++data; if (groupSize > 0 && (i % groupSize) == (groupSize - 1) && i < (size - 1)) @@ -13733,18 +13161,14 @@ const String String::toHexString (const unsigned char* data, const int size, con int String::getHexValue32() const throw() { int result = 0; - const juce_wchar* c = text; + CharPointer_UTF32 t (text); - for (;;) + while (! t.isEmpty()) { - const int hexValue = CharacterFunctions::getHexDigitValue (*c); + const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (hexValue >= 0) result = (result << 4) | hexValue; - else if (*c == 0) - break; - - ++c; } return result; @@ -13753,18 +13177,14 @@ int String::getHexValue32() const throw() int64 String::getHexValue64() const throw() { int64 result = 0; - const juce_wchar* c = text; + CharPointer_UTF32 t (text); - for (;;) + while (! t.isEmpty()) { - const int hexValue = CharacterFunctions::getHexDigitValue (*c); + const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (hexValue >= 0) result = (result << 4) | hexValue; - else if (*c == 0) - break; - - ++c; } return result; @@ -13817,123 +13237,42 @@ const String String::createStringFromData (const void* const data_, const int si } } +void* String::createSpaceAtEndOfBuffer (const size_t numExtraBytes) const +{ + const int currentLen = length() + 1; + + String& mutableThis = const_cast (*this); + mutableThis.text = StringHolder::makeUniqueWithSize (mutableThis.text, currentLen + 1 + numExtraBytes / sizeof (juce_wchar)); + + return mutableThis.text + currentLen; +} + const char* String::toUTF8() const { if (isEmpty()) - { return reinterpret_cast (text); - } - else - { - const int currentLen = length() + 1; - const int utf8BytesNeeded = getNumBytesAsUTF8(); - - String* const mutableThis = const_cast (this); - mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar)); - char* const otherCopy = reinterpret_cast (mutableThis->text + currentLen); + const int utf8BytesNeeded = getNumBytesAsUTF8(); + char* const utf8Area = static_cast (createSpaceAtEndOfBuffer (utf8BytesNeeded)); -#if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) - *(juce_wchar*) (otherCopy + (utf8BytesNeeded & ~(sizeof (juce_wchar) - 1))) = 0; -#endif - copyToUTF8 (otherCopy, std::numeric_limits::max()); + #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) + *(juce_wchar*) (utf8Area + (utf8BytesNeeded & ~(sizeof (juce_wchar) - 1))) = 0; + #endif - return otherCopy; - } + copyToUTF8 (utf8Area, std::numeric_limits::max()); + return utf8Area; } int String::copyToUTF8 (char* const buffer, const int maxBufferSizeBytes) const throw() { jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! - int num = 0, index = 0; - - for (;;) - { - const uint32 c = (uint32) text [index++]; - - if (c >= 0x80) - { - int numExtraBytes = 1; - - if (c >= 0x800) - { - ++numExtraBytes; - - if (c >= 0x10000) - ++numExtraBytes; - } - - if (buffer != 0) - { - if (num + numExtraBytes >= maxBufferSizeBytes) - { - buffer [num++] = 0; - break; - } - else - { - buffer [num++] = (uint8) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6))); - - while (--numExtraBytes >= 0) - buffer [num++] = (uint8) (0x80 | (0x3f & (c >> (numExtraBytes * 6)))); - } - } - else - { - num += numExtraBytes + 1; - } - } - else - { - if (buffer != 0) - { - if (num + 1 >= maxBufferSizeBytes) - { - buffer [num++] = 0; - break; - } - - buffer [num] = (uint8) c; - } - - ++num; - } - - if (c == 0) - break; - } - - return num; + return CharPointer_UTF8 (buffer).copyAndAdvanceUpToBytes (CharPointer_UTF32 (text), maxBufferSizeBytes); } int String::getNumBytesAsUTF8() const throw() { - int num = 0; - const juce_wchar* t = text; - - for (;;) - { - const uint32 c = (uint32) *t; - - if (c >= 0x80) - { - ++num; - if (c >= 0x800) - { - ++num; - if (c >= 0x10000) - ++num; - } - } - else if (c == 0) - break; - - ++num; - ++t; - } - - return num; + return CharPointer_UTF8::getBytesRequiredFor (CharPointer_UTF32 (text)); } const String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) @@ -14001,41 +13340,41 @@ const String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) const char* String::toCString() const { + #if JUCE_ANDROID + return toUTF8(); + #else if (isEmpty()) - { return reinterpret_cast (text); - } - else - { - const int len = length(); - String* const mutableThis = const_cast (this); - mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, (len + 1) * 2); - char* otherCopy = reinterpret_cast (mutableThis->text + len + 1); - CharacterFunctions::copy (otherCopy, text, len); - otherCopy [len] = 0; - return otherCopy; - } + const int len = getNumBytesAsCString(); + char* const extraSpace = static_cast (createSpaceAtEndOfBuffer (len + 1)); + wcstombs (extraSpace, text, len); + extraSpace [len] = 0; + return extraSpace; + #endif } -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514 4996) -#endif - int String::getNumBytesAsCString() const throw() { + #if JUCE_ANDROID + return getNumBytesAsUTF8(); + #else return (int) wcstombs (0, text, 0); + #endif } int String::copyToCString (char* destBuffer, const int maxBufferSizeBytes) const throw() { + #if JUCE_ANDROID + return copyToUTF8 (destBuffer, maxBufferSizeBytes); + #else const int numBytes = (int) wcstombs (destBuffer, text, maxBufferSizeBytes); if (destBuffer != 0 && numBytes >= 0) destBuffer [numBytes] = 0; return numBytes; + #endif } #if JUCE_MSVC @@ -14105,9 +13444,12 @@ public: expect (s1.startsWithIgnoreCase ("aB") && s1.endsWithIgnoreCase ("CD")); expect (s1.endsWith ("bcd") && ! s1.endsWith ("aabcd")); expect (s1.indexOf (String::empty) == 0); + expect (s1.indexOfIgnoreCase (String::empty) == 0); expect (s1.startsWith (String::empty) && s1.endsWith (String::empty) && s1.contains (String::empty)); expect (s1.contains ("cd") && s1.contains ("ab") && s1.contains ("abcd")); - expect (s1.containsChar ('a') && ! s1.containsChar (0)); + expect (s1.containsChar ('a')); + expect (! s1.containsChar ('x')); + expect (! s1.containsChar (0)); expect (String ("abc foo bar").containsWholeWord ("abc") && String ("abc foo bar").containsWholeWord ("abc")); } @@ -14136,7 +13478,7 @@ public: expect (String ("45454545x").lastIndexOf (L"45") == 6); expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7); expect (String ("45454545x").lastIndexOfAnyOf (L"456x") == 8); - expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("Ab") == 6); + expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("aB") == 6); expect (s.indexOfChar (L'4') == 4); expect (s + s == "012345678012345678"); expect (s.startsWith (s)); @@ -14228,6 +13570,7 @@ public: expect (! s5.containsWholeWordIgnoreCase (L"Wordx")); expect (!s5.containsWholeWordIgnoreCase ("xWord2")); expect (s5.containsNonWhitespaceChars()); + expect (s5.containsOnly ("ordw23 ")); expect (! String (" \n\r\t").containsNonWhitespaceChars()); expect (s5.matchesWildcard (L"wor*", false)); @@ -14236,50 +13579,50 @@ public: expect (s5.matchesWildcard ("*word?", true)); expect (s5.matchesWildcard (L"Word*3", true)); - expect (s5.fromFirstOccurrenceOf (String::empty, true, false) == s5); - expect (s5.fromFirstOccurrenceOf ("xword2", true, false) == s5.substring (100)); - expect (s5.fromFirstOccurrenceOf (L"word2", true, false) == s5.substring (5)); - expect (s5.fromFirstOccurrenceOf ("Word2", true, true) == s5.substring (5)); - expect (s5.fromFirstOccurrenceOf ("word2", false, false) == s5.getLastCharacters (6)); - expect (s5.fromFirstOccurrenceOf (L"Word2", false, true) == s5.getLastCharacters (6)); + expectEquals (s5.fromFirstOccurrenceOf (String::empty, true, false), s5); + expectEquals (s5.fromFirstOccurrenceOf ("xword2", true, false), s5.substring (100)); + expectEquals (s5.fromFirstOccurrenceOf (L"word2", true, false), s5.substring (5)); + expectEquals (s5.fromFirstOccurrenceOf ("Word2", true, true), s5.substring (5)); + expectEquals (s5.fromFirstOccurrenceOf ("word2", false, false), s5.getLastCharacters (6)); + expectEquals (s5.fromFirstOccurrenceOf (L"Word2", false, true), s5.getLastCharacters (6)); - expect (s5.fromLastOccurrenceOf (String::empty, true, false) == s5); - expect (s5.fromLastOccurrenceOf (L"wordx", true, false) == s5); - expect (s5.fromLastOccurrenceOf ("word", true, false) == s5.getLastCharacters (5)); - expect (s5.fromLastOccurrenceOf (L"worD", true, true) == s5.getLastCharacters (5)); - expect (s5.fromLastOccurrenceOf ("word", false, false) == s5.getLastCharacters (1)); - expect (s5.fromLastOccurrenceOf (L"worD", false, true) == s5.getLastCharacters (1)); + expectEquals (s5.fromLastOccurrenceOf (String::empty, true, false), s5); + expectEquals (s5.fromLastOccurrenceOf (L"wordx", true, false), s5); + expectEquals (s5.fromLastOccurrenceOf ("word", true, false), s5.getLastCharacters (5)); + expectEquals (s5.fromLastOccurrenceOf (L"worD", true, true), s5.getLastCharacters (5)); + expectEquals (s5.fromLastOccurrenceOf ("word", false, false), s5.getLastCharacters (1)); + expectEquals (s5.fromLastOccurrenceOf (L"worD", false, true), s5.getLastCharacters (1)); expect (s5.upToFirstOccurrenceOf (String::empty, true, false).isEmpty()); - expect (s5.upToFirstOccurrenceOf ("word4", true, false) == s5); - expect (s5.upToFirstOccurrenceOf (L"word2", true, false) == s5.substring (0, 10)); - expect (s5.upToFirstOccurrenceOf ("Word2", true, true) == s5.substring (0, 10)); - expect (s5.upToFirstOccurrenceOf (L"word2", false, false) == s5.substring (0, 5)); - expect (s5.upToFirstOccurrenceOf ("Word2", false, true) == s5.substring (0, 5)); - - expect (s5.upToLastOccurrenceOf (String::empty, true, false) == s5); - expect (s5.upToLastOccurrenceOf ("zword", true, false) == s5); - expect (s5.upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); - expect (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); - expect (s5.upToLastOccurrenceOf ("Word", true, true) == s5.dropLastCharacters (1)); - expect (s5.upToLastOccurrenceOf ("word", false, false) == s5.dropLastCharacters (5)); - expect (s5.upToLastOccurrenceOf ("Word", false, true) == s5.dropLastCharacters (5)); - - expect (s5.replace ("word", L"xyz", false) == String ("xyz xyz2 xyz3")); + expectEquals (s5.upToFirstOccurrenceOf ("word4", true, false), s5); + expectEquals (s5.upToFirstOccurrenceOf (L"word2", true, false), s5.substring (0, 10)); + expectEquals (s5.upToFirstOccurrenceOf ("Word2", true, true), s5.substring (0, 10)); + expectEquals (s5.upToFirstOccurrenceOf (L"word2", false, false), s5.substring (0, 5)); + expectEquals (s5.upToFirstOccurrenceOf ("Word2", false, true), s5.substring (0, 5)); + + expectEquals (s5.upToLastOccurrenceOf (String::empty, true, false), s5); + expectEquals (s5.upToLastOccurrenceOf ("zword", true, false), s5); + expectEquals (s5.upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); + expectEquals (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); + expectEquals (s5.upToLastOccurrenceOf ("Word", true, true), s5.dropLastCharacters (1)); + expectEquals (s5.upToLastOccurrenceOf ("word", false, false), s5.dropLastCharacters (5)); + expectEquals (s5.upToLastOccurrenceOf ("Word", false, true), s5.dropLastCharacters (5)); + + expectEquals (s5.replace ("word", L"xyz", false), String ("xyz xyz2 xyz3")); expect (s5.replace (L"Word", "xyz", true) == "xyz xyz2 xyz3"); expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz"); expect (s5.replace ("Word", "", true) == " 2 3"); - expect (s5.replace ("Word2", L"xyz", true) == String ("word xyz word3")); + expectEquals (s5.replace ("Word2", L"xyz", true), String ("word xyz word3")); expect (s5.replaceCharacter (L'w', 'x') != s5); - expect (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w') == s5); + expectEquals (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w'), s5); expect (s5.replaceCharacters ("wo", "xy") != s5); - expect (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo") == s5); - expect (s5.retainCharacters ("1wordxya") == String ("wordwordword")); + expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo"), s5); + expectEquals (s5.retainCharacters ("1wordxya"), String ("wordwordword")); expect (s5.retainCharacters (String::empty).isEmpty()); expect (s5.removeCharacters ("1wordxya") == " 2 3"); - expect (s5.removeCharacters (String::empty) == s5); + expectEquals (s5.removeCharacters (String::empty), s5); expect (s5.initialSectionContainingOnly ("word") == L"word"); - expect (s5.initialSectionNotContaining (String ("xyz ")) == String ("word")); + expectEquals (s5.initialSectionNotContaining (String ("xyz ")), String ("word")); expect (! s5.isQuotedString()); expect (s5.quoted().isQuotedString()); expect (! s5.quoted().unquoted().isQuotedString()); @@ -14287,14 +13630,14 @@ public: expect (String ("'x").isQuotedString()); String s6 (" \t xyz \t\r\n"); - expect (s6.trim() == String ("xyz")); + expectEquals (s6.trim(), String ("xyz")); expect (s6.trim().trim() == "xyz"); - expect (s5.trim() == s5); - expect (s6.trimStart().trimEnd() == s6.trim()); - expect (s6.trimStart().trimEnd() == s6.trimEnd().trimStart()); - expect (s6.trimStart().trimStart().trimEnd().trimEnd() == s6.trimEnd().trimStart()); + expectEquals (s5.trim(), s5); + expectEquals (s6.trimStart().trimEnd(), s6.trim()); + expectEquals (s6.trimStart().trimEnd(), s6.trimEnd().trimStart()); + expectEquals (s6.trimStart().trimStart().trimEnd().trimEnd(), s6.trimEnd().trimStart()); expect (s6.trimStart() != s6.trimEnd()); - expect (("\t\r\n " + s6 + "\t\n \r").trim() == s6.trim()); + expectEquals (("\t\r\n " + s6 + "\t\n \r").trim(), s6.trim()); expect (String::repeatedString ("xyz", 3) == L"xyzxyzxyz"); } @@ -14307,12 +13650,12 @@ public: char buffer [100]; memset (buffer, 0xff, sizeof (buffer)); s.copyToUTF8 (buffer, 100); - expect (String::fromUTF8 (buffer, 100) == s); + expectEquals (String::fromUTF8 (buffer, 100), s); juce_wchar bufferUnicode [100]; memset (bufferUnicode, 0xff, sizeof (bufferUnicode)); s.copyToUnicode (bufferUnicode, 100); - expect (String (bufferUnicode, 100) == s); + expectEquals (String (bufferUnicode, 100), s); } { @@ -14776,11 +14119,13 @@ void StringArray::appendNumbersToDuplicates (const bool ignoreCase, const juce_wchar* preNumberString, const juce_wchar* postNumberString) { + String defaultPre (" ("), defaultPost (")"); // (these aren't literals because of non-unicode literals on Android) + if (preNumberString == 0) - preNumberString = L" ("; + preNumberString = defaultPre; if (postNumberString == 0) - postNumberString = L")"; + postNumberString = defaultPost; for (int i = 0; i < size() - 1; ++i) { @@ -15035,12 +14380,14 @@ BEGIN_JUCE_NAMESPACE XmlDocument::XmlDocument (const String& documentText) : originalText (documentText), + input (0), ignoreEmptyTextElements (true) { } XmlDocument::XmlDocument (const File& file) - : ignoreEmptyTextElements (true), + : input (0), + ignoreEmptyTextElements (true), inputSource (new FileInputSource (file)) { } @@ -15122,7 +14469,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle } } - input = textToParse; + input = static_cast (textToParse); lastError = String::empty; errorOccurred = false; outOfData = false; @@ -15136,7 +14483,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle { skipHeader(); - if (input != 0) + if (input.getAddress() != 0) { ScopedPointer result (readNextElement (! onlyReadOuterDocumentElement)); @@ -15198,18 +14545,16 @@ int XmlDocument::findNextTokenLength() throw() void XmlDocument::skipHeader() { - const juce_wchar* const found = CharacterFunctions::find (input, JUCE_T("= 0) { - input = found; - input = CharacterFunctions::find (input, JUCE_T("?>")); - - if (input == 0) + const int headerEnd = (input + headerStart).indexOf (CharPointer_UTF8 ("?>")); + if (headerEnd < 0) return; #if JUCE_DEBUG - const String header (found, input - found); + const String header ((input + headerStart).getAddress(), headerEnd - headerStart); const String encoding (header.fromFirstOccurrenceOf ("encoding", false, true) .fromFirstOccurrenceOf ("=", false, false) .fromFirstOccurrenceOf ("\"", false, false) @@ -15225,16 +14570,17 @@ void XmlDocument::skipHeader() jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-")); #endif - input += 2; + input += headerEnd + 2; } skipNextWhiteSpace(); - const juce_wchar* docType = CharacterFunctions::find (input, JUCE_T("")); + const int closeComment = input.indexOf (CharPointer_UTF8 ("-->")); - if (closeComment == 0) + if (closeComment < 0) { outOfData = true; break; } - input = closeComment + 3; + input += closeComment + 3; continue; } else if (input[1] == '?') { - const juce_wchar* const closeBracket = CharacterFunctions::find (input, JUCE_T("?>")); + const int closeBracket = input.indexOf (CharPointer_UTF8 ("?>")); - if (closeBracket == 0) + if (closeBracket < 0) { outOfData = true; break; } - input = closeBracket + 2; + input += closeBracket + 2; continue; } } @@ -15324,7 +14669,7 @@ void XmlDocument::readQuotedString (String& result) else { --input; - const juce_wchar* const start = input; + const CharPointer_UTF32 start (input); for (;;) { @@ -15332,14 +14677,14 @@ void XmlDocument::readQuotedString (String& result) if (character == quote) { - result.append (start, (int) (input - start)); + result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); ++input; return; } else if (character == '&') { - result.append (start, (int) (input - start)); + result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); break; } else if (character == 0) @@ -15363,11 +14708,11 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (outOfData) return 0; - input = CharacterFunctions::find (input, JUCE_T("<")); + const int openBracket = input.indexOf ((juce_wchar) '<'); - if (input != 0) + if (openBracket >= 0) { - ++input; + input += openBracket + 1; int tagLen = findNextTokenLength(); if (tagLen == 0) @@ -15383,7 +14728,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) } } - node = new XmlElement (String (input, tagLen)); + node = new XmlElement (String (input.getAddress(), tagLen)); input += tagLen; LinkedListPointer::Appender attributeAppender (node->attributes); @@ -15419,7 +14764,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (attNameLen > 0) { - const juce_wchar* attNameStart = input; + const CharPointer_UTF32 attNameStart (input); input += attNameLen; skipNextWhiteSpace(); @@ -15433,7 +14778,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (nextChar == '"' || nextChar == '\'') { XmlElement::XmlAttributeNode* const newAtt - = new XmlElement::XmlAttributeNode (String (attNameStart, attNameLen), + = new XmlElement::XmlAttributeNode (String (attNameStart.getAddress(), attNameLen), String::empty); readQuotedString (newAtt->value); @@ -15462,7 +14807,7 @@ void XmlDocument::readChildElements (XmlElement* parent) for (;;) { - const juce_wchar* const preWhitespaceInput = input; + const CharPointer_UTF32 preWhitespaceInput (input); skipNextWhiteSpace(); if (outOfData) @@ -15476,8 +14821,11 @@ void XmlDocument::readChildElements (XmlElement* parent) if (input[1] == '/') { // our close tag.. - input = CharacterFunctions::find (input, JUCE_T(">")); - ++input; + const int closeTag = input.indexOf ((juce_wchar) '>'); + + if (closeTag >= 0) + input += closeTag + 1; + break; } else if (input[1] == '!' @@ -15490,7 +14838,7 @@ void XmlDocument::readChildElements (XmlElement* parent) && input[8] == '[') { input += 9; - const juce_wchar* const inputStart = input; + const CharPointer_UTF32 inputStart (input); int len = 0; @@ -15514,7 +14862,7 @@ void XmlDocument::readChildElements (XmlElement* parent) ++len; } - childAppender.append (XmlElement::createTextElement (String (inputStart, len))); + childAppender.append (XmlElement::createTextElement (String (inputStart.getAddress(), len))); } else { @@ -15553,10 +14901,10 @@ void XmlDocument::readChildElements (XmlElement* parent) if (entity.startsWithChar ('<') && entity [1] != 0) { - const juce_wchar* const oldInput = input; + const CharPointer_UTF32 oldInput (input); const bool oldOutOfData = outOfData; - input = entity; + input = static_cast (entity); outOfData = false; for (;;) @@ -15579,7 +14927,7 @@ void XmlDocument::readChildElements (XmlElement* parent) } else { - const juce_wchar* start = input; + const CharPointer_UTF32 start (input); int len = 0; for (;;) @@ -15601,7 +14949,7 @@ void XmlDocument::readChildElements (XmlElement* parent) ++len; } - textElementContent.append (start, len); + textElementContent.append (start.getAddress(), len); } } @@ -15618,27 +14966,27 @@ void XmlDocument::readEntity (String& result) // skip over the ampersand ++input; - if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("amp;"), 4) == 0) + if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("amp;"), 4) == 0) { input += 4; result += '&'; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("quot;"), 5) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("quot;"), 5) == 0) { input += 5; result += '"'; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("apos;"), 5) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("apos;"), 5) == 0) { input += 5; result += '\''; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("lt;"), 3) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("lt;"), 3) == 0) { input += 3; result += '<'; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("gt;"), 3) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("gt;"), 3) == 0) { input += 3; result += '>'; @@ -15698,20 +15046,19 @@ void XmlDocument::readEntity (String& result) } else { - const juce_wchar* const entityNameStart = input; - const juce_wchar* const closingSemiColon = CharacterFunctions::find (input, JUCE_T(";")); + const CharPointer_UTF32 entityNameStart (input); + const int closingSemiColon = input.indexOf ((juce_wchar) ';'); - if (closingSemiColon == 0) + if (closingSemiColon < 0) { outOfData = true; result += '&'; } else { - input = closingSemiColon + 1; + input += closingSemiColon + 1; - result += expandExternalEntity (String (entityNameStart, - (int) (closingSemiColon - entityNameStart))); + result += expandExternalEntity (String (entityNameStart.getAddress(), closingSemiColon)); } } } @@ -16594,7 +15941,7 @@ bool XmlElement::isTextElement() const throw() return tagName.isEmpty(); } -static const juce_wchar* const juce_xmltextContentAttributeName = L"text"; +static const String juce_xmltextContentAttributeName ("text"); const String& XmlElement::getText() const throw() { @@ -20062,21 +19409,17 @@ const File PropertiesFile::getDefaultAppSettingsFile (const String& applicationN // mustn't have illegal characters in this name.. jassert (applicationName == File::createLegalFileName (applicationName)); -#if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS File dir (commonToAllUsers ? "/Library/Preferences" : "~/Library/Preferences"); if (folderName.isNotEmpty()) dir = dir.getChildFile (folderName); -#endif - -#ifdef JUCE_LINUX + #elif JUCE_LINUX || JUCE_ANDROID const File dir ((commonToAllUsers ? "/var/" : "~/") + (folderName.isNotEmpty() ? folderName : ("." + applicationName))); -#endif - -#if JUCE_WINDOWS + #elif JUCE_WINDOWS File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory : File::userApplicationDataDirectory)); @@ -20085,8 +19428,7 @@ const File PropertiesFile::getDefaultAppSettingsFile (const String& applicationN dir = dir.getChildFile (folderName.isNotEmpty() ? folderName : applicationName); - -#endif + #endif return dir.getChildFile (applicationName) .withFileExtension (fileNameSuffix); @@ -26281,7 +25623,7 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, if (name.isEmpty()) { midiCallbacks.add (callbackToAdd); - midiCallbackDevices.add (0); + midiCallbackDevices.add (String::empty); } else { @@ -26291,7 +25633,7 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, { const ScopedLock sl (midiCallbackLock); midiCallbacks.add (callbackToAdd); - midiCallbackDevices.add (enabledMidiInputs[i]); + midiCallbackDevices.add (enabledMidiInputs[i]->getName()); break; } } @@ -26304,12 +25646,7 @@ void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputC for (int i = midiCallbacks.size(); --i >= 0;) { - String devName; - - if (midiCallbackDevices.getUnchecked(i) != 0) - devName = midiCallbackDevices.getUnchecked(i)->getName(); - - if (devName == name) + if (midiCallbackDevices[i] == name) { midiCallbacks.remove (i); midiCallbackDevices.remove (i); @@ -26328,9 +25665,9 @@ void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, for (int i = midiCallbackDevices.size(); --i >= 0;) { - MidiInput* const md = midiCallbackDevices.getUnchecked(i); + const String name (midiCallbackDevices[i]); - if (md == source || (md == 0 && isDefaultSource)) + if ((isDefaultSource && name.isEmpty()) || (name.isNotEmpty() && name == source->getName())) midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message); } } @@ -47133,33 +46470,33 @@ namespace CppTokeniser bool isReservedKeyword (const juce_wchar* const token, const int tokenLength) throw() { - static const juce_wchar* const keywords2Char[] = - { JUCE_T("if"), JUCE_T("do"), JUCE_T("or"), JUCE_T("id"), 0 }; + static const char* const keywords2Char[] = + { "if", "do", "or", "id", 0 }; - static const juce_wchar* const keywords3Char[] = - { JUCE_T("for"), JUCE_T("int"), JUCE_T("new"), JUCE_T("try"), JUCE_T("xor"), JUCE_T("and"), JUCE_T("asm"), JUCE_T("not"), 0 }; + static const char* const keywords3Char[] = + { "for", "int", "new", "try", "xor", "and", "asm", "not", 0 }; - static const juce_wchar* const keywords4Char[] = - { JUCE_T("bool"), JUCE_T("void"), JUCE_T("this"), JUCE_T("true"), JUCE_T("long"), JUCE_T("else"), JUCE_T("char"), - JUCE_T("enum"), JUCE_T("case"), JUCE_T("goto"), JUCE_T("auto"), 0 }; + static const char* const keywords4Char[] = + { "bool", "void", "this", "true", "long", "else", "char", + "enum", "case", "goto", "auto", 0 }; - static const juce_wchar* const keywords5Char[] = - { JUCE_T("while"), JUCE_T("bitor"), JUCE_T("break"), JUCE_T("catch"), JUCE_T("class"), JUCE_T("compl"), JUCE_T("const"), JUCE_T("false"), - JUCE_T("float"), JUCE_T("short"), JUCE_T("throw"), JUCE_T("union"), JUCE_T("using"), JUCE_T("or_eq"), 0 }; + static const char* const keywords5Char[] = + { "while", "bitor", "break", "catch", "class", "compl", "const", "false", + "float", "short", "throw", "union", "using", "or_eq", 0 }; - static const juce_wchar* const keywords6Char[] = - { JUCE_T("return"), JUCE_T("struct"), JUCE_T("and_eq"), JUCE_T("bitand"), JUCE_T("delete"), JUCE_T("double"), JUCE_T("extern"), - JUCE_T("friend"), JUCE_T("inline"), JUCE_T("not_eq"), JUCE_T("public"), JUCE_T("sizeof"), JUCE_T("static"), JUCE_T("signed"), - JUCE_T("switch"), JUCE_T("typeid"), JUCE_T("wchar_t"), JUCE_T("xor_eq"), 0}; + static const char* const keywords6Char[] = + { "return", "struct", "and_eq", "bitand", "delete", "double", "extern", + "friend", "inline", "not_eq", "public", "sizeof", "static", "signed", + "switch", "typeid", "wchar_t", "xor_eq", 0}; - static const juce_wchar* const keywordsOther[] = - { JUCE_T("const_cast"), JUCE_T("continue"), JUCE_T("default"), JUCE_T("explicit"), JUCE_T("mutable"), JUCE_T("namespace"), - JUCE_T("operator"), JUCE_T("private"), JUCE_T("protected"), JUCE_T("register"), JUCE_T("reinterpret_cast"), JUCE_T("static_cast"), - JUCE_T("template"), JUCE_T("typedef"), JUCE_T("typename"), JUCE_T("unsigned"), JUCE_T("virtual"), JUCE_T("volatile"), - JUCE_T("@implementation"), JUCE_T("@interface"), JUCE_T("@end"), JUCE_T("@synthesize"), JUCE_T("@dynamic"), JUCE_T("@public"), - JUCE_T("@private"), JUCE_T("@property"), JUCE_T("@protected"), JUCE_T("@class"), 0 }; + static const char* const keywordsOther[] = + { "const_cast", "continue", "default", "explicit", "mutable", "namespace", + "operator", "private", "protected", "register", "reinterpret_cast", "static_cast", + "template", "typedef", "typename", "unsigned", "virtual", "volatile", + "@implementation", "@interface", "@end", "@synthesize", "@dynamic", "@public", + "@private", "@property", "@protected", "@class", 0 }; - const juce_wchar* const* k; + const char* const* k; switch (tokenLength) { @@ -47180,7 +46517,7 @@ namespace CppTokeniser int i = 0; while (k[i] != 0) { - if (k[i][0] == token[0] && CharacterFunctions::compare (k[i], token) == 0) + if (k[i][0] == token[0] && CharPointer_UTF8 (k[i]).compare (CharPointer_UTF32 (token)) == 0) return true; ++i; @@ -49102,7 +48439,7 @@ void ListBox::updateContent() bool selectionChanged = false; - if (selected [selected.size() - 1] >= totalItems) + if (selected.size() > 0 && selected [selected.size() - 1] >= totalItems) { selected.removeRange (Range (totalItems, std::numeric_limits::max())); lastRowSelected = getSelectedRow (0); @@ -244768,7 +244105,7 @@ void FileChooser::showPlatformDialog (Array& results, const String& title, while (*filename != 0) { results.add (File (String (files) + "\\" + String (filename))); - filename += CharacterFunctions::length (filename) + 1; + filename += wcslen (filename) + 1; } } else if (files[0] != 0) @@ -244824,11 +244161,11 @@ const String SystemClipboard::getTextFromClipboard() if (bufH != 0) { - const wchar_t* const data = (const wchar_t*) GlobalLock (bufH); + const WCHAR* const data = (const WCHAR*) GlobalLock (bufH); if (data != 0) { - result = String (data, (int) (GlobalSize (bufH) / sizeof (wchar_t))); + result = String (data, (int) (GlobalSize (bufH) / sizeof (WCHAR))); GlobalUnlock (bufH); } @@ -245516,7 +244853,7 @@ static Handle createHandleDataRef (Handle dataHandle, const char* fileName) { Str255 suffix; - CharacterFunctions::copy ((char*) suffix, fileName, 128); + strncpy ((char*) suffix, fileName, 128); StringPtr name = suffix; err = PtrAndHand (name, dataRef, name[0] + 1); @@ -253802,9 +253139,7 @@ END_JUCE_NAMESPACE /*** End of inlined file: juce_win32_NativeCode.cpp ***/ - #endif - - #if JUCE_LINUX + #elif JUCE_LINUX /*** Start of inlined file: juce_linux_NativeCode.cpp ***/ /* @@ -253838,7 +253173,9 @@ CriticalSection::CriticalSection() throw() pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif pthread_mutex_init (&internal, &atts); } @@ -253873,7 +253210,9 @@ public: pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); + #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif pthread_mutex_init (&mutex, &atts); } @@ -254272,9 +253611,14 @@ void FileOutputStream::flushInternal() const File juce_getExecutableFile() { + #if JUCE_ANDROID + // TODO + return File::nonexistent; + #else Dl_info exeInfo; - dladdr ((const void*) juce_getExecutableFile, &exeInfo); + dladdr ((void*) juce_getExecutableFile, &exeInfo); // (can't be a const void* on android) return File::getCurrentWorkingDirectory().getChildFile (String::fromUTF8 (exeInfo.dli_fname)); + #endif } int64 File::getBytesFreeOnVolume() const @@ -254494,7 +253838,13 @@ void Thread::closeThreadHandle() void Thread::killThread() { if (threadHandle_ != 0) + { + #if JUCE_ANDROID + jassertfalse; // pthread_cancel not available! + #else pthread_cancel ((pthread_t) threadHandle_); + #endif + } } void Thread::setCurrentThreadName (const String& /*name*/) @@ -255408,7 +254758,7 @@ private: header << "\r\nUser-Agent: JUCE/" << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION << "\r\nConnection: Close\r\nContent-Length: " - << postData.getSize() << "\r\n" + << (int) postData.getSize() << "\r\n" << headers << "\r\n"; MemoryBlock mb; @@ -262545,9 +261895,7 @@ END_JUCE_NAMESPACE /*** End of inlined file: juce_linux_NativeCode.cpp ***/ - #endif - - #if JUCE_MAC || JUCE_IOS + #elif JUCE_MAC || JUCE_IOS /*** Start of inlined file: juce_mac_NativeCode.mm ***/ /* @@ -263725,7 +263073,9 @@ CriticalSection::CriticalSection() throw() pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif pthread_mutex_init (&internal, &atts); } @@ -263760,7 +263110,9 @@ public: pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); + #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif pthread_mutex_init (&mutex, &atts); } @@ -264159,9 +263511,14 @@ void FileOutputStream::flushInternal() const File juce_getExecutableFile() { + #if JUCE_ANDROID + // TODO + return File::nonexistent; + #else Dl_info exeInfo; - dladdr ((const void*) juce_getExecutableFile, &exeInfo); + dladdr ((void*) juce_getExecutableFile, &exeInfo); // (can't be a const void* on android) return File::getCurrentWorkingDirectory().getChildFile (String::fromUTF8 (exeInfo.dli_fname)); + #endif } int64 File::getBytesFreeOnVolume() const @@ -264381,7 +263738,13 @@ void Thread::closeThreadHandle() void Thread::killThread() { if (threadHandle_ != 0) + { + #if JUCE_ANDROID + jassertfalse; // pthread_cancel not available! + #else pthread_cancel ((pthread_t) threadHandle_); + #endif + } } void Thread::setCurrentThreadName (const String& /*name*/) @@ -278957,5 +278320,2796 @@ END_JUCE_NAMESPACE /*** End of inlined file: juce_mac_NativeCode.mm ***/ + #elif JUCE_ANDROID + +/*** Start of inlined file: juce_android_NativeCode.cpp ***/ +/* + This file wraps together all the android-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + +#if JUCE_ANDROID + +#undef JUCE_BUILD_NATIVE +#define JUCE_BUILD_NATIVE 1 + +BEGIN_JUCE_NAMESPACE + +#define JUCE_INCLUDED_FILE 1 + +// Now include the actual code files.. + +/*** Start of inlined file: juce_posix_SharedCode.h ***/ +/* + This file contains posix routines that are common to both the Linux and Mac builds. + + It gets included directly in the cpp files for these platforms. +*/ + +CriticalSection::CriticalSection() throw() +{ + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + #if ! JUCE_ANDROID + pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif + pthread_mutex_init (&internal, &atts); +} + +CriticalSection::~CriticalSection() throw() +{ + pthread_mutex_destroy (&internal); +} + +void CriticalSection::enter() const throw() +{ + pthread_mutex_lock (&internal); +} + +bool CriticalSection::tryEnter() const throw() +{ + return pthread_mutex_trylock (&internal) == 0; +} + +void CriticalSection::exit() const throw() +{ + pthread_mutex_unlock (&internal); +} + +class WaitableEventImpl +{ +public: + WaitableEventImpl (const bool manualReset_) + : triggered (false), + manualReset (manualReset_) + { + pthread_cond_init (&condition, 0); + + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + #if ! JUCE_ANDROID + pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif + pthread_mutex_init (&mutex, &atts); + } + + ~WaitableEventImpl() + { + pthread_cond_destroy (&condition); + pthread_mutex_destroy (&mutex); + } + + bool wait (const int timeOutMillisecs) throw() + { + pthread_mutex_lock (&mutex); + + if (! triggered) + { + if (timeOutMillisecs < 0) + { + do + { + pthread_cond_wait (&condition, &mutex); + } + while (! triggered); + } + else + { + struct timeval now; + gettimeofday (&now, 0); + + struct timespec time; + time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000); + time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; + + if (time.tv_nsec >= 1000000000) + { + time.tv_nsec -= 1000000000; + time.tv_sec++; + } + + do + { + if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT) + { + pthread_mutex_unlock (&mutex); + return false; + } + } + while (! triggered); + } + } + + if (! manualReset) + triggered = false; + + pthread_mutex_unlock (&mutex); + return true; + } + + void signal() throw() + { + pthread_mutex_lock (&mutex); + triggered = true; + pthread_cond_broadcast (&condition); + pthread_mutex_unlock (&mutex); + } + + void reset() throw() + { + pthread_mutex_lock (&mutex); + triggered = false; + pthread_mutex_unlock (&mutex); + } + +private: + pthread_cond_t condition; + pthread_mutex_t mutex; + bool triggered; + const bool manualReset; + + JUCE_DECLARE_NON_COPYABLE (WaitableEventImpl); +}; + +WaitableEvent::WaitableEvent (const bool manualReset) throw() + : internal (new WaitableEventImpl (manualReset)) +{ +} + +WaitableEvent::~WaitableEvent() throw() +{ + delete static_cast (internal); +} + +bool WaitableEvent::wait (const int timeOutMillisecs) const throw() +{ + return static_cast (internal)->wait (timeOutMillisecs); +} + +void WaitableEvent::signal() const throw() +{ + static_cast (internal)->signal(); +} + +void WaitableEvent::reset() const throw() +{ + static_cast (internal)->reset(); +} + +void JUCE_CALLTYPE Thread::sleep (int millisecs) +{ + struct timespec time; + time.tv_sec = millisecs / 1000; + time.tv_nsec = (millisecs % 1000) * 1000000; + nanosleep (&time, 0); +} + +const juce_wchar File::separator = '/'; +const String File::separatorString ("/"); + +const File File::getCurrentWorkingDirectory() +{ + HeapBlock heapBuffer; + + char localBuffer [1024]; + char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1); + int bufferSize = 4096; + + while (cwd == 0 && errno == ERANGE) + { + heapBuffer.malloc (bufferSize); + cwd = getcwd (heapBuffer, bufferSize - 1); + bufferSize += 1024; + } + + return File (String::fromUTF8 (cwd)); +} + +bool File::setAsCurrentWorkingDirectory() const +{ + return chdir (getFullPathName().toUTF8()) == 0; +} + +namespace +{ + #if JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T + typedef struct stat64 juce_statStruct; // (need to use the 64-bit version to work around a simulator bug) + #else + typedef struct stat juce_statStruct; + #endif + + bool juce_stat (const String& fileName, juce_statStruct& info) + { + return fileName.isNotEmpty() + #if JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T + && (stat64 (fileName.toUTF8(), &info) == 0); + #else + && (stat (fileName.toUTF8(), &info) == 0); + #endif + } + + // if this file doesn't exist, find a parent of it that does.. + bool juce_doStatFS (File f, struct statfs& result) + { + for (int i = 5; --i >= 0;) + { + if (f.exists()) + break; + + f = f.getParentDirectory(); + } + + return statfs (f.getFullPathName().toUTF8(), &result) == 0; + } + + void updateStatInfoForFile (const String& path, bool* const isDir, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + if (isDir != 0 || fileSize != 0 || modTime != 0 || creationTime != 0) + { + juce_statStruct info; + const bool statOk = juce_stat (path, info); + + if (isDir != 0) *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); + if (fileSize != 0) *fileSize = statOk ? info.st_size : 0; + if (modTime != 0) *modTime = Time (statOk ? (int64) info.st_mtime * 1000 : 0); + if (creationTime != 0) *creationTime = Time (statOk ? (int64) info.st_ctime * 1000 : 0); + } + + if (isReadOnly != 0) + *isReadOnly = access (path.toUTF8(), W_OK) != 0; + } +} + +bool File::isDirectory() const +{ + juce_statStruct info; + + return fullPath.isEmpty() + || (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0)); +} + +bool File::exists() const +{ + juce_statStruct info; + + return fullPath.isNotEmpty() + #if JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T + && (lstat64 (fullPath.toUTF8(), &info) == 0); + #else + && (lstat (fullPath.toUTF8(), &info) == 0); + #endif +} + +bool File::existsAsFile() const +{ + return exists() && ! isDirectory(); +} + +int64 File::getSize() const +{ + juce_statStruct info; + return juce_stat (fullPath, info) ? info.st_size : 0; +} + +bool File::hasWriteAccess() const +{ + if (exists()) + return access (fullPath.toUTF8(), W_OK) == 0; + + if ((! isDirectory()) && fullPath.containsChar (separator)) + return getParentDirectory().hasWriteAccess(); + + return false; +} + +bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const +{ + juce_statStruct info; + if (! juce_stat (fullPath, info)) + return false; + + info.st_mode &= 0777; // Just permissions + + if (shouldBeReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + else + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + return chmod (fullPath.toUTF8(), info.st_mode) == 0; +} + +void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const +{ + modificationTime = 0; + accessTime = 0; + creationTime = 0; + + juce_statStruct info; + if (juce_stat (fullPath, info)) + { + modificationTime = (int64) info.st_mtime * 1000; + accessTime = (int64) info.st_atime * 1000; + creationTime = (int64) info.st_ctime * 1000; + } +} + +bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 /*creationTime*/) const +{ + juce_statStruct info; + + if ((modificationTime != 0 || accessTime != 0) && juce_stat (fullPath, info)) + { + struct utimbuf times; + times.actime = accessTime != 0 ? (time_t) (accessTime / 1000) : info.st_atime; + times.modtime = modificationTime != 0 ? (time_t) (modificationTime / 1000) : info.st_mtime; + + return utime (fullPath.toUTF8(), ×) == 0; + } + + return false; +} + +bool File::deleteFile() const +{ + if (! exists()) + return true; + + if (isDirectory()) + return rmdir (fullPath.toUTF8()) == 0; + + return remove (fullPath.toUTF8()) == 0; +} + +bool File::moveInternal (const File& dest) const +{ + if (rename (fullPath.toUTF8(), dest.getFullPathName().toUTF8()) == 0) + return true; + + if (hasWriteAccess() && copyInternal (dest)) + { + if (deleteFile()) + return true; + + dest.deleteFile(); + } + + return false; +} + +void File::createDirectoryInternal (const String& fileName) const +{ + mkdir (fileName.toUTF8(), 0777); +} + +int64 juce_fileSetPosition (void* handle, int64 pos) +{ + if (handle != 0 && lseek ((int) (pointer_sized_int) handle, pos, SEEK_SET) == pos) + return pos; + + return -1; +} + +void FileInputStream::openHandle() +{ + totalSize = file.getSize(); + + const int f = open (file.getFullPathName().toUTF8(), O_RDONLY, 00644); + + if (f != -1) + fileHandle = (void*) f; +} + +void FileInputStream::closeHandle() +{ + if (fileHandle != 0) + { + close ((int) (pointer_sized_int) fileHandle); + fileHandle = 0; + } +} + +size_t FileInputStream::readInternal (void* const buffer, const size_t numBytes) +{ + if (fileHandle != 0) + return jmax ((ssize_t) 0, ::read ((int) (pointer_sized_int) fileHandle, buffer, numBytes)); + + return 0; +} + +void FileOutputStream::openHandle() +{ + if (file.exists()) + { + const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644); + + if (f != -1) + { + currentPosition = lseek (f, 0, SEEK_END); + + if (currentPosition >= 0) + fileHandle = (void*) f; + else + close (f); + } + } + else + { + const int f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644); + + if (f != -1) + fileHandle = (void*) f; + } +} + +void FileOutputStream::closeHandle() +{ + if (fileHandle != 0) + { + close ((int) (pointer_sized_int) fileHandle); + fileHandle = 0; + } +} + +int FileOutputStream::writeInternal (const void* const data, const int numBytes) +{ + if (fileHandle != 0) + return (int) ::write ((int) (pointer_sized_int) fileHandle, data, numBytes); + + return 0; +} + +void FileOutputStream::flushInternal() +{ + if (fileHandle != 0) + fsync ((int) (pointer_sized_int) fileHandle); +} + +const File juce_getExecutableFile() +{ + #if JUCE_ANDROID + // TODO + return File::nonexistent; + #else + Dl_info exeInfo; + dladdr ((void*) juce_getExecutableFile, &exeInfo); // (can't be a const void* on android) + return File::getCurrentWorkingDirectory().getChildFile (String::fromUTF8 (exeInfo.dli_fname)); + #endif +} + +int64 File::getBytesFreeOnVolume() const +{ + struct statfs buf; + if (juce_doStatFS (*this, buf)) + return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user + + return 0; +} + +int64 File::getVolumeTotalSize() const +{ + struct statfs buf; + if (juce_doStatFS (*this, buf)) + return (int64) buf.f_bsize * (int64) buf.f_blocks; + + return 0; +} + +const String File::getVolumeLabel() const +{ +#if JUCE_MAC + struct VolAttrBuf + { + u_int32_t length; + attrreference_t mountPointRef; + char mountPointSpace [MAXPATHLEN]; + } attrBuf; + + struct attrlist attrList; + zerostruct (attrList); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; + + File f (*this); + + for (;;) + { + if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0) + return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset, + (int) attrBuf.mountPointRef.attr_length); + + const File parent (f.getParentDirectory()); + + if (f == parent) + break; + + f = parent; + } +#endif + + return String::empty; +} + +int File::getVolumeSerialNumber() const +{ + return 0; // xxx +} + +void juce_runSystemCommand (const String& command) +{ + int result = system (command.toUTF8()); + (void) result; +} + +const String juce_getOutputFromCommand (const String& command) +{ + // slight bodge here, as we just pipe the output into a temp file and read it... + const File tempFile (File::getSpecialLocation (File::tempDirectory) + .getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false)); + + juce_runSystemCommand (command + " > " + tempFile.getFullPathName()); + + String result (tempFile.loadFileAsString()); + tempFile.deleteFile(); + return result; +} + +class InterProcessLock::Pimpl +{ +public: + Pimpl (const String& name, const int timeOutMillisecs) + : handle (0), refCount (1) + { + #if JUCE_MAC + // (don't use getSpecialLocation() to avoid the temp folder being different for each app) + const File temp (File ("~/Library/Caches/Juce").getChildFile (name)); + #else + const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name)); + #endif + temp.create(); + handle = open (temp.getFullPathName().toUTF8(), O_RDWR); + + if (handle != 0) + { + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_WRLCK; + + const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; + + for (;;) + { + const int result = fcntl (handle, F_SETLK, &fl); + + if (result >= 0) + return; + + if (errno != EINTR) + { + if (timeOutMillisecs == 0 + || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime)) + break; + + Thread::sleep (10); + } + } + } + + closeFile(); + } + + ~Pimpl() + { + closeFile(); + } + + void closeFile() + { + if (handle != 0) + { + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_UNLCK; + + while (! (fcntl (handle, F_SETLKW, &fl) >= 0 || errno != EINTR)) + {} + + close (handle); + handle = 0; + } + } + + int handle, refCount; +}; + +InterProcessLock::InterProcessLock (const String& name_) + : name (name_) +{ +} + +InterProcessLock::~InterProcessLock() +{ +} + +bool InterProcessLock::enter (const int timeOutMillisecs) +{ + const ScopedLock sl (lock); + + if (pimpl == 0) + { + pimpl = new Pimpl (name, timeOutMillisecs); + + if (pimpl->handle == 0) + pimpl = 0; + } + else + { + pimpl->refCount++; + } + + return pimpl != 0; +} + +void InterProcessLock::exit() +{ + const ScopedLock sl (lock); + + // Trying to release the lock too many times! + jassert (pimpl != 0); + + if (pimpl != 0 && --(pimpl->refCount) == 0) + pimpl = 0; +} + +void JUCE_API juce_threadEntryPoint (void*); + +void* threadEntryProc (void* userData) +{ + JUCE_AUTORELEASEPOOL + juce_threadEntryPoint (userData); + return 0; +} + +void Thread::launchThread() +{ + threadHandle_ = 0; + pthread_t handle = 0; + + if (pthread_create (&handle, 0, threadEntryProc, this) == 0) + { + pthread_detach (handle); + threadHandle_ = (void*) handle; + threadId_ = (ThreadID) threadHandle_; + } +} + +void Thread::closeThreadHandle() +{ + threadId_ = 0; + threadHandle_ = 0; +} + +void Thread::killThread() +{ + if (threadHandle_ != 0) + { + #if JUCE_ANDROID + jassertfalse; // pthread_cancel not available! + #else + pthread_cancel ((pthread_t) threadHandle_); + #endif + } +} + +void Thread::setCurrentThreadName (const String& /*name*/) +{ +} + +bool Thread::setThreadPriority (void* handle, int priority) +{ + struct sched_param param; + int policy; + priority = jlimit (0, 10, priority); + + if (handle == 0) + handle = (void*) pthread_self(); + + if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0) + return false; + + policy = priority == 0 ? SCHED_OTHER : SCHED_RR; + + const int minPriority = sched_get_priority_min (policy); + const int maxPriority = sched_get_priority_max (policy); + + param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority; + return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0; +} + +Thread::ThreadID Thread::getCurrentThreadId() +{ + return (ThreadID) pthread_self(); +} + +void Thread::yield() +{ + sched_yield(); +} + +/* Remove this macro if you're having problems compiling the cpu affinity + calls (the API for these has changed about quite a bit in various Linux + versions, and a lot of distros seem to ship with obsolete versions) +*/ +#if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES) + #define SUPPORT_AFFINITIES 1 +#endif + +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) +{ +#if SUPPORT_AFFINITIES + cpu_set_t affinity; + CPU_ZERO (&affinity); + + for (int i = 0; i < 32; ++i) + if ((affinityMask & (1 << i)) != 0) + CPU_SET (i, &affinity); + + /* + N.B. If this line causes a compile error, then you've probably not got the latest + version of glibc installed. + + If you don't want to update your copy of glibc and don't care about cpu affinities, + then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0. + */ + sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity); + sched_yield(); + +#else + /* affinities aren't supported because either the appropriate header files weren't found, + or the SUPPORT_AFFINITIES macro was turned off + */ + jassertfalse; + (void) affinityMask; +#endif +} +/*** End of inlined file: juce_posix_SharedCode.h ***/ + + + +/*** Start of inlined file: juce_android_Files.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +bool File::copyInternal (const File& dest) const +{ + // TODO this is a brute-force copy, better to use an OS function if one exists.. + + FileInputStream in (*this); + + if (dest.deleteFile()) + { + { + FileOutputStream out (dest); + + if (out.failedToOpen()) + return false; + + if (out.writeFromInputStream (in, -1) == getSize()) + return true; + } + + dest.deleteFile(); + } + + return false; +} + +void File::findFileSystemRoots (Array& destArray) +{ + // TODO appropriate on android? + + destArray.add (File ("/")); +} + +bool File::isOnCDRomDrive() const +{ + return false; +} + +bool File::isOnHardDisk() const +{ + return true; +} + +bool File::isOnRemovableDrive() const +{ + return false; +} + +bool File::isHidden() const +{ + // TODO is this appropriate for android? + + return getFileName().startsWithChar ('.'); +} + +namespace +{ + const File juce_readlink (const String& file, const File& defaultFile) + { + const int size = 8192; + HeapBlock buffer; + buffer.malloc (size + 4); + + const size_t numBytes = readlink (file.toUTF8(), buffer, size); + + if (numBytes > 0 && numBytes <= size) + return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); + + return defaultFile; + } +} + +const File File::getLinkedTarget() const +{ + // TODO - same as linux? + return juce_readlink (getFullPathName().toUTF8(), *this); +} + +const char* juce_Argv0 = 0; // referenced from juce_Application.cpp + +const File File::getSpecialLocation (const SpecialLocationType type) +{ + + // TODO this stuff is all copied from linux, needs sanity-checking for android + + switch (type) + { + case userHomeDirectory: + { + const char* homeDir = getenv ("HOME"); + + if (homeDir == 0) + { + struct passwd* const pw = getpwuid (getuid()); + if (pw != 0) + homeDir = pw->pw_dir; + } + + return File (String::fromUTF8 (homeDir)); + } + + case userDocumentsDirectory: + case userMusicDirectory: + case userMoviesDirectory: + case userApplicationDataDirectory: + return File ("~"); + + case userDesktopDirectory: + return File ("~/Desktop"); + + case commonApplicationDataDirectory: + return File ("/var"); + + case globalApplicationsDirectory: + return File ("/usr"); + + case tempDirectory: + { + File tmp ("/var/tmp"); + + if (! tmp.isDirectory()) + { + tmp = "/tmp"; + + if (! tmp.isDirectory()) + tmp = File::getCurrentWorkingDirectory(); + } + + return tmp; + } + + case invokedExecutableFile: + if (juce_Argv0 != 0) + return File (String::fromUTF8 (juce_Argv0)); + // deliberate fall-through... + + case currentExecutableFile: + case currentApplicationFile: + return juce_getExecutableFile(); + + case hostApplicationPath: + return juce_readlink ("/proc/self/exe", juce_getExecutableFile()); + + default: + jassertfalse; // unknown type? + break; + } + + return File::nonexistent; +} + +const String File::getVersion() const +{ + return String::empty; +} + +bool File::moveToTrash() const +{ + if (! exists()) + return true; + + // TODO + + return false; +} + +// TODO If the normal posix directory functions work, the following stuff should all be fine... + +class DirectoryIterator::NativeIterator::Pimpl +{ +public: + Pimpl (const File& directory, const String& wildCard_) + : parentDir (File::addTrailingSeparator (directory.getFullPathName())), + wildCard (wildCard_), + dir (opendir (directory.getFullPathName().toUTF8())) + { + wildcardUTF8 = wildCard.toUTF8(); + } + + ~Pimpl() + { + if (dir != 0) + closedir (dir); + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + if (dir != 0) + { + for (;;) + { + struct dirent* const de = readdir (dir); + + if (de == 0) + break; + + if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) + { + filenameFound = String::fromUTF8 (de->d_name); + + updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); + + if (isHidden != 0) + *isHidden = filenameFound.startsWithChar ('.'); + + return true; + } + } + } + + return false; + } + +private: + String parentDir, wildCard; + const char* wildcardUTF8; + DIR* dir; + + JUCE_DECLARE_NON_COPYABLE (Pimpl); +}; + +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +{ +} + +DirectoryIterator::NativeIterator::~NativeIterator() +{ +} + +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) +{ + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); +} + +bool PlatformUtilities::openDocument (const String& fileName, const String& parameters) +{ + +} + +void File::revealToUser() const +{ + +} + +#endif +/*** End of inlined file: juce_android_Files.cpp ***/ + + +/*** Start of inlined file: juce_posix_NamedPipe.cpp ***/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +struct NamedPipeInternal +{ + String pipeInName, pipeOutName; + int pipeIn, pipeOut; + + bool volatile createdPipe, blocked, stopReadOperation; + + static void signalHandler (int) {} +}; + +void NamedPipe::cancelPendingReads() +{ + while (internal != 0 && static_cast (internal)->blocked) + { + NamedPipeInternal* const intern = static_cast (internal); + + intern->stopReadOperation = true; + + char buffer [1] = { 0 }; + int bytesWritten = (int) ::write (intern->pipeIn, buffer, 1); + (void) bytesWritten; + + int timeout = 2000; + while (intern->blocked && --timeout >= 0) + Thread::sleep (2); + + intern->stopReadOperation = false; + } +} + +void NamedPipe::close() +{ + NamedPipeInternal* const intern = static_cast (internal); + + if (intern != 0) + { + internal = 0; + + if (intern->pipeIn != -1) + ::close (intern->pipeIn); + + if (intern->pipeOut != -1) + ::close (intern->pipeOut); + + if (intern->createdPipe) + { + unlink (intern->pipeInName.toUTF8()); + unlink (intern->pipeOutName.toUTF8()); + } + + delete intern; + } +} + +bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) +{ + close(); + + NamedPipeInternal* const intern = new NamedPipeInternal(); + internal = intern; + intern->createdPipe = createPipe; + intern->blocked = false; + intern->stopReadOperation = false; + + signal (SIGPIPE, NamedPipeInternal::signalHandler); + siginterrupt (SIGPIPE, 1); + + const String pipePath ("/tmp/" + File::createLegalFileName (pipeName)); + + intern->pipeInName = pipePath + "_in"; + intern->pipeOutName = pipePath + "_out"; + intern->pipeIn = -1; + intern->pipeOut = -1; + + if (createPipe) + { + if ((mkfifo (intern->pipeInName.toUTF8(), 0666) && errno != EEXIST) + || (mkfifo (intern->pipeOutName.toUTF8(), 0666) && errno != EEXIST)) + { + delete intern; + internal = 0; + + return false; + } + } + + return true; +} + +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int /*timeOutMilliseconds*/) +{ + int bytesRead = -1; + NamedPipeInternal* const intern = static_cast (internal); + + if (intern != 0) + { + intern->blocked = true; + + if (intern->pipeIn == -1) + { + if (intern->createdPipe) + intern->pipeIn = ::open (intern->pipeInName.toUTF8(), O_RDWR); + else + intern->pipeIn = ::open (intern->pipeOutName.toUTF8(), O_RDWR); + + if (intern->pipeIn == -1) + { + intern->blocked = false; + return -1; + } + } + + bytesRead = 0; + + char* p = static_cast (destBuffer); + + while (bytesRead < maxBytesToRead) + { + const int bytesThisTime = maxBytesToRead - bytesRead; + const int numRead = (int) ::read (intern->pipeIn, p, bytesThisTime); + + if (numRead <= 0 || intern->stopReadOperation) + { + bytesRead = -1; + break; + } + + bytesRead += numRead; + p += bytesRead; + } + + intern->blocked = false; + } + + return bytesRead; +} + +int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) +{ + int bytesWritten = -1; + NamedPipeInternal* const intern = static_cast (internal); + + if (intern != 0) + { + if (intern->pipeOut == -1) + { + if (intern->createdPipe) + intern->pipeOut = ::open (intern->pipeOutName.toUTF8(), O_WRONLY); + else + intern->pipeOut = ::open (intern->pipeInName.toUTF8(), O_WRONLY); + + if (intern->pipeOut == -1) + { + return -1; + } + } + + const char* p = static_cast (sourceBuffer); + bytesWritten = 0; + + const uint32 timeOutTime = Time::getMillisecondCounter() + timeOutMilliseconds; + + while (bytesWritten < numBytesToWrite + && (timeOutMilliseconds < 0 || Time::getMillisecondCounter() < timeOutTime)) + { + const int bytesThisTime = numBytesToWrite - bytesWritten; + const int numWritten = (int) ::write (intern->pipeOut, p, bytesThisTime); + + if (numWritten <= 0) + { + bytesWritten = -1; + break; + } + + bytesWritten += numWritten; + p += bytesWritten; + } + } + + return bytesWritten; +} + +#endif +/*** End of inlined file: juce_posix_NamedPipe.cpp ***/ + + +/*** Start of inlined file: juce_android_SystemStats.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +void PlatformUtilities::beep() +{ + // TODO +} + +void Logger::outputDebugString (const String& text) +{ + // TODO is this ok..? + std::cerr << text << std::endl; +} + +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() +{ + return Android; +} + +const String SystemStats::getOperatingSystemName() +{ + return "Android"; +} + +bool SystemStats::isOperatingSystem64Bit() +{ + #if JUCE_64BIT + return true; + #else + return false; + #endif +} + +namespace LinuxStatsHelpers +{ + // TODO would this work? If not, the CPU info functions aren't of huge importance.. + const String getCpuInfo (const char* const key) + { + StringArray lines; + lines.addLines (File ("/proc/cpuinfo").loadFileAsString()); + + for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) + if (lines[i].startsWithIgnoreCase (key)) + return lines[i].fromFirstOccurrenceOf (":", false, false).trim(); + + return String::empty; + } +} + +const String SystemStats::getCpuVendor() +{ + return LinuxStatsHelpers::getCpuInfo ("vendor_id"); +} + +int SystemStats::getCpuSpeedInMegaherz() +{ + return roundToInt (LinuxStatsHelpers::getCpuInfo ("cpu MHz").getFloatValue()); +} + +int SystemStats::getMemorySizeInMegabytes() +{ + // TODO + + struct sysinfo sysi; + + if (sysinfo (&sysi) == 0) + return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); + + return 0; +} + +int SystemStats::getPageSize() +{ + // TODO if this doesn't work, just return 0, it's not important + return sysconf (_SC_PAGESIZE); +} + +const String SystemStats::getLogonName() +{ + // TODO + + const char* user = getenv ("USER"); + + if (user == 0) + { + struct passwd* const pw = getpwuid (getuid()); + if (pw != 0) + user = pw->pw_name; + } + + return String::fromUTF8 (user); +} + +const String SystemStats::getFullUserName() +{ + return getLogonName(); +} + +void SystemStats::initialiseStats() +{ + // TODO these flags should be set, but if we're only running on ARM they can all just be false + + const String flags (LinuxStatsHelpers::getCpuInfo ("flags")); + cpuFlags.hasMMX = flags.contains ("mmx"); + cpuFlags.hasSSE = flags.contains ("sse"); + cpuFlags.hasSSE2 = flags.contains ("sse2"); + cpuFlags.has3DNow = flags.contains ("3dnow"); + + // TODO this should probably be set to the number of cores if possible + cpuFlags.numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; +} + +void PlatformUtilities::fpuReset() {} + +uint32 juce_millisecondsSinceStartup() throw() +{ + // TODO must return a monotonically rising clock with decent accuracy + + timespec t; + clock_gettime (CLOCK_MONOTONIC, &t); + + return t.tv_sec * 1000 + t.tv_nsec / 1000000; +} + +int64 Time::getHighResolutionTicks() throw() +{ + // TODO + + timespec t; + clock_gettime (CLOCK_MONOTONIC, &t); + + return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / (int64) 1000); +} + +int64 Time::getHighResolutionTicksPerSecond() throw() +{ + // TODO + + return 1000000; // (microseconds) +} + +double Time::getMillisecondCounterHiRes() throw() +{ + return getHighResolutionTicks() * 0.001; +} + +bool Time::setSystemTimeToThisTime() const +{ + jassertfalse; + return false; +} + +#endif +/*** End of inlined file: juce_android_SystemStats.cpp ***/ + + +/*** Start of inlined file: juce_android_Threads.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.h! +*/ + +// TODO all the main thread code lives inside juce_posix_SharedCode.h, so if there's +// anything in there that doesn't work with android, it'd need to be #ifdef'ed out in that file and +// done in this file instead.. + +// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime +void Process::setPriority (ProcessPriority prior) +{ + // TODO this might work, but if not, it might need tweaking + + struct sched_param param; + int policy, maxp, minp; + + const int p = (int) prior; + + if (p <= 1) + policy = SCHED_OTHER; + else + policy = SCHED_RR; + + minp = sched_get_priority_min (policy); + maxp = sched_get_priority_max (policy); + + if (p < 2) + param.sched_priority = 0; + else if (p == 2 ) + // Set to middle of lower realtime priority range + param.sched_priority = minp + (maxp - minp) / 4; + else + // Set to middle of higher realtime priority range + param.sched_priority = minp + (3 * (maxp - minp) / 4); + + pthread_setschedparam (pthread_self(), policy, ¶m); +} + +void Process::terminate() +{ + // TODO correct way to kill the process? + exit (0); +} + +JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() +{ + return false; +} + +JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() +{ + return juce_isRunningUnderDebugger(); +} + +void Process::raisePrivilege() {} +void Process::lowerPrivilege() {} + +#endif +/*** End of inlined file: juce_android_Threads.cpp ***/ + + +/*** Start of inlined file: juce_android_Network.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +void MACAddress::findAllAddresses (Array& result) +{ + // TODO not a big priority to do this + +} + +bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) +{ + // TODO might be nice, but low priority + + return false; +} + +// TODO This class needs to act as a HTTP or FTP stream + +class WebInputStream : public InputStream +{ +public: + + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + { + // TODO try to open, set this flag if it fails + openedOk = false; + } + + ~WebInputStream() + { + } + + bool isExhausted() + { + return true; // TODO + } + + int64 getPosition() + { + return 0; // TODO + } + + int64 getTotalLength() + { + return -1; // TODO + } + + int read (void* buffer, int bytesToRead) + { + // TODO + return 0; + } + + bool setPosition (int64 wantedPos) + { + // TODO + return false; + } + + bool openedOk; + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); +}; + +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) +{ + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); + + return wi->openedOk ? wi.release() : 0; +} + +#endif +/*** End of inlined file: juce_android_Network.cpp ***/ + + +/*** Start of inlined file: juce_android_Messaging.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +void MessageManager::doPlatformSpecificInitialisation() +{ + +} + +void MessageManager::doPlatformSpecificShutdown() +{ + +} + +bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages) +{ + // TODO + + /* + The idea here is that this will check the system message queue, pull off a + message if there is one, deliver it, and return true if a message was delivered. + If the queue's empty, return false. + + If the message is one of our special ones (i.e. a Message object being delivered, + this must call MessageManager::getInstance()->deliverMessage() to deliver it + + */ + + return true; +} + +bool juce_postMessageToSystemQueue (Message* message) +{ + // TODO Must use some kind of mechanism to post the given message to the system + // queue, in such a way that juce_dispatchNextMessageOnSystemQueue will be able + // to deliver it when it arrives + + return true; +} + +// TODO this stuff will probably work as it is.. + +class AsyncFunctionCaller : public AsyncUpdater +{ +public: + static void* call (MessageCallbackFunction* func_, void* parameter_) + { + if (MessageManager::getInstance()->isThisTheMessageThread()) + return func_ (parameter_); + + AsyncFunctionCaller caller (func_, parameter_); + caller.triggerAsyncUpdate(); + caller.finished.wait(); + return caller.result; + } + + void handleAsyncUpdate() + { + result = (*func) (parameter); + finished.signal(); + } + +private: + WaitableEvent finished; + MessageCallbackFunction* func; + void* parameter; + void* volatile result; + + AsyncFunctionCaller (MessageCallbackFunction* func_, void* parameter_) + : result (0), func (func_), parameter (parameter_) + {} + + JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCaller); +}; + +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter) +{ + return AsyncFunctionCaller::call (func, parameter); +} + +void MessageManager::broadcastMessage (const String&) +{ + // TODO no need to bother with this +} + +#endif +/*** End of inlined file: juce_android_Messaging.cpp ***/ + + +/*** Start of inlined file: juce_android_Fonts.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +const StringArray Font::findAllTypefaceNames() +{ + StringArray results; + + // TODO find all the typefaces + + return results; +} + +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback) +{ + // TODO these need to be set to some kind of sensible defaults.. + defaultSans = "Verdana"; + defaultSerif = "Times"; + defaultFixed = "Lucida Console"; + defaultFallback = "Tahoma"; +} + +// TODO the nature of this class depends on the way it's going to be drawn - if the rendering +// engine draws the glyphs itself (e.g. the CoreGraphicsContext), then this may only need +// to hold some kind of platform-specific font handle, which the rendering engine can use. +// If the glyphs are drawn as shapes (as done by the software renderer), then this will need +// to make sure the getOutlineForGlyph method works. + +class AndroidTypeface : public Typeface +{ +public: + AndroidTypeface (const Font& font) + : Typeface (font.getTypefaceName()) + { + // TODO + } + + float getAscent() const + { + return 0; // TODO this is the font's ascent expressed as a proportion of its height, so should be 0 -> 1.0f + } + + float getDescent() const + { + return 0; // TODO this is font's decscent expressed as a proportion of its height, so should be 0 -> 1.0f + } + + float getStringWidth (const String& text) + { + // TODO this must lay out the given text and return the width of the text as a multiple of the font's height + return 0; + } + + void getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) + { + // TODO this must lay out the given text, and return the set of glyph numbers used, and their + // positions, in the arrays provided. + // The glyph numbers are whatever is appropriate for use by the rendering engine, and the offsets + // are expressed as distances along the X axis from the left of the first character, and are + // measured in multiples of the font's height (like all other sizes in the typeface) + } + + bool getOutlineForGlyph (int glyphNumber, Path& destPath) + { + // TODO must return the path that can be used to draw the given glyph number (where the glyph + // number will have been returned by the getGlyphPositions method). + // The path must have its origin at the character's left-hand baseline, and must be scaled so that + // the font's height maps onto a length of 1.0f in the path. + return false; + } + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface); +}; + +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) +{ + return new AndroidTypeface (font); +} + +#endif +/*** End of inlined file: juce_android_Fonts.cpp ***/ + + +/*** Start of inlined file: juce_android_GraphicsContext.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +// TODO - implement this lot! Probably need to make it wrap a Java2D graphics context. + +class AndroidLowLevelGraphicsContext : public LowLevelGraphicsContext +{ +public: + AndroidLowLevelGraphicsContext() + { + } + + ~AndroidLowLevelGraphicsContext() + { + } + + bool isVectorDevice() const { return false; } + + void setOrigin (int x, int y) + { + } + + void addTransform (const AffineTransform& transform) + { + } + + float getScaleFactor() + { + return 1.0f; + } + + bool clipToRectangle (const Rectangle& r) + { + return false; + } + + bool clipToRectangleList (const RectangleList& clipRegion) + { + return false; + } + + void excludeClipRectangle (const Rectangle& r) + { + } + + void clipToPath (const Path& path, const AffineTransform& transform) + { + } + + void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) + { + } + + bool clipRegionIntersects (const Rectangle& r) + { + return true; + } + + const Rectangle getClipBounds() const + { + return Rectangle(); + } + + bool isClipEmpty() const + { + return false; + } + + void saveState() + { + } + + void restoreState() + { + } + + void beginTransparencyLayer (float opacity) + { + } + + void endTransparencyLayer() + { + } + + void setFill (const FillType& fillType) + { + } + + void setOpacity (float newOpacity) + { + } + + void setInterpolationQuality (Graphics::ResamplingQuality quality) + { + } + + void fillRect (const Rectangle& r, bool replaceExistingContents) + { + } + + void fillPath (const Path& path, const AffineTransform& transform) + { + } + + void drawImage (const Image& sourceImage, const AffineTransform& transform, bool fillEntireClipAsTiles) + { + } + + void drawLine (const Line & line) + { + } + + void drawVerticalLine (int x, float top, float bottom) + { + } + + void drawHorizontalLine (int y, float left, float right) + { + } + + void setFont (const Font& newFont) + { + } + + const Font getFont() + { + return Font(); + } + + void drawGlyph (int glyphNumber, const AffineTransform& transform) + { + } + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidLowLevelGraphicsContext); +}; + +#endif +/*** End of inlined file: juce_android_GraphicsContext.cpp ***/ + + +/*** Start of inlined file: juce_android_Windowing.cpp ***/ +// (This file gets included by juce_win32_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +/* + TODO The main windowing object + + As well as implementing all the methods below, the ComponentPeer base class has a whole bunch + of callback methods like handlePaint, handleMouseEvent, handleKeyPress, handleFocusGain, etc which + must be called by the subclass when the appropriate events occur. + +*/ + +class AndroidComponentPeer : public ComponentPeer +{ +public: + + AndroidComponentPeer (Component* const component, const int windowStyleFlags) + : ComponentPeer (component, windowStyleFlags) + { + // TODO create the window, obeying the appropriate style flags, which + // are from the set of flags in ComponentPeer::StyleFlags + } + + ~AndroidComponentPeer() + { + } + + void* getNativeHandle() const + { + return 0; // TODO gets returned by Component::getWindowHandle() so should be whatever makes sense + } + + void setVisible (bool shouldBeVisible) + { + } + + void setTitle (const String& title) + { + } + + void setPosition (int x, int y) + { + // TODO relative to desktop top-left + } + + void setSize (int w, int h) + { + } + + void setBounds (int x, int y, int w, int h, bool isNowFullScreen) + { + // TODO relative to desktop top-left + } + + const Rectangle getBounds() const + { + // TODO relative to desktop top-left + return Rectangle(); + } + + const Point getScreenPosition() const + { + // TODO relative to desktop top-left + return Point(); + } + + const Point localToGlobal (const Point& relativePosition) + { + // TODO probably ok as it is.. + return relativePosition + getScreenPosition(); + } + + const Point globalToLocal (const Point& screenPosition) + { + // TODO probably ok as it is.. + return screenPosition - getScreenPosition(); + } + + void setMinimised (bool shouldBeMinimised) + { + // TODO probably irrelevant on android + } + + bool isMinimised() const + { + } + + void setFullScreen (bool shouldBeFullScreen) + { + // TODO + } + + bool isFullScreen() const + { + // TODO + return false; + } + + void setIcon (const Image& newIcon) + { + // TODO probably irrelevant + } + + bool contains (const Point& position, bool trueIfInAChildWindow) const + { + // TODO should check that other windows aren't in front of this point + + return isPositiveAndBelow (position.getX(), component->getWidth()) + && isPositiveAndBelow (position.getY(), component->getHeight()); + } + + const BorderSize getFrameSize() const + { + // TODO + return BorderSize(); + } + + bool setAlwaysOnTop (bool alwaysOnTop) + { + // TODO probably not needed + return false; + } + + void toFront (bool makeActive) + { + // TODO + } + + void toBehind (ComponentPeer* other) + { + // TODO + } + + bool isFocused() const + { + // TODO + return false; + } + + void grabFocus() + { + // TODO + } + + void textInputRequired (const Point& position) + { + // TODO called when the on-screen keyboard should appear - the position indicates roughly where + // the text entry position is + } + + void repaint (const Rectangle& area) + { + // TODO invalidate the given area (relative to this window's top left) + } + + void performAnyPendingRepaintsNow() + { + // TODO probably not needed + } + + void setAlpha (float newAlpha) + { + // TODO changes the window's overall transparency + } + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidComponentPeer); +}; + +ComponentPeer* Component::createNewPeer (int styleFlags, void*) +{ + return new AndroidComponentPeer (this, styleFlags); +} + +bool Desktop::canUseSemiTransparentWindows() throw() +{ + return true; // TODO presumably this is true +} + +Desktop::DisplayOrientation Desktop::getCurrentOrientation() const +{ + // TODO + return upright; +} + +void Desktop::createMouseInputSources() +{ + // This creates a mouse input source for each possible finger + + for (int i = 0; i < 10; ++i) + mouseSources.add (new MouseInputSource (i, false)); +} + +const Point MouseInputSource::getCurrentMousePosition() +{ + // TODO + return Point(); +} + +void Desktop::setMousePosition (const Point& newPosition) +{ + // not needed +} + +bool KeyPress::isKeyCurrentlyDown (const int keyCode) +{ + // TODO + return false; +} + +void ModifierKeys::updateCurrentModifiers() throw() +{ + // not needed +} + +const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw() +{ + // TODO must return the state of mouse buttons, so if there are any + // touches underway, this should set the left-button flag + return ModifierKeys(); +} + +bool Process::isForegroundProcess() +{ + return true; // TODO ? +} + +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + // TODO just show a simple native box + +} + +Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage) +{ + return createSoftwareImage (format, width, height, clearImage); +} + +void Desktop::setScreenSaverEnabled (const bool isEnabled) +{ + // TODO low priority, but might need doing at some point +} + +bool Desktop::isScreenSaverEnabled() +{ + return true; +} + +void juce_setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool /*allowMenusAndBars*/) +{ +} + +void juce_updateMultiMonitorInfo (Array >& monitorCoords, const bool clipToWorkArea) +{ + // TODO must set the correct screen size in this rectangle.. + + monitorCoords.add (Rectangle (0, 0, 640, 480)); +} + +const Image juce_createIconForFile (const File& file) +{ + Image image; + + // TODO returns a file's icon.. No need to implement yet. + + return image; +} + +void* MouseCursor::createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) +{ + return 0; +} + +void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool isStandard) +{ +} + +void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type) +{ + return 0; +} + +void MouseCursor::showInWindow (ComponentPeer*) const {} +void MouseCursor::showInAllWindows() const {} + +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove) +{ + return false; +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + return false; +} + +const int extendedKeyModifier = 0x10000; + +const int KeyPress::spaceKey = ' '; +const int KeyPress::returnKey = 0x0d; +const int KeyPress::escapeKey = 0x1b; +const int KeyPress::backspaceKey = 0x7f; +const int KeyPress::leftKey = extendedKeyModifier + 1; +const int KeyPress::rightKey = extendedKeyModifier + 2; +const int KeyPress::upKey = extendedKeyModifier + 3; +const int KeyPress::downKey = extendedKeyModifier + 4; +const int KeyPress::pageUpKey = extendedKeyModifier + 5; +const int KeyPress::pageDownKey = extendedKeyModifier + 6; +const int KeyPress::endKey = extendedKeyModifier + 7; +const int KeyPress::homeKey = extendedKeyModifier + 8; +const int KeyPress::deleteKey = extendedKeyModifier + 9; +const int KeyPress::insertKey = -1; +const int KeyPress::tabKey = 9; +const int KeyPress::F1Key = extendedKeyModifier + 10; +const int KeyPress::F2Key = extendedKeyModifier + 11; +const int KeyPress::F3Key = extendedKeyModifier + 12; +const int KeyPress::F4Key = extendedKeyModifier + 13; +const int KeyPress::F5Key = extendedKeyModifier + 14; +const int KeyPress::F6Key = extendedKeyModifier + 16; +const int KeyPress::F7Key = extendedKeyModifier + 17; +const int KeyPress::F8Key = extendedKeyModifier + 18; +const int KeyPress::F9Key = extendedKeyModifier + 19; +const int KeyPress::F10Key = extendedKeyModifier + 20; +const int KeyPress::F11Key = extendedKeyModifier + 21; +const int KeyPress::F12Key = extendedKeyModifier + 22; +const int KeyPress::F13Key = extendedKeyModifier + 23; +const int KeyPress::F14Key = extendedKeyModifier + 24; +const int KeyPress::F15Key = extendedKeyModifier + 25; +const int KeyPress::F16Key = extendedKeyModifier + 26; +const int KeyPress::numberPad0 = extendedKeyModifier + 27; +const int KeyPress::numberPad1 = extendedKeyModifier + 28; +const int KeyPress::numberPad2 = extendedKeyModifier + 29; +const int KeyPress::numberPad3 = extendedKeyModifier + 30; +const int KeyPress::numberPad4 = extendedKeyModifier + 31; +const int KeyPress::numberPad5 = extendedKeyModifier + 32; +const int KeyPress::numberPad6 = extendedKeyModifier + 33; +const int KeyPress::numberPad7 = extendedKeyModifier + 34; +const int KeyPress::numberPad8 = extendedKeyModifier + 35; +const int KeyPress::numberPad9 = extendedKeyModifier + 36; +const int KeyPress::numberPadAdd = extendedKeyModifier + 37; +const int KeyPress::numberPadSubtract = extendedKeyModifier + 38; +const int KeyPress::numberPadMultiply = extendedKeyModifier + 39; +const int KeyPress::numberPadDivide = extendedKeyModifier + 40; +const int KeyPress::numberPadSeparator = extendedKeyModifier + 41; +const int KeyPress::numberPadDecimalPoint = extendedKeyModifier + 42; +const int KeyPress::numberPadEquals = extendedKeyModifier + 43; +const int KeyPress::numberPadDelete = extendedKeyModifier + 44; +const int KeyPress::playKey = extendedKeyModifier + 45; +const int KeyPress::stopKey = extendedKeyModifier + 46; +const int KeyPress::fastForwardKey = extendedKeyModifier + 47; +const int KeyPress::rewindKey = extendedKeyModifier + 48; + +#endif +/*** End of inlined file: juce_android_Windowing.cpp ***/ + + +/*** Start of inlined file: juce_android_FileChooser.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +void FileChooser::showPlatformDialog (Array& results, + const String& title, + const File& currentFileOrDirectory, + const String& filter, + bool selectsDirectory, + bool selectsFiles, + bool isSaveDialogue, + bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, + FilePreviewComponent* extraInfoComponent) +{ + // TODO probably not important to implement this right away.. + +} + +#endif +/*** End of inlined file: juce_android_FileChooser.cpp ***/ + + +/*** Start of inlined file: juce_android_Misc.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +void SystemClipboard::copyTextToClipboard (const String& text) +{ + // TODO +} + +const String SystemClipboard::getTextFromClipboard() +{ + String result; + + // TODO + + return result; +} + +#endif +/*** End of inlined file: juce_android_Misc.cpp ***/ + + +/*** Start of inlined file: juce_android_WebBrowserComponent.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_WEB_BROWSER + +// TODO this stuff isn't a high priority, but is where an embedded web browser would be done.. + +WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_) + : browser (0), + blankPageShown (false), + unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_) +{ + setOpaque (true); +} + +WebBrowserComponent::~WebBrowserComponent() +{ +} + +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + +} + +void WebBrowserComponent::stop() +{ +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + +} + +void WebBrowserComponent::refresh() +{ +} + +void WebBrowserComponent::paint (Graphics& g) +{ + g.fillAll (Colours::white); +} + +void WebBrowserComponent::checkWindowAssociation() +{ +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::resized() +{ +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String& url) +{ + return true; +} + +#endif +/*** End of inlined file: juce_android_WebBrowserComponent.cpp ***/ + + +/*** Start of inlined file: juce_android_OpenGLComponent.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_OPENGL + +// TODO - not urgent to implement the opengl stuff.. + +OpenGLContext* OpenGLComponent::createContext() +{ + return 0; +} + +void* OpenGLComponent::getNativeWindowHandle() const +{ + return 0; +} + +void juce_glViewport (const int w, const int h) +{ +// glViewport (0, 0, w, h); +} + +void OpenGLPixelFormat::getAvailablePixelFormats (Component* component, + OwnedArray & results) +{ + +} + +#endif +/*** End of inlined file: juce_android_OpenGLComponent.cpp ***/ + + +/*** Start of inlined file: juce_android_Midi.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +// TODO not a big priority, the stuff in here should act as stubs for the midi classes + +const StringArray MidiOutput::getDevices() +{ + StringArray devices; + + return devices; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return 0; +} + +MidiOutput* MidiOutput::openDevice (int index) +{ + return 0; +} + +MidiOutput::~MidiOutput() +{ +} + +void MidiOutput::reset() +{ +} + +bool MidiOutput::getVolume (float&, float&) +{ + return false; +} + +void MidiOutput::setVolume (float, float) +{ +} + +void MidiOutput::sendMessageNow (const MidiMessage&) +{ +} + +MidiInput::MidiInput (const String& name_) + : name (name_), + internal (0) +{ +} + +MidiInput::~MidiInput() +{ +} + +void MidiInput::start() +{ +} + +void MidiInput::stop() +{ +} + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +const StringArray MidiInput::getDevices() +{ + StringArray devs; + + return devs; +} + +MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + return 0; +} + +#endif +/*** End of inlined file: juce_android_Midi.cpp ***/ + + +/*** Start of inlined file: juce_android_Audio.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +class AndroidAudioIODevice : public AudioIODevice +{ +public: + + AndroidAudioIODevice (const String& deviceName) + : AudioIODevice (deviceName, "Audio"), + callback (0), + sampleRate (0), + numInputChannels (0), + numOutputChannels (0), + actualBufferSize (0), + isRunning (false) + { + numInputChannels = 2; + numOutputChannels = 2; + + // TODO initialise as needed.. + } + + ~AndroidAudioIODevice() + { + close(); + } + + const StringArray getOutputChannelNames() + { + StringArray s; + s.add ("Left"); // TODO if the device is anything other than stereo, this might need to be smarter + s.add ("Right"); + return s; + } + + const StringArray getInputChannelNames() + { + StringArray s; + s.add ("Left"); + s.add ("Right"); + return s; + } + + int getNumSampleRates() { return 1;} + double getSampleRate (int index) { return sampleRate; } + + int getNumBufferSizesAvailable() { return 1; } + int getBufferSizeSamples (int index) { return getDefaultBufferSize(); } + int getDefaultBufferSize() { return 1024; } + + const String open (const BigInteger& inputChannels, + const BigInteger& outputChannels, + double sampleRate, + int bufferSize) + { + close(); + + lastError = String::empty; + int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; + + activeOutputChans = outputChannels; + activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); + numOutputChannels = activeOutputChans.countNumberOfSetBits(); + + activeInputChans = inputChannels; + activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); + numInputChannels = activeInputChans.countNumberOfSetBits(); + + // TODO actually open it here, setting lastError if it fails.. + + actualBufferSize = 0; // whatever is possible based on preferredBufferSize + + isRunning = true; + + return lastError; + } + + void close() + { + if (isRunning) + { + isRunning = false; + + // TODO close the device here + } + } + + int getOutputLatencyInSamples() + { + return 0; // TODO should be found out if poss + } + + int getInputLatencyInSamples() + { + return 0; // TODO should be found out if poss + } + + bool isOpen() { return isRunning; } + int getCurrentBufferSizeSamples() { return actualBufferSize; } + int getCurrentBitDepth() { return 16; } + double getCurrentSampleRate() { return sampleRate; } + const BigInteger getActiveOutputChannels() const { return activeOutputChans; } + const BigInteger getActiveInputChannels() const { return activeInputChans; } + const String getLastError() { return lastError; } + bool isPlaying() { return isRunning && callback != 0; } + + // TODO the start/stop methods are probably ok to leave as they are... + + void start (AudioIODeviceCallback* newCallback) + { + if (isRunning && callback != newCallback) + { + if (newCallback != 0) + newCallback->audioDeviceAboutToStart (this); + + const ScopedLock sl (callbackLock); + callback = newCallback; + } + } + + void stop() + { + if (isRunning) + { + AudioIODeviceCallback* lastCallback; + + { + const ScopedLock sl (callbackLock); + lastCallback = callback; + callback = 0; + } + + if (lastCallback != 0) + lastCallback->audioDeviceStopped(); + } + } + +private: + + CriticalSection callbackLock; + AudioIODeviceCallback* callback; + double sampleRate; + int numInputChannels, numOutputChannels; + int actualBufferSize; + bool isRunning; + String lastError; + BigInteger activeOutputChans, activeInputChans; + + JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice); +}; + +// TODO assuming there's only one type of audio device, this class is pretty much ok as it is... +class AndroidAudioIODeviceType : public AudioIODeviceType +{ +public: + AndroidAudioIODeviceType() + : AudioIODeviceType ("Android Audio") + { + } + + void scanForDevices() + { + } + + const StringArray getDeviceNames (bool wantInputNames) const + { + StringArray s; + s.add ("Android Audio"); + return s; + } + + int getDefaultDeviceIndex (bool forInput) const + { + return 0; + } + + int getIndexOfDevice (AudioIODevice* device, bool asInput) const + { + return device != 0 ? 0 : -1; + } + + bool hasSeparateInputsAndOutputs() const { return false; } + + AudioIODevice* createDevice (const String& outputDeviceName, + const String& inputDeviceName) + { + if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) + return new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName + : inputDeviceName); + + return 0; + } + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType); +}; + +AudioIODeviceType* juce_createAudioIODeviceType_Android() +{ + return new AndroidAudioIODeviceType(); +} + +#endif +/*** End of inlined file: juce_android_Audio.cpp ***/ + + +/*** Start of inlined file: juce_android_CameraDevice.cpp ***/ +// (This file gets included by juce_android_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_USE_CAMERA + +// TODO this is an internal data structure that can be used to hold all the platform-specific handles and stuff +class AndroidCameraInternal +{ +public: + AndroidCameraInternal() + { + } + + ~AndroidCameraInternal() + { + } + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidCameraInternal); +}; + +CameraDevice::CameraDevice (const String& name_, int /*index*/) + : name (name_) +{ + internal = new AndroidCameraInternal(); + + // TODO probably initialisation here +} + +CameraDevice::~CameraDevice() +{ + stopRecording(); + delete static_cast (internal); + internal = 0; +} + +Component* CameraDevice::createViewerComponent() +{ + // TODO returns some kind of Component that can preview the camera + + return 0; +} + +const String CameraDevice::getFileExtension() +{ + return ".m4a"; // TODO correct? +} + +void CameraDevice::startRecordingToFile (const File& file, int quality) +{ + // TODO +} + +const Time CameraDevice::getTimeOfFirstRecordedFrame() const +{ + // TODO + return Time(); +} + +void CameraDevice::stopRecording() +{ + // TODO +} + +void CameraDevice::addListener (Listener* listenerToAdd) +{ + // TODO +} + +void CameraDevice::removeListener (Listener* listenerToRemove) +{ + // TODO +} + +const StringArray CameraDevice::getAvailableDevices() +{ + StringArray devs; + + // TODO scan for devices.. + + return devs; +} + +CameraDevice* CameraDevice::openDevice (int index, + int minWidth, int minHeight, + int maxWidth, int maxHeight) +{ + // TODO opens a device, returning a new CameraDevice object if this works. + + return 0; +} + +#endif +/*** End of inlined file: juce_android_CameraDevice.cpp ***/ + +END_JUCE_NAMESPACE + +#endif +/*** End of inlined file: juce_android_NativeCode.cpp ***/ + + #endif #endif diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 9c43b7c6e1..2a683be890 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -73,7 +73,7 @@ namespace JuceDummyNamespace {} */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 17 +#define JUCE_BUILDNUMBER 18 /** Current Juce version number. @@ -107,7 +107,7 @@ namespace JuceDummyNamespace {} #define JUCE_WINDOWS 1 #elif defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 -#elif defined(__APPLE_CPP__) || defined(__APPLE_CC__) +#elif defined (__APPLE_CPP__) || defined(__APPLE_CC__) #include // (needed to find out what platform we're using) #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR @@ -116,6 +116,9 @@ namespace JuceDummyNamespace {} #else #define JUCE_MAC 1 #endif +#elif defined (JUCE_ANDROID) + #undef JUCE_ANDROID + #define JUCE_ANDROID 1 #else #error "Unknown platform!" #endif @@ -184,15 +187,19 @@ namespace JuceDummyNamespace {} #endif -#if JUCE_LINUX +#if JUCE_LINUX || JUCE_ANDROID #ifdef _DEBUG #define JUCE_DEBUG 1 #endif // Allow override for big-endian Linux platforms - #ifndef JUCE_BIG_ENDIAN + #if defined (__LITTLE_ENDIAN__) || ! defined (JUCE_BIG_ENDIAN) #define JUCE_LITTLE_ENDIAN 1 + #undef JUCE_BIG_ENDIAN + #else + #undef JUCE_LITTLE_ENDIAN + #define JUCE_BIG_ENDIAN 1 #endif #if defined (__LP64__) || defined (_LP64) @@ -201,7 +208,9 @@ namespace JuceDummyNamespace {} #define JUCE_32BIT 1 #endif - #define JUCE_INTEL 1 + #if __MMX__ || __SSE__ || __amd64__ + #define JUCE_INTEL 1 + #endif #endif // Compiler type macros. @@ -836,6 +845,11 @@ namespace JuceDummyNamespace {} #endif #endif +#if JUCE_ANDROID + #include + #include +#endif + // DLL building settings on Win32 #if JUCE_MSVC #ifdef JUCE_DLL_BUILD @@ -1089,9 +1103,6 @@ typedef unsigned int uint32; typedef unsigned int pointer_sized_uint; #endif -/** A platform-independent unicode character type. */ -typedef wchar_t juce_wchar; - // Some indispensible min/max functions /** Returns the larger of two values. */ @@ -1339,6 +1350,8 @@ inline bool juce_isfinite (FloatingPointType value) { #if JUCE_WINDOWS return _finite (value); + #elif JUCE_ANDROID + return isfinite (value); #else return std::isfinite (value); #endif @@ -1546,18 +1559,22 @@ inline uint32 ByteOrder::swap (uint32 n) { #if JUCE_MAC || JUCE_IOS return OSSwapInt32 (n); - #elif JUCE_GCC + #elif JUCE_GCC && JUCE_INTEL asm("bswap %%eax" : "=a"(n) : "a"(n)); return n; #elif JUCE_USE_INTRINSICS return _byteswap_ulong (n); - #else + #elif JUCE_MSVC __asm { mov eax, n bswap eax mov n, eax } return n; + #elif JUCE_ANDROID + return bswap_32 (n); + #else + return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8); #endif } @@ -1621,28 +1638,34 @@ inline void ByteOrder::bigEndian24BitToChars (const int value, char* const destB #ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ #define __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ -#define JUCE_T(stringLiteral) (L##stringLiteral) -typedef juce_wchar tchar; +#if JUCE_ANDROID && ! DOXYGEN + typedef uint32 juce_wchar; + #define JUCE_T(stringLiteral) CharPointer_UTF8 (stringLiteral) +#else + /** A platform-independent unicode character type. */ + typedef wchar_t juce_wchar; + #define JUCE_T(stringLiteral) (L##stringLiteral) +#endif #if ! JUCE_DONT_DEFINE_MACROS + /** The 'T' macro allows a literal string to be compiled as unicode. -/** The 'T' macro allows a literal string to be compiled as unicode. - - If you write your string literals in the form T("xyz"), it will be compiled as L"xyz" - or "xyz", depending on which representation is best for the String class to work with. - - Because the 'T' symbol is occasionally used inside 3rd-party library headers which you - may need to include after juce.h, you can use the juce_withoutMacros.h file (in - the juce/src directory) to avoid defining this macro. See the comments in - juce_withoutMacros.h for more info. -*/ -#define T(stringLiteral) JUCE_T(stringLiteral) + If you write your string literals in the form T("xyz"), it will be compiled as L"xyz" + or "xyz", depending on which representation is best for the String class to work with. + Because the 'T' symbol is occasionally used inside 3rd-party library headers which you + may need to include after juce.h, you can use the juce_withoutMacros.h file (in + the juce/src directory) to avoid defining this macro. See the comments in + juce_withoutMacros.h for more info. + */ + #define T(stringLiteral) JUCE_T(stringLiteral) #endif +#undef max +#undef min + /** - A set of methods for manipulating characters and character strings, with - duplicate methods to handle 8-bit and unicode characters. + A set of methods for manipulating characters and character strings. These are defined as wrappers around the basic C string handlers, to provide a clean, cross-platform layer, (because various platforms differ in the @@ -1653,94 +1676,1441 @@ typedef juce_wchar tchar; class JUCE_API CharacterFunctions { public: - static int length (const char* s) throw(); - static int length (const juce_wchar* s) throw(); - static void copy (char* dest, const char* src, int maxBytes) throw(); - static void copy (juce_wchar* dest, const juce_wchar* src, int maxChars) throw(); + static juce_wchar toUpperCase (juce_wchar character) throw(); + static juce_wchar toLowerCase (juce_wchar character) throw(); + + static bool isUpperCase (juce_wchar character) throw(); + static bool isLowerCase (juce_wchar character) throw(); + + static bool isWhitespace (char character) throw(); + static bool isWhitespace (juce_wchar character) throw(); + + static bool isDigit (char character) throw(); + static bool isDigit (juce_wchar character) throw(); + + static bool isLetter (char character) throw(); + static bool isLetter (juce_wchar character) throw(); + + static bool isLetterOrDigit (char character) throw(); + static bool isLetterOrDigit (juce_wchar character) throw(); + + /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */ + static int getHexDigitValue (juce_wchar digit) throw(); - static void copy (juce_wchar* dest, const char* src, int maxChars) throw(); - static void copy (char* dest, const juce_wchar* src, int maxBytes) throw(); - static int bytesRequiredForCopy (const juce_wchar* src) throw(); + template + static double getDoubleValue (const CharPointerType& text) throw() + { + double result[3] = { 0, 0, 0 }, accumulator[2] = { 0, 0 }; + int exponentAdjustment[2] = { 0, 0 }, exponentAccumulator[2] = { -1, -1 }; + int exponent = 0, decPointIndex = 0, digit = 0; + int lastDigit = 0, numSignificantDigits = 0; + bool isNegative = false, digitsFound = false; + const int maxSignificantDigits = 15 + 2; - static void append (char* dest, const char* src) throw(); - static void append (juce_wchar* dest, const juce_wchar* src) throw(); + CharPointerType s (text.findEndOfWhitespace()); + juce_wchar c = *s; - static int compare (const char* s1, const char* s2) throw(); - static int compare (const juce_wchar* s1, const juce_wchar* s2) throw(); - static int compare (const juce_wchar* s1, const char* s2) throw(); - static int compare (const char* s1, const juce_wchar* s2) throw(); + switch (c) + { + case '-': isNegative = true; // fall-through.. + case '+': c = *++s; + } + + switch (c) + { + case 'n': + case 'N': + if ((s[1] == 'a' || s[1] == 'A') && (s[2] == 'n' || s[2] == 'N')) + return std::numeric_limits::quiet_NaN(); + break; + + case 'i': + case 'I': + if ((s[1] == 'n' || s[1] == 'N') && (s[2] == 'f' || s[2] == 'F')) + return std::numeric_limits::infinity(); + break; + } + + for (;;) + { + if (s.isDigit()) + { + lastDigit = digit; + digit = s.getAndAdvance() - '0'; + digitsFound = true; + + if (decPointIndex != 0) + exponentAdjustment[1]++; + + if (numSignificantDigits == 0 && digit == 0) + continue; + + if (++numSignificantDigits > maxSignificantDigits) + { + if (digit > 5) + ++accumulator [decPointIndex]; + else if (digit == 5 && (lastDigit & 1) != 0) + ++accumulator [decPointIndex]; + + if (decPointIndex > 0) + exponentAdjustment[1]--; + else + exponentAdjustment[0]++; + + while (s.isDigit()) + { + ++s; + if (decPointIndex == 0) + exponentAdjustment[0]++; + } + } + else + { + const double maxAccumulatorValue = (double) ((std::numeric_limits::max() - 9) / 10); + if (accumulator [decPointIndex] > maxAccumulatorValue) + { + result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex]) + + accumulator [decPointIndex]; + accumulator [decPointIndex] = 0; + exponentAccumulator [decPointIndex] = 0; + } + + accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit; + exponentAccumulator [decPointIndex]++; + } + } + else if (decPointIndex == 0 && *s == '.') + { + ++s; + decPointIndex = 1; + + if (numSignificantDigits > maxSignificantDigits) + { + while (s.isDigit()) + ++s; + break; + } + } + else + { + break; + } + } + + result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0]; + + if (decPointIndex != 0) + result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1]; + + c = *s; + if ((c == 'e' || c == 'E') && digitsFound) + { + bool negativeExponent = false; + + switch (*++s) + { + case '-': negativeExponent = true; // fall-through.. + case '+': ++s; + } + + while (s.isDigit()) + exponent = (exponent * 10) + (s.getAndAdvance() - '0'); + + if (negativeExponent) + exponent = -exponent; + } - static int compare (const char* s1, const char* s2, int maxChars) throw(); - static int compare (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw(); + double r = mulexp10 (result[0], exponent + exponentAdjustment[0]); + if (decPointIndex != 0) + r += mulexp10 (result[1], exponent - exponentAdjustment[1]); - static int compareIgnoreCase (const char* s1, const char* s2) throw(); - static int compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2) throw(); - static int compareIgnoreCase (const juce_wchar* s1, const char* s2) throw(); + return isNegative ? -r : r; + } - static int compareIgnoreCase (const char* s1, const char* s2, int maxChars) throw(); - static int compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw(); + template + static IntType getIntValue (const CharPointerType& text) throw() + { + IntType v = 0; + CharPointerType s (text.findEndOfWhitespace()); - static const char* find (const char* haystack, const char* needle) throw(); - static const juce_wchar* find (const juce_wchar* haystack, const juce_wchar* needle) throw(); + const bool isNeg = *s == '-'; + if (isNeg) + ++s; - static int indexOfChar (const char* haystack, char needle, bool ignoreCase) throw(); - static int indexOfChar (const juce_wchar* haystack, juce_wchar needle, bool ignoreCase) throw(); + for (;;) + { + const juce_wchar c = s.getAndAdvance(); - static int indexOfCharFast (const char* haystack, char needle) throw(); - static int indexOfCharFast (const juce_wchar* haystack, juce_wchar needle) throw(); + if (c >= '0' && c <= '9') + v = v * 10 + (IntType) (c - '0'); + else + break; + } - static int getIntialSectionContainingOnly (const char* text, const char* allowedChars) throw(); - static int getIntialSectionContainingOnly (const juce_wchar* text, const juce_wchar* allowedChars) throw(); + return isNeg ? -v : v; + } static int ftime (char* dest, int maxChars, const char* format, const struct tm* tm) throw(); static int ftime (juce_wchar* dest, int maxChars, const juce_wchar* format, const struct tm* tm) throw(); - static int getIntValue (const char* s) throw(); - static int getIntValue (const juce_wchar* s) throw(); + template + static void copyAndAdvance (DestCharPointerType& dest, SrcCharPointerType src) throw() + { + juce_wchar c; - static int64 getInt64Value (const char* s) throw(); - static int64 getInt64Value (const juce_wchar* s) throw(); + do + { + c = src.getAndAdvance(); + dest.write (c); + } + while (c != 0); + } - static double getDoubleValue (const char* s) throw(); - static double getDoubleValue (const juce_wchar* s) throw(); + template + static int copyAndAdvanceUpToBytes (DestCharPointerType& dest, SrcCharPointerType src, int maxBytes) throw() + { + int numBytesDone = 0; - static char toUpperCase (char character) throw(); - static juce_wchar toUpperCase (juce_wchar character) throw(); - static void toUpperCase (char* s) throw(); + for (;;) + { + const juce_wchar c = src.getAndAdvance(); + const size_t bytesNeeded = DestCharPointerType::getBytesRequiredFor (c); - static void toUpperCase (juce_wchar* s) throw(); - static bool isUpperCase (char character) throw(); - static bool isUpperCase (juce_wchar character) throw(); + maxBytes -= bytesNeeded; + if (maxBytes < 0) + break; - static char toLowerCase (char character) throw(); - static juce_wchar toLowerCase (juce_wchar character) throw(); - static void toLowerCase (char* s) throw(); - static void toLowerCase (juce_wchar* s) throw(); - static bool isLowerCase (char character) throw(); - static bool isLowerCase (juce_wchar character) throw(); + numBytesDone += bytesNeeded; + dest.write (c); + if (c == 0) + break; + } - static bool isWhitespace (char character) throw(); - static bool isWhitespace (juce_wchar character) throw(); + return numBytesDone; + } - static bool isDigit (char character) throw(); - static bool isDigit (juce_wchar character) throw(); + template + static void copyAndAdvanceUpToNumChars (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) throw() + { + while (--maxChars >= 0) + { + const juce_wchar c = src.getAndAdvance(); + dest.write (c); + if (c == 0) + break; + } + } - static bool isLetter (char character) throw(); - static bool isLetter (juce_wchar character) throw(); + template + static int compare (CharPointerType1 s1, CharPointerType2 s2) throw() + { + for (;;) + { + const int c1 = (int) s1.getAndAdvance(); + const int c2 = (int) s2.getAndAdvance(); - static bool isLetterOrDigit (char character) throw(); - static bool isLetterOrDigit (juce_wchar character) throw(); + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } - /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legel - hex digit. - */ - static int getHexDigitValue (juce_wchar digit) throw(); + return 0; + } + + template + static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) throw() + { + while (--maxChars >= 0) + { + const int c1 = (int) s1.getAndAdvance(); + const int c2 = (int) s2.getAndAdvance(); + + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } + + return 0; + } + + template + static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) throw() + { + for (;;) + { + int c1 = s1.toUpperCase(); + int c2 = s2.toUpperCase(); + ++s1; + ++s2; + + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } + + return 0; + } + + template + static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) throw() + { + while (--maxChars >= 0) + { + int c1 = s1.toUpperCase(); + int c2 = s2.toUpperCase(); + ++s1; + ++s2; + + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } + + return 0; + } + + template + static int indexOf (CharPointerType1 haystack, const CharPointerType2& needle) throw() + { + int index = 0; + const int needleLength = needle.length(); + + for (;;) + { + if (haystack.compareUpTo (needle, needleLength) == 0) + return index; + + if (haystack.getAndAdvance() == 0) + return -1; + + ++index; + } + } + + template + static int indexOfChar (Type text, const juce_wchar charToFind) throw() + { + int i = 0; + + while (! text.isEmpty()) + { + if (text.getAndAdvance() == charToFind) + return i; + + ++i; + } + + return -1; + } + + template + static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) throw() + { + charToFind = CharacterFunctions::toLowerCase (charToFind); + int i = 0; + + while (! text.isEmpty()) + { + if (text.toLowerCase() == charToFind) + return i; + + ++text; + ++i; + } + + return -1; + } + + template + static Type findEndOfWhitespace (const Type& text) throw() + { + Type p (text); + + while (p.isWhitespace()) + ++p; + + return p; + } + +private: + static double mulexp10 (const double value, int exponent) throw(); }; #endif // __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ /*** End of inlined file: juce_CharacterFunctions.h ***/ +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4514 4996) +#endif + + +/*** Start of inlined file: juce_CharPointer_UTF8.h ***/ +#ifndef __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ +#define __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ + +/** + Wraps a pointer to a null-terminated UTF-8 character string, and provides + various methods to operate on the data. + @see CharPointer_UTF16, CharPointer_UTF32 +*/ +class CharPointer_UTF8 +{ +public: + typedef char CharType; + + inline CharPointer_UTF8 (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_UTF8 (const CharPointer_UTF8& other) throw() + : data (other.data) + { + } + + inline CharPointer_UTF8& operator= (const CharPointer_UTF8& other) throw() + { + data = other.data; + return *this; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns true if this pointer is pointing to a null character. */ + inline bool isEmpty() const throw() { return *data == 0; } + + /** Returns the unicode character that this pointer is pointing to. */ + juce_wchar operator*() const throw() + { + const char byte = *data; + + if (byte >= 0) + return byte; + + juce_wchar n = byte; + juce_wchar mask = 0x7f; + juce_wchar bit = 0x40; + size_t numExtraValues = 0; + + while ((n & bit) != 0 && bit > 0x10) + { + mask >>= 1; + ++numExtraValues; + bit >>= 1; + } + + n &= mask; + + for (size_t i = 1; i <= numExtraValues; ++i) + { + const juce_wchar nextByte = data [i]; + + if ((nextByte & 0xc0) != 0x80) + break; + + n <<= 6; + n |= (nextByte & 0x3f); + } + + return n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF8& operator++() throw() + { + const char n = *data++; + + if (n < 0) + { + juce_wchar bit = 0x40; + + while ((n & bit) != 0 && bit > 0x10) + { + ++data; + bit >>= 1; + } + } + + return *this; + } + + /** Returns the character that this pointer is currently pointing to, and then + advances the pointer to point to the next character. */ + juce_wchar getAndAdvance() throw() + { + const char byte = *data++; + + if (byte >= 0) + return byte; + + uint32 n = (uint32) (uint8) byte; + uint32 mask = 0x7f; + uint32 bit = 0x40; + int numExtraValues = 0; + + while ((n & bit) != 0 && bit > 0x10) + { + mask >>= 1; + ++numExtraValues; + bit >>= 1; + } + + n &= mask; + + while (--numExtraValues >= 0) + { + const uint32 nextByte = (uint32) (uint8) *data++; + + if ((nextByte & 0xc0) != 0x80) + break; + + n <<= 6; + n |= (nextByte & 0x3f); + } + + return (juce_wchar) n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF8 operator++ (int) throw() + { + CharPointer_UTF8 temp (*this); + ++*this; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + void operator+= (int numToSkip) throw() + { + jassert (numToSkip >= 0); + + while (--numToSkip >= 0) + ++*this; + } + + /** Returns the character at a given character index from the start of the string. */ + juce_wchar operator[] (int characterIndex) const throw() + { + CharPointer_UTF8 p (*this); + p += characterIndex; + return *p; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_UTF8 operator+ (int numToSkip) const throw() + { + CharPointer_UTF8 p (*this); + p += numToSkip; + return p; + } + + /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ + void write (const juce_wchar charToWrite) throw() + { + const uint32 c = (uint32) charToWrite; + + if (c >= 0x80) + { + int numExtraBytes = 1; + if (c >= 0x800) + { + ++numExtraBytes; + if (c >= 0x10000) + ++numExtraBytes; + } + + *data++ = (CharType) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6))); + + while (--numExtraBytes >= 0) + *data++ = (CharType) (0x80 | (0x3f & (c >> (numExtraBytes * 6)))); + } + else + { + *data++ = (CharType) c; + } + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + const CharType* d = data; + size_t count = 0; + + for (;;) + { + const int n = *d++; + + if ((n & 0x80) != 0) + { + int bit = 0x40; + + while ((n & bit) != 0) + { + ++d; + bit >>= 1; + + if (bit == 0) + break; // illegal utf-8 sequence + } + } + else if (n == 0) + break; + + ++count; + } + + return count; + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return strlen (data) + 1; + } + + /** Returns the number of bytes that would be needed to represent the given + unicode character in this encoding format. + */ + static size_t getBytesRequiredFor (const juce_wchar charToWrite) throw() + { + size_t num = 1; + const uint32 c = (uint32) charToWrite; + + if (c >= 0x80) + { + ++num; + if (c >= 0x800) + { + ++num; + if (c >= 0x10000) + ++num; + } + } + + return num; + } + + /** Returns the number of bytes that would be needed to represent the given + string in this encoding format. + The value returned does NOT include the terminating null character. + */ + template + static size_t getBytesRequiredFor (CharPointer text) throw() + { + size_t count = 0; + juce_wchar n; + + while ((n = text.getAndAdvance()) != 0) + count += getBytesRequiredFor (n); + + return count; + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_UTF8 findTerminatingNull() const throw() + { + return CharPointer_UTF8 (data + strlen (data)); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void copyAndAdvance (const CharPointer& src) throw() + { + CharacterFunctions::copyAndAdvance (*this, src); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void copyAndAdvance (const CharPointer_UTF8& src) throw() + { + data = (CharType*) strcpy ((char*) data, src.data); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int copyAndAdvanceUpToBytes (const CharPointer& src, int maxBytes) throw() + { + return CharacterFunctions::copyAndAdvanceUpToBytes (*this, src, maxBytes); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxChars parameter specifies the maximum number of characters that can be + written to the destination buffer before stopping (including the terminating null). + */ + template + void copyAndAdvanceUpToNumChars (const CharPointer& src, int maxChars) throw() + { + CharacterFunctions::copyAndAdvanceUpToNumChars (*this, src, maxChars); + } + + /** Compares this string with another one. */ + template + int compare (const CharPointer& other) const throw() + { + return CharacterFunctions::compare (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const throw() + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + /** Compares this string with another one. */ + int compareIgnoreCase (const CharPointer_UTF8& other) const throw() + { + #if JUCE_WINDOWS + return stricmp (data, other.data); + #else + return strcasecmp (data, other.data); + #endif + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareIgnoreCaseUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); + } + + /** Compares this string with another one, up to a specified number of characters. */ + int compareIgnoreCaseUpTo (const CharPointer_UTF8& other, int maxChars) const throw() + { + #if JUCE_WINDOWS + return strnicmp (data, other.data, maxChars); + #else + return strncasecmp (data, other.data, maxChars); + #endif + } + + /** Returns the character index of a substring, or -1 if it isn't found. */ + template + int indexOf (const CharPointer& stringToFind) const throw() + { + return CharacterFunctions::indexOf (*this, stringToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind) const throw() + { + return CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind, const bool ignoreCase) const throw() + { + return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) + : CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns true if the first character of this string is whitespace. */ + bool isWhitespace() const throw() { return *data == ' ' || (*data <= 13 && *data >= 9); } + /** Returns true if the first character of this string is a digit. */ + bool isDigit() const throw() { return *data >= '0' && *data <= '9'; } + /** Returns true if the first character of this string is a letter. */ + bool isLetter() const throw() { return CharacterFunctions::isLetter (operator*()) != 0; } + /** Returns true if the first character of this string is a letter or digit. */ + bool isLetterOrDigit() const throw() { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; } + /** Returns true if the first character of this string is upper-case. */ + bool isUpperCase() const throw() { return CharacterFunctions::isUpperCase (operator*()) != 0; } + /** Returns true if the first character of this string is lower-case. */ + bool isLowerCase() const throw() { return CharacterFunctions::isLowerCase (operator*()) != 0; } + + /** Returns an upper-case version of the first character of this string. */ + juce_wchar toUpperCase() const throw() { return CharacterFunctions::toUpperCase (operator*()); } + /** Returns a lower-case version of the first character of this string. */ + juce_wchar toLowerCase() const throw() { return CharacterFunctions::toLowerCase (operator*()); } + + /** Parses this string as a 32-bit integer. */ + int getIntValue32() const throw() { return atoi (data); } + + /** Parses this string as a 64-bit integer. */ + int64 getIntValue64() const throw() + { + #if JUCE_LINUX || JUCE_ANDROID + return atoll (data); + #elif JUCE_WINDOWS + return _atoi64 (data); + #else + return CharacterFunctions::getIntValue (*this); + #endif + } + + /** Parses this string as a floating point double. */ + double getDoubleValue() const throw() { return CharacterFunctions::getDoubleValue (*this); } + + /** Returns the first non-whitespace character in the string. */ + CharPointer_UTF8 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + +private: + CharType* data; +}; + +#endif // __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ +/*** End of inlined file: juce_CharPointer_UTF8.h ***/ + + +/*** Start of inlined file: juce_CharPointer_UTF16.h ***/ +#ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ +#define __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ + +/** + Wraps a pointer to a null-terminated UTF-16 character string, and provides + various methods to operate on the data. + @see CharPointer_UTF8, CharPointer_UTF32 +*/ +class CharPointer_UTF16 +{ +public: + typedef int16 CharType; + + inline CharPointer_UTF16 (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_UTF16 (const CharPointer_UTF16& other) throw() + : data (other.data) + { + } + + inline CharPointer_UTF16& operator= (const CharPointer_UTF16& other) throw() + { + data = other.data; + return *this; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns true if this pointer is pointing to a null character. */ + inline bool isEmpty() const throw() { return *data == 0; } + + /** Returns the unicode character that this pointer is pointing to. */ + juce_wchar operator*() const throw() + { + uint32 n = (uint32) (uint16) *data; + + if (n >= 0xd800 && n <= 0xdfff) + n = 0x10000 + (((n & ~0xd800) << 10) | (data[1] & ~0xdc00)); + + return (juce_wchar) n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF16& operator++() throw() + { + const juce_wchar n = *data++; + + if (n >= 0xd800 && n <= 0xdfff) + ++data; + + return *this; + } + + /** Returns the character that this pointer is currently pointing to, and then + advances the pointer to point to the next character. */ + juce_wchar getAndAdvance() throw() + { + uint32 n = (uint32) (uint16) *data++; + + if (n >= 0xd800 && n <= 0xdfff) + { + n = 0x10000 + (((n & ~0xd800) << 10) | (*data & ~0xdc00)); + ++data; + } + + return (juce_wchar) n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF16 operator++ (int) throw() + { + CharPointer_UTF16 temp (*this); + ++*this; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + void operator+= (int numToSkip) throw() + { + jassert (numToSkip >= 0); + + while (--numToSkip >= 0) + ++*this; + } + + /** Returns the character at a given character index from the start of the string. */ + juce_wchar operator[] (int characterIndex) const throw() + { + CharPointer_UTF16 p (*this); + p += characterIndex; + return *p; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_UTF16 operator+ (int numToSkip) const throw() + { + CharPointer_UTF16 p (*this); + p += numToSkip; + return p; + } + + /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ + void write (const juce_wchar charToWrite) throw() + { + if (charToWrite < 0xd800 || (charToWrite > 0xdfff && charToWrite <= 0xffff)) + { + *data++ = (CharType) charToWrite; + } + else + { + *data++ = (CharType) ((0xd800 - (0x10000 >> 10)) + (charToWrite >> 10)); + *data++ = (CharType) (0xdc00 + (charToWrite & 0x3ff)); + } + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + const CharType* d = data; + size_t count = 0; + + for (;;) + { + const int n = *d++; + + if (n >= 0xd800 && n <= 0xdfff) + ++d; + else if (n == 0) + break; + + ++count; + } + + return count; + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return sizeof (CharType) * (findNullIndex (data) + 1); + } + + /** Returns the number of bytes that would be needed to represent the given + unicode character in this encoding format. + */ + static size_t getBytesRequiredFor (const juce_wchar charToWrite) throw() + { + return (charToWrite < 0xd800 || (charToWrite > 0xdfff && charToWrite <= 0xffff)) + ? 1 : 2; + } + + /** Returns the number of bytes that would be needed to represent the given + string in this encoding format. + The value returned does NOT include the terminating null character. + */ + template + static size_t getBytesRequiredFor (CharPointer text) throw() + { + size_t count = 0; + juce_wchar n; + + while ((n = text.getAndAdvance()) != 0) + count += getBytesRequiredFor (n); + + return count; + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_UTF16 findTerminatingNull() const throw() + { + const CharType* t = data; + + while (*t != 0) + ++t; + + return CharPointer_UTF16 (t); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void copyAndAdvance (const CharPointer& src) throw() + { + CharacterFunctions::copyAndAdvance (*this, src); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void copyAndAdvance (const CharPointer_UTF16& src) throw() + { + const CharType* s = src.data; + + while ((*data = *s) != 0) + { + ++data; + ++s; + } + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int copyAndAdvanceUpToBytes (const CharPointer& src, int maxBytes) throw() + { + return CharacterFunctions::copyAndAdvanceUpToBytes (*this, src, maxBytes); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxChars parameter specifies the maximum number of characters that can be + written to the destination buffer before stopping (including the terminating null). + */ + template + void copyAndAdvanceUpToNumChars (const CharPointer& src, int maxChars) throw() + { + CharacterFunctions::copyAndAdvanceUpToNumChars (*this, src, maxChars); + } + + /** Compares this string with another one. */ + template + int compare (const CharPointer& other) const throw() + { + return CharacterFunctions::compare (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const throw() + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareIgnoreCaseUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); + } + + /** Returns the character index of a substring, or -1 if it isn't found. */ + template + int indexOf (const CharPointer& stringToFind) const throw() + { + return CharacterFunctions::indexOf (*this, stringToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind) const throw() + { + return CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind, const bool ignoreCase) const throw() + { + return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) + : CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns true if the first character of this string is whitespace. */ + bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (operator*()) != 0; } + /** Returns true if the first character of this string is a digit. */ + bool isDigit() const throw() { return CharacterFunctions::isDigit (operator*()) != 0; } + /** Returns true if the first character of this string is a letter. */ + bool isLetter() const throw() { return CharacterFunctions::isLetter (operator*()) != 0; } + /** Returns true if the first character of this string is a letter or digit. */ + bool isLetterOrDigit() const throw() { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; } + /** Returns true if the first character of this string is upper-case. */ + bool isUpperCase() const throw() { return CharacterFunctions::isUpperCase (operator*()) != 0; } + /** Returns true if the first character of this string is lower-case. */ + bool isLowerCase() const throw() { return CharacterFunctions::isLowerCase (operator*()) != 0; } + + /** Returns an upper-case version of the first character of this string. */ + juce_wchar toUpperCase() const throw() { return CharacterFunctions::toUpperCase (operator*()); } + /** Returns a lower-case version of the first character of this string. */ + juce_wchar toLowerCase() const throw() { return CharacterFunctions::toLowerCase (operator*()); } + + /** Parses this string as a 32-bit integer. */ + int getIntValue32() const throw() { return CharacterFunctions::getIntValue (*this); } + /** Parses this string as a 64-bit integer. */ + int64 getIntValue64() const throw() { return CharacterFunctions::getIntValue (*this); } + + /** Parses this string as a floating point double. */ + double getDoubleValue() const throw() { return CharacterFunctions::getDoubleValue (*this); } + + /** Returns the first non-whitespace character in the string. */ + CharPointer_UTF16 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + +private: + CharType* data; + + static int findNullIndex (const CharType* const t) throw() + { + int n = 0; + + while (t[n] != 0) + ++n; + + return n; + } +}; + +#endif // __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ +/*** End of inlined file: juce_CharPointer_UTF16.h ***/ + + +/*** Start of inlined file: juce_CharPointer_UTF32.h ***/ +#ifndef __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ +#define __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ + +/** + Wraps a pointer to a null-terminated UTF-32 character string, and provides + various methods to operate on the data. + @see CharPointer_UTF8, CharPointer_UTF16 +*/ +class CharPointer_UTF32 +{ +public: + typedef juce_wchar CharType; + + inline CharPointer_UTF32 (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_UTF32 (const CharPointer_UTF32& other) throw() + : data (other.data) + { + } + + inline CharPointer_UTF32& operator= (const CharPointer_UTF32& other) throw() + { + data = other.data; + return *this; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns true if this pointer is pointing to a null character. */ + inline bool isEmpty() const throw() { return *data == 0; } + + /** Returns the unicode character that this pointer is pointing to. */ + inline juce_wchar operator*() const throw() { return *data; } + + /** Moves this pointer along to the next character in the string. */ + inline CharPointer_UTF32& operator++() throw() + { + ++data; + return *this; + } + + /** Moves this pointer to the previous character in the string. */ + inline CharPointer_UTF32& operator--() throw() + { + --data; + return *this; + } + + /** Returns the character that this pointer is currently pointing to, and then + advances the pointer to point to the next character. */ + inline juce_wchar getAndAdvance() throw() { return *data++; } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF32 operator++ (int) throw() + { + CharPointer_UTF32 temp (*this); + ++*this; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + inline void operator+= (int numToSkip) throw() + { + data += numToSkip; + } + + inline void operator-= (int numToSkip) throw() + { + data -= numToSkip; + } + + /** Returns the character at a given character index from the start of the string. */ + inline juce_wchar operator[] (int characterIndex) const throw() + { + return data [characterIndex]; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_UTF32 operator+ (int numToSkip) const throw() + { + return CharPointer_UTF32 (data + numToSkip); + } + + /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ + CharPointer_UTF32 operator- (int numToSkip) const throw() + { + return CharPointer_UTF32 (data - numToSkip); + } + + /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ + inline void write (const juce_wchar charToWrite) throw() + { + *data++ = charToWrite; + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + #if JUCE_ANDROID + size_t n = 0; + while (data[n] == 0) + ++n; + return n; + #else + return wcslen (data); + #endif + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return sizeof (CharType) * (length() + 1); + } + + /** Returns the number of bytes that would be needed to represent the given + unicode character in this encoding format. + */ + static inline size_t getBytesRequiredFor (const juce_wchar) throw() + { + return sizeof (CharType); + } + + /** Returns the number of bytes that would be needed to represent the given + string in this encoding format. + The value returned does NOT include the terminating null character. + */ + template + static size_t getBytesRequiredFor (const CharPointer& text) throw() + { + return sizeof (CharType) * text.length(); + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_UTF32 findTerminatingNull() const throw() + { + return CharPointer_UTF32 (data + length()); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void copyAndAdvance (const CharPointer& src) throw() + { + CharacterFunctions::copyAndAdvance (*this, src); + } + + #if ! JUCE_ANDROID + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void copyAndAdvance (const CharPointer_UTF32& src) throw() + { + data = wcscpy (data, src.data); + } + #endif + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int copyAndAdvanceUpToBytes (const CharPointer& src, int maxBytes) throw() + { + return CharacterFunctions::copyAndAdvanceUpToBytes (*this, src, maxBytes); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxChars parameter specifies the maximum number of characters that can be + written to the destination buffer before stopping (including the terminating null). + */ + template + void copyAndAdvanceUpToNumChars (const CharPointer& src, int maxChars) throw() + { + CharacterFunctions::copyAndAdvanceUpToNumChars (*this, src, maxChars); + } + + /** Compares this string with another one. */ + template + int compare (const CharPointer& other) const throw() + { + return CharacterFunctions::compare (*this, other); + } + + #if ! JUCE_ANDROID + /** Compares this string with another one. */ + int compare (const CharPointer_UTF32& other) const throw() + { + return wcscmp (data, other.data); + } + #endif + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareIgnoreCaseUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); + } + + /** Returns the character index of a substring, or -1 if it isn't found. */ + template + int indexOf (const CharPointer& stringToFind) const throw() + { + return CharacterFunctions::indexOf (*this, stringToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind) const throw() + { + int i = 0; + + while (data[i] != 0) + { + if (data[i] == charToFind) + return i; + + ++i; + } + + return -1; + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind, const bool ignoreCase) const throw() + { + return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) + : CharacterFunctions::indexOfChar (*this, charToFind); + } + + #if JUCE_WINDOWS && ! DOXYGEN + int compareIgnoreCase (const CharPointer_UTF32& other) const throw() + { + return _wcsicmp (data, other.data); + } + + int compareIgnoreCaseUpTo (const CharPointer_UTF32& other, int maxChars) const throw() + { + return _wcsnicmp (data, other.data, maxChars); + } + + int indexOf (const CharPointer_UTF32& stringToFind) const throw() + { + const CharType* const t = wcsstr (data, stringToFind.getAddress()); + return t == 0 ? -1 : (t - data); + } + #endif + + /** Returns true if the first character of this string is whitespace. */ + bool isWhitespace() const { return CharacterFunctions::isWhitespace (*data) != 0; } + /** Returns true if the first character of this string is a digit. */ + bool isDigit() const { return CharacterFunctions::isDigit (*data) != 0; } + /** Returns true if the first character of this string is a letter. */ + bool isLetter() const { return CharacterFunctions::isLetter (*data) != 0; } + /** Returns true if the first character of this string is a letter or digit. */ + bool isLetterOrDigit() const { return CharacterFunctions::isLetterOrDigit (*data) != 0; } + /** Returns true if the first character of this string is upper-case. */ + bool isUpperCase() const { return CharacterFunctions::isUpperCase (*data) != 0; } + /** Returns true if the first character of this string is lower-case. */ + bool isLowerCase() const { return CharacterFunctions::isLowerCase (*data) != 0; } + + /** Returns an upper-case version of the first character of this string. */ + juce_wchar toUpperCase() const throw() { return CharacterFunctions::toUpperCase (*data); } + /** Returns a lower-case version of the first character of this string. */ + juce_wchar toLowerCase() const throw() { return CharacterFunctions::toLowerCase (*data); } + + /** Parses this string as a 32-bit integer. */ + int getIntValue32() const throw() + { + #if JUCE_WINDOWS + return _wtoi (data); + #else + return CharacterFunctions::getIntValue (*this); + #endif + } + + /** Parses this string as a 64-bit integer. */ + int64 getIntValue64() const throw() + { + #if JUCE_WINDOWS + return _wtoi64 (data); + #else + return CharacterFunctions::getIntValue (*this); + #endif + } + + /** Parses this string as a floating point double. */ + double getDoubleValue() const throw() { return CharacterFunctions::getDoubleValue (*this); } + + /** Returns the first non-whitespace character in the string. */ + CharPointer_UTF32 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + +private: + CharType* data; +}; + +#endif // __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ +/*** End of inlined file: juce_CharPointer_UTF32.h ***/ + +#if JUCE_MSVC + #pragma warning (pop) +#endif + class OutputStream; /** @@ -1824,8 +3194,6 @@ public: String& operator+= (juce_wchar characterToAppend); /** Appends a decimal number at the end of this string. */ String& operator+= (int numberToAppend); - /** Appends a decimal number at the end of this string. */ - String& operator+= (unsigned int numberToAppend); /** Appends a string at the end of this one. @@ -2655,7 +4023,8 @@ public: */ const char* toCString() const; - /** Returns the number of bytes + /** Returns the number of bytes required to represent this string as C-string. + The number returned does NOT include the trailing zero. */ int getNumBytesAsCString() const throw(); @@ -2741,6 +4110,7 @@ private: void createInternal (const juce_wchar* text, size_t numChars); void appendInternal (const juce_wchar* text, int numExtraChars); + void* createSpaceAtEndOfBuffer (size_t numExtraBytes) const; // This private cast operator should prevent strings being accidentally cast // to bools (this is possible because the compiler can add an implicit cast @@ -2784,12 +4154,8 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, short number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int number); /** Appends a decimal number at the end of a string. */ -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, unsigned int number); -/** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, long number); /** Appends a decimal number at the end of a string. */ -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, unsigned long number); -/** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, float number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, double number); @@ -3112,6 +4478,8 @@ inline Type Atomic::get() const throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchangeAdd ((volatile long*) &value, (long) 0)) : castFrom64Bit ((int64) juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) 0)); + #elif JUCE_ANDROID + return castFrom32Bit (__atomic_cmpxchg (castTo32Bit (value), castTo32Bit (value), (volatile int*) &value)); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) : castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); @@ -3121,7 +4489,9 @@ inline Type Atomic::get() const throw() template inline Type Atomic::exchange (const Type newValue) throw() { - #if JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC + #if JUCE_ANDROID + return castFrom32Bit (__atomic_swap (castTo32Bit (newValue), (volatile int*) &value)); + #elif JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC Type currentVal = value; while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } return currentVal; @@ -3140,6 +4510,14 @@ inline Type Atomic::operator+= (const Type amountToAdd) throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) (juce_InterlockedExchangeAdd ((volatile long*) &value, (long) amountToAdd) + (long) amountToAdd) : (Type) (juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) amountToAdd) + (__int64) amountToAdd); + #elif JUCE_ANDROID + for (;;) + { + const Type oldValue (value); + const Type newValue (oldValue + amountToAdd); + if (compareAndSetBool (newValue, oldValue)) + return newValue; + } #elif JUCE_ATOMICS_GCC return (Type) __sync_add_and_fetch (&value, amountToAdd); #endif @@ -3160,6 +4538,8 @@ inline Type Atomic::operator++() throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedIncrement ((volatile long*) &value) : (Type) juce_InterlockedIncrement64 ((volatile __int64*) &value); + #elif JUCE_ANDROID + return (Type) __atomic_inc (&value); #elif JUCE_ATOMICS_GCC return (Type) __sync_add_and_fetch (&value, 1); #endif @@ -3174,6 +4554,8 @@ inline Type Atomic::operator--() throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedDecrement ((volatile long*) &value) : (Type) juce_InterlockedDecrement64 ((volatile __int64*) &value); + #elif JUCE_ANDROID + return (Type) __atomic_dec (&value); #elif JUCE_ATOMICS_GCC return (Type) __sync_add_and_fetch (&value, -1); #endif @@ -3185,7 +4567,7 @@ inline bool Atomic::compareAndSetBool (const Type newValue, const Type val #if JUCE_ATOMICS_MAC return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); - #elif JUCE_ATOMICS_WINDOWS + #elif JUCE_ATOMICS_WINDOWS || JUCE_ANDROID return compareAndSetValue (newValue, valueToCompare) == valueToCompare; #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) @@ -3210,6 +4592,8 @@ inline Type Atomic::compareAndSetValue (const Type newValue, const Type va #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedCompareExchange ((volatile long*) &value, (long) castTo32Bit (newValue), (long) castTo32Bit (valueToCompare))) : castFrom64Bit ((int64) juce_InterlockedCompareExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue), (__int64) castTo64Bit (valueToCompare))); + #elif JUCE_ANDROID + return castFrom32Bit (__atomic_cmpxchg (castTo32Bit (valueToCompare), castTo32Bit (newValue), (volatile int*) &value)); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); @@ -12215,11 +13599,6 @@ public: { } - /** Destructor. */ - ~SparseSet() - { - } - /** Clears the set. */ void clear() { @@ -12269,7 +13648,7 @@ public: index -= len; } - return Type (0); + return Type(); } /** Checks whether a particular value is in the set. */ @@ -18374,6 +19753,15 @@ private: #endif #ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ +#endif +#ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ + +#endif +#ifndef __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ + +#endif +#ifndef __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ + #endif #ifndef __JUCE_IDENTIFIER_JUCEHEADER__ @@ -18679,7 +20067,7 @@ public: private: String originalText; - const juce_wchar* input; + CharPointer_UTF32 input; bool outOfData, errorOccurred; String lastError, dtdText; @@ -40285,7 +41673,7 @@ private: StringArray midiInsFromXml; OwnedArray enabledMidiInputs; Array midiCallbacks; - Array midiCallbackDevices; + StringArray midiCallbackDevices; String defaultMidiOutputName; ScopedPointer defaultMidiOutput; CriticalSection audioCallbackLock, midiCallbackLock; diff --git a/src/audio/devices/juce_AudioDeviceManager.cpp b/src/audio/devices/juce_AudioDeviceManager.cpp index 03e0c79f62..4e48dbc887 100644 --- a/src/audio/devices/juce_AudioDeviceManager.cpp +++ b/src/audio/devices/juce_AudioDeviceManager.cpp @@ -798,7 +798,7 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, if (name.isEmpty()) { midiCallbacks.add (callbackToAdd); - midiCallbackDevices.add (0); + midiCallbackDevices.add (String::empty); } else { @@ -808,7 +808,7 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, { const ScopedLock sl (midiCallbackLock); midiCallbacks.add (callbackToAdd); - midiCallbackDevices.add (enabledMidiInputs[i]); + midiCallbackDevices.add (enabledMidiInputs[i]->getName()); break; } } @@ -821,12 +821,7 @@ void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputC for (int i = midiCallbacks.size(); --i >= 0;) { - String devName; - - if (midiCallbackDevices.getUnchecked(i) != 0) - devName = midiCallbackDevices.getUnchecked(i)->getName(); - - if (devName == name) + if (midiCallbackDevices[i] == name) { midiCallbacks.remove (i); midiCallbackDevices.remove (i); @@ -845,9 +840,9 @@ void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, for (int i = midiCallbackDevices.size(); --i >= 0;) { - MidiInput* const md = midiCallbackDevices.getUnchecked(i); + const String name (midiCallbackDevices[i]); - if (md == source || (md == 0 && isDefaultSource)) + if ((isDefaultSource && name.isEmpty()) || (name.isNotEmpty() && name == source->getName())) midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message); } } diff --git a/src/audio/devices/juce_AudioDeviceManager.h b/src/audio/devices/juce_AudioDeviceManager.h index 6d96294450..32799d476e 100644 --- a/src/audio/devices/juce_AudioDeviceManager.h +++ b/src/audio/devices/juce_AudioDeviceManager.h @@ -464,7 +464,7 @@ private: StringArray midiInsFromXml; OwnedArray enabledMidiInputs; Array midiCallbacks; - Array midiCallbackDevices; + StringArray midiCallbackDevices; String defaultMidiOutputName; ScopedPointer defaultMidiOutput; CriticalSection audioCallbackLock, midiCallbackLock; diff --git a/src/containers/juce_SparseSet.h b/src/containers/juce_SparseSet.h index 1a97f66740..ec8479b69a 100644 --- a/src/containers/juce_SparseSet.h +++ b/src/containers/juce_SparseSet.h @@ -58,11 +58,6 @@ public: { } - /** Destructor. */ - ~SparseSet() - { - } - //============================================================================== /** Clears the set. */ void clear() @@ -113,7 +108,7 @@ public: index -= len; } - return Type (0); + return Type(); } /** Checks whether a particular value is in the set. */ diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 8649956f63..f83c9e82ad 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 17 +#define JUCE_BUILDNUMBER 18 /** Current Juce version number. @@ -130,6 +130,11 @@ #endif #endif +#if JUCE_ANDROID + #include + #include +#endif + //============================================================================== // DLL building settings on Win32 #if JUCE_MSVC diff --git a/src/core/juce_TargetPlatform.h b/src/core/juce_TargetPlatform.h index c81a3697c2..19512ec514 100644 --- a/src/core/juce_TargetPlatform.h +++ b/src/core/juce_TargetPlatform.h @@ -45,7 +45,7 @@ #define JUCE_WINDOWS 1 #elif defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 -#elif defined(__APPLE_CPP__) || defined(__APPLE_CC__) +#elif defined (__APPLE_CPP__) || defined(__APPLE_CC__) #include // (needed to find out what platform we're using) #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR @@ -54,6 +54,9 @@ #else #define JUCE_MAC 1 #endif +#elif defined (JUCE_ANDROID) + #undef JUCE_ANDROID + #define JUCE_ANDROID 1 #else #error "Unknown platform!" #endif @@ -124,17 +127,20 @@ #endif - //============================================================================== -#if JUCE_LINUX +#if JUCE_LINUX || JUCE_ANDROID #ifdef _DEBUG #define JUCE_DEBUG 1 #endif // Allow override for big-endian Linux platforms - #ifndef JUCE_BIG_ENDIAN + #if defined (__LITTLE_ENDIAN__) || ! defined (JUCE_BIG_ENDIAN) #define JUCE_LITTLE_ENDIAN 1 + #undef JUCE_BIG_ENDIAN + #else + #undef JUCE_LITTLE_ENDIAN + #define JUCE_BIG_ENDIAN 1 #endif #if defined (__LP64__) || defined (_LP64) @@ -143,7 +149,9 @@ #define JUCE_32BIT 1 #endif - #define JUCE_INTEL 1 + #if __MMX__ || __SSE__ || __amd64__ + #define JUCE_INTEL 1 + #endif #endif //============================================================================== diff --git a/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp b/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp index 4d51c96f7f..0be7cf6524 100644 --- a/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp +++ b/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp @@ -56,33 +56,33 @@ namespace CppTokeniser bool isReservedKeyword (const juce_wchar* const token, const int tokenLength) throw() { - static const juce_wchar* const keywords2Char[] = - { JUCE_T("if"), JUCE_T("do"), JUCE_T("or"), JUCE_T("id"), 0 }; + static const char* const keywords2Char[] = + { "if", "do", "or", "id", 0 }; - static const juce_wchar* const keywords3Char[] = - { JUCE_T("for"), JUCE_T("int"), JUCE_T("new"), JUCE_T("try"), JUCE_T("xor"), JUCE_T("and"), JUCE_T("asm"), JUCE_T("not"), 0 }; + static const char* const keywords3Char[] = + { "for", "int", "new", "try", "xor", "and", "asm", "not", 0 }; - static const juce_wchar* const keywords4Char[] = - { JUCE_T("bool"), JUCE_T("void"), JUCE_T("this"), JUCE_T("true"), JUCE_T("long"), JUCE_T("else"), JUCE_T("char"), - JUCE_T("enum"), JUCE_T("case"), JUCE_T("goto"), JUCE_T("auto"), 0 }; + static const char* const keywords4Char[] = + { "bool", "void", "this", "true", "long", "else", "char", + "enum", "case", "goto", "auto", 0 }; - static const juce_wchar* const keywords5Char[] = - { JUCE_T("while"), JUCE_T("bitor"), JUCE_T("break"), JUCE_T("catch"), JUCE_T("class"), JUCE_T("compl"), JUCE_T("const"), JUCE_T("false"), - JUCE_T("float"), JUCE_T("short"), JUCE_T("throw"), JUCE_T("union"), JUCE_T("using"), JUCE_T("or_eq"), 0 }; + static const char* const keywords5Char[] = + { "while", "bitor", "break", "catch", "class", "compl", "const", "false", + "float", "short", "throw", "union", "using", "or_eq", 0 }; - static const juce_wchar* const keywords6Char[] = - { JUCE_T("return"), JUCE_T("struct"), JUCE_T("and_eq"), JUCE_T("bitand"), JUCE_T("delete"), JUCE_T("double"), JUCE_T("extern"), - JUCE_T("friend"), JUCE_T("inline"), JUCE_T("not_eq"), JUCE_T("public"), JUCE_T("sizeof"), JUCE_T("static"), JUCE_T("signed"), - JUCE_T("switch"), JUCE_T("typeid"), JUCE_T("wchar_t"), JUCE_T("xor_eq"), 0}; + static const char* const keywords6Char[] = + { "return", "struct", "and_eq", "bitand", "delete", "double", "extern", + "friend", "inline", "not_eq", "public", "sizeof", "static", "signed", + "switch", "typeid", "wchar_t", "xor_eq", 0}; - static const juce_wchar* const keywordsOther[] = - { JUCE_T("const_cast"), JUCE_T("continue"), JUCE_T("default"), JUCE_T("explicit"), JUCE_T("mutable"), JUCE_T("namespace"), - JUCE_T("operator"), JUCE_T("private"), JUCE_T("protected"), JUCE_T("register"), JUCE_T("reinterpret_cast"), JUCE_T("static_cast"), - JUCE_T("template"), JUCE_T("typedef"), JUCE_T("typename"), JUCE_T("unsigned"), JUCE_T("virtual"), JUCE_T("volatile"), - JUCE_T("@implementation"), JUCE_T("@interface"), JUCE_T("@end"), JUCE_T("@synthesize"), JUCE_T("@dynamic"), JUCE_T("@public"), - JUCE_T("@private"), JUCE_T("@property"), JUCE_T("@protected"), JUCE_T("@class"), 0 }; + static const char* const keywordsOther[] = + { "const_cast", "continue", "default", "explicit", "mutable", "namespace", + "operator", "private", "protected", "register", "reinterpret_cast", "static_cast", + "template", "typedef", "typename", "unsigned", "virtual", "volatile", + "@implementation", "@interface", "@end", "@synthesize", "@dynamic", "@public", + "@private", "@property", "@protected", "@class", 0 }; - const juce_wchar* const* k; + const char* const* k; switch (tokenLength) { @@ -103,7 +103,7 @@ namespace CppTokeniser int i = 0; while (k[i] != 0) { - if (k[i][0] == token[0] && CharacterFunctions::compare (k[i], token) == 0) + if (k[i][0] == token[0] && CharPointer_UTF8 (k[i]).compare (CharPointer_UTF32 (token)) == 0) return true; ++i; diff --git a/src/gui/components/controls/juce_ListBox.cpp b/src/gui/components/controls/juce_ListBox.cpp index 836710a462..22d164223a 100644 --- a/src/gui/components/controls/juce_ListBox.cpp +++ b/src/gui/components/controls/juce_ListBox.cpp @@ -434,7 +434,7 @@ void ListBox::updateContent() bool selectionChanged = false; - if (selected [selected.size() - 1] >= totalItems) + if (selected.size() > 0 && selected [selected.size() - 1] >= totalItems) { selected.removeRange (Range (totalItems, std::numeric_limits::max())); lastRowSelected = getSelectedRow (0); diff --git a/src/io/network/juce_Socket.cpp b/src/io/network/juce_Socket.cpp index 89d207abbe..7ebfaaccc6 100644 --- a/src/io/network/juce_Socket.cpp +++ b/src/io/network/juce_Socket.cpp @@ -34,7 +34,7 @@ #endif #else - #if JUCE_LINUX + #if JUCE_LINUX || JUCE_ANDROID #include #include #include @@ -58,7 +58,7 @@ BEGIN_JUCE_NAMESPACE #include "../../threads/juce_ScopedLock.h" #include "../../threads/juce_Thread.h" -#if defined (JUCE_LINUX) || defined (JUCE_MAC) || defined (JUCE_IOS) +#if JUCE_LINUX || JUCE_MAC || JUCE_IOS || JUCE_ANDROID typedef socklen_t juce_socklen_t; #else typedef int juce_socklen_t; diff --git a/src/io/network/juce_URL.cpp b/src/io/network/juce_URL.cpp index 765f9af42b..aa8bd843b5 100644 --- a/src/io/network/juce_URL.cpp +++ b/src/io/network/juce_URL.cpp @@ -121,7 +121,7 @@ namespace URLHelpers int i = 0; while (CharacterFunctions::isLetterOrDigit (url[i]) - || CharacterFunctions::indexOfChar (L"+-.", url[i], false) >= 0) + || url[i] == '+' || url[i] == '-' || url[i] == '.') ++i; return url[i] == ':' ? i + 1 : 0; @@ -179,7 +179,7 @@ namespace URLHelpers // just a short text attachment, so use simple url encoding.. headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " - << (unsigned int) postData.getSize() << "\r\n"; + << (int) postData.getSize() << "\r\n"; } } } @@ -411,8 +411,8 @@ const String URL::removeEscapeChars (const String& s) const String URL::addEscapeChars (const String& s, const bool isParameter) { - const char* const legalChars = isParameter ? "_-.*!'()" - : ",$_-.*!'()"; + const CharPointer_UTF8 legalChars (isParameter ? "_-.*!'()" + : ",$_-.*!'()"); Array utf8 (s.toUTF8(), s.getNumBytesAsUTF8()); @@ -421,7 +421,7 @@ const String URL::addEscapeChars (const String& s, const bool isParameter) const char c = utf8.getUnchecked(i); if (! (CharacterFunctions::isLetterOrDigit (c) - || CharacterFunctions::indexOfChar (legalChars, c, false) >= 0)) + || legalChars.indexOf ((juce_wchar) c) >= 0)) { if (c == ' ') { diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h index d6d58e2d6e..8b058b01a5 100644 --- a/src/juce_core_includes.h +++ b/src/juce_core_includes.h @@ -233,6 +233,15 @@ #ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ #include "text/juce_CharacterFunctions.h" #endif +#ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ + #include "text/juce_CharPointer_UTF16.h" +#endif +#ifndef __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ + #include "text/juce_CharPointer_UTF32.h" +#endif +#ifndef __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ + #include "text/juce_CharPointer_UTF8.h" +#endif #ifndef __JUCE_IDENTIFIER_JUCEHEADER__ #include "text/juce_Identifier.h" #endif diff --git a/src/maths/juce_MathsFunctions.h b/src/maths/juce_MathsFunctions.h index ffc7c01ae1..f599166f5f 100644 --- a/src/maths/juce_MathsFunctions.h +++ b/src/maths/juce_MathsFunctions.h @@ -91,10 +91,6 @@ typedef unsigned int uint32; typedef unsigned int pointer_sized_uint; #endif -/** A platform-independent unicode character type. */ -typedef wchar_t juce_wchar; - - //============================================================================== // Some indispensible min/max functions @@ -351,6 +347,8 @@ inline bool juce_isfinite (FloatingPointType value) { #if JUCE_WINDOWS return _finite (value); + #elif JUCE_ANDROID + return isfinite (value); #else return std::isfinite (value); #endif diff --git a/src/memory/juce_Atomic.h b/src/memory/juce_Atomic.h index da3734301a..cb5b0969f6 100644 --- a/src/memory/juce_Atomic.h +++ b/src/memory/juce_Atomic.h @@ -244,6 +244,8 @@ inline Type Atomic::get() const throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchangeAdd ((volatile long*) &value, (long) 0)) : castFrom64Bit ((int64) juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) 0)); + #elif JUCE_ANDROID + return castFrom32Bit (__atomic_cmpxchg (castTo32Bit (value), castTo32Bit (value), (volatile int*) &value)); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) : castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); @@ -253,7 +255,9 @@ inline Type Atomic::get() const throw() template inline Type Atomic::exchange (const Type newValue) throw() { - #if JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC + #if JUCE_ANDROID + return castFrom32Bit (__atomic_swap (castTo32Bit (newValue), (volatile int*) &value)); + #elif JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC Type currentVal = value; while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } return currentVal; @@ -272,6 +276,14 @@ inline Type Atomic::operator+= (const Type amountToAdd) throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) (juce_InterlockedExchangeAdd ((volatile long*) &value, (long) amountToAdd) + (long) amountToAdd) : (Type) (juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) amountToAdd) + (__int64) amountToAdd); + #elif JUCE_ANDROID + for (;;) + { + const Type oldValue (value); + const Type newValue (oldValue + amountToAdd); + if (compareAndSetBool (newValue, oldValue)) + return newValue; + } #elif JUCE_ATOMICS_GCC return (Type) __sync_add_and_fetch (&value, amountToAdd); #endif @@ -292,6 +304,8 @@ inline Type Atomic::operator++() throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedIncrement ((volatile long*) &value) : (Type) juce_InterlockedIncrement64 ((volatile __int64*) &value); + #elif JUCE_ANDROID + return (Type) __atomic_inc (&value); #elif JUCE_ATOMICS_GCC return (Type) __sync_add_and_fetch (&value, 1); #endif @@ -306,6 +320,8 @@ inline Type Atomic::operator--() throw() #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedDecrement ((volatile long*) &value) : (Type) juce_InterlockedDecrement64 ((volatile __int64*) &value); + #elif JUCE_ANDROID + return (Type) __atomic_dec (&value); #elif JUCE_ATOMICS_GCC return (Type) __sync_add_and_fetch (&value, -1); #endif @@ -317,7 +333,7 @@ inline bool Atomic::compareAndSetBool (const Type newValue, const Type val #if JUCE_ATOMICS_MAC return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); - #elif JUCE_ATOMICS_WINDOWS + #elif JUCE_ATOMICS_WINDOWS || JUCE_ANDROID return compareAndSetValue (newValue, valueToCompare) == valueToCompare; #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) @@ -342,6 +358,8 @@ inline Type Atomic::compareAndSetValue (const Type newValue, const Type va #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedCompareExchange ((volatile long*) &value, (long) castTo32Bit (newValue), (long) castTo32Bit (valueToCompare))) : castFrom64Bit ((int64) juce_InterlockedCompareExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue), (__int64) castTo64Bit (valueToCompare))); + #elif JUCE_ANDROID + return castFrom32Bit (__atomic_cmpxchg (castTo32Bit (valueToCompare), castTo32Bit (newValue), (volatile int*) &value)); #elif JUCE_ATOMICS_GCC return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); diff --git a/src/memory/juce_ByteOrder.h b/src/memory/juce_ByteOrder.h index d5fb9b1822..62bf7e53d2 100644 --- a/src/memory/juce_ByteOrder.h +++ b/src/memory/juce_ByteOrder.h @@ -118,18 +118,22 @@ inline uint32 ByteOrder::swap (uint32 n) { #if JUCE_MAC || JUCE_IOS return OSSwapInt32 (n); - #elif JUCE_GCC + #elif JUCE_GCC && JUCE_INTEL asm("bswap %%eax" : "=a"(n) : "a"(n)); return n; #elif JUCE_USE_INTRINSICS return _byteswap_ulong (n); - #else + #elif JUCE_MSVC __asm { mov eax, n bswap eax mov n, eax } return n; + #elif JUCE_ANDROID + return bswap_32 (n); + #else + return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8); #endif } diff --git a/src/native/common/juce_posix_SharedCode.h b/src/native/common/juce_posix_SharedCode.h index 8ff043f55c..f6fb06cc64 100644 --- a/src/native/common/juce_posix_SharedCode.h +++ b/src/native/common/juce_posix_SharedCode.h @@ -36,7 +36,9 @@ CriticalSection::CriticalSection() throw() pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif pthread_mutex_init (&internal, &atts); } @@ -73,7 +75,9 @@ public: pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); + #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif pthread_mutex_init (&mutex, &atts); } @@ -481,9 +485,14 @@ void FileOutputStream::flushInternal() //============================================================================== const File juce_getExecutableFile() { + #if JUCE_ANDROID + // TODO + return File::nonexistent; + #else Dl_info exeInfo; - dladdr ((const void*) juce_getExecutableFile, &exeInfo); + dladdr ((void*) juce_getExecutableFile, &exeInfo); // (can't be a const void* on android) return File::getCurrentWorkingDirectory().getChildFile (String::fromUTF8 (exeInfo.dli_fname)); + #endif } //============================================================================== @@ -708,7 +717,13 @@ void Thread::closeThreadHandle() void Thread::killThread() { if (threadHandle_ != 0) + { + #if JUCE_ANDROID + jassertfalse; // pthread_cancel not available! + #else pthread_cancel ((pthread_t) threadHandle_); + #endif + } } void Thread::setCurrentThreadName (const String& /*name*/) diff --git a/src/native/linux/juce_linux_Network.cpp b/src/native/linux/juce_linux_Network.cpp index 1ce62dfa17..91c16f8c63 100644 --- a/src/native/linux/juce_linux_Network.cpp +++ b/src/native/linux/juce_linux_Network.cpp @@ -358,7 +358,7 @@ private: header << "\r\nUser-Agent: JUCE/" << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION << "\r\nConnection: Close\r\nContent-Length: " - << postData.getSize() << "\r\n" + << (int) postData.getSize() << "\r\n" << headers << "\r\n"; MemoryBlock mb; diff --git a/src/native/windows/juce_win32_FileChooser.cpp b/src/native/windows/juce_win32_FileChooser.cpp index 1c1e053e61..5720f7cae6 100644 --- a/src/native/windows/juce_win32_FileChooser.cpp +++ b/src/native/windows/juce_win32_FileChooser.cpp @@ -270,7 +270,7 @@ void FileChooser::showPlatformDialog (Array& results, const String& title, while (*filename != 0) { results.add (File (String (files) + "\\" + String (filename))); - filename += CharacterFunctions::length (filename) + 1; + filename += wcslen (filename) + 1; } } else if (files[0] != 0) diff --git a/src/native/windows/juce_win32_Misc.cpp b/src/native/windows/juce_win32_Misc.cpp index 96f00d5b38..d113738859 100644 --- a/src/native/windows/juce_win32_Misc.cpp +++ b/src/native/windows/juce_win32_Misc.cpp @@ -67,11 +67,11 @@ const String SystemClipboard::getTextFromClipboard() if (bufH != 0) { - const wchar_t* const data = (const wchar_t*) GlobalLock (bufH); + const WCHAR* const data = (const WCHAR*) GlobalLock (bufH); if (data != 0) { - result = String (data, (int) (GlobalSize (bufH) / sizeof (wchar_t))); + result = String (data, (int) (GlobalSize (bufH) / sizeof (WCHAR))); GlobalUnlock (bufH); } diff --git a/src/native/windows/juce_win32_QuickTimeMovieComponent.cpp b/src/native/windows/juce_win32_QuickTimeMovieComponent.cpp index b83a3a9d4a..e0cb7f44c5 100644 --- a/src/native/windows/juce_win32_QuickTimeMovieComponent.cpp +++ b/src/native/windows/juce_win32_QuickTimeMovieComponent.cpp @@ -308,7 +308,7 @@ static Handle createHandleDataRef (Handle dataHandle, const char* fileName) { Str255 suffix; - CharacterFunctions::copy ((char*) suffix, fileName, 128); + strncpy ((char*) suffix, fileName, 128); StringPtr name = suffix; err = PtrAndHand (name, dataRef, name[0] + 1); diff --git a/src/text/juce_CharPointer_UTF16.h b/src/text/juce_CharPointer_UTF16.h new file mode 100644 index 0000000000..75181f588d --- /dev/null +++ b/src/text/juce_CharPointer_UTF16.h @@ -0,0 +1,343 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ +#define __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ + + +//============================================================================== +/** + Wraps a pointer to a null-terminated UTF-16 character string, and provides + various methods to operate on the data. + @see CharPointer_UTF8, CharPointer_UTF32 +*/ +class CharPointer_UTF16 +{ +public: + typedef int16 CharType; + + inline CharPointer_UTF16 (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_UTF16 (const CharPointer_UTF16& other) throw() + : data (other.data) + { + } + + inline CharPointer_UTF16& operator= (const CharPointer_UTF16& other) throw() + { + data = other.data; + return *this; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns true if this pointer is pointing to a null character. */ + inline bool isEmpty() const throw() { return *data == 0; } + + /** Returns the unicode character that this pointer is pointing to. */ + juce_wchar operator*() const throw() + { + uint32 n = (uint32) (uint16) *data; + + if (n >= 0xd800 && n <= 0xdfff) + n = 0x10000 + (((n & ~0xd800) << 10) | (data[1] & ~0xdc00)); + + return (juce_wchar) n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF16& operator++() throw() + { + const juce_wchar n = *data++; + + if (n >= 0xd800 && n <= 0xdfff) + ++data; + + return *this; + } + + /** Returns the character that this pointer is currently pointing to, and then + advances the pointer to point to the next character. */ + juce_wchar getAndAdvance() throw() + { + uint32 n = (uint32) (uint16) *data++; + + if (n >= 0xd800 && n <= 0xdfff) + { + n = 0x10000 + (((n & ~0xd800) << 10) | (*data & ~0xdc00)); + ++data; + } + + return (juce_wchar) n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF16 operator++ (int) throw() + { + CharPointer_UTF16 temp (*this); + ++*this; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + void operator+= (int numToSkip) throw() + { + jassert (numToSkip >= 0); + + while (--numToSkip >= 0) + ++*this; + } + + /** Returns the character at a given character index from the start of the string. */ + juce_wchar operator[] (int characterIndex) const throw() + { + CharPointer_UTF16 p (*this); + p += characterIndex; + return *p; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_UTF16 operator+ (int numToSkip) const throw() + { + CharPointer_UTF16 p (*this); + p += numToSkip; + return p; + } + + /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ + void write (const juce_wchar charToWrite) throw() + { + if (charToWrite < 0xd800 || (charToWrite > 0xdfff && charToWrite <= 0xffff)) + { + *data++ = (CharType) charToWrite; + } + else + { + *data++ = (CharType) ((0xd800 - (0x10000 >> 10)) + (charToWrite >> 10)); + *data++ = (CharType) (0xdc00 + (charToWrite & 0x3ff)); + } + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + const CharType* d = data; + size_t count = 0; + + for (;;) + { + const int n = *d++; + + if (n >= 0xd800 && n <= 0xdfff) + ++d; + else if (n == 0) + break; + + ++count; + } + + return count; + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return sizeof (CharType) * (findNullIndex (data) + 1); + } + + /** Returns the number of bytes that would be needed to represent the given + unicode character in this encoding format. + */ + static size_t getBytesRequiredFor (const juce_wchar charToWrite) throw() + { + return (charToWrite < 0xd800 || (charToWrite > 0xdfff && charToWrite <= 0xffff)) + ? 1 : 2; + } + + /** Returns the number of bytes that would be needed to represent the given + string in this encoding format. + The value returned does NOT include the terminating null character. + */ + template + static size_t getBytesRequiredFor (CharPointer text) throw() + { + size_t count = 0; + juce_wchar n; + + while ((n = text.getAndAdvance()) != 0) + count += getBytesRequiredFor (n); + + return count; + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_UTF16 findTerminatingNull() const throw() + { + const CharType* t = data; + + while (*t != 0) + ++t; + + return CharPointer_UTF16 (t); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void copyAndAdvance (const CharPointer& src) throw() + { + CharacterFunctions::copyAndAdvance (*this, src); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void copyAndAdvance (const CharPointer_UTF16& src) throw() + { + const CharType* s = src.data; + + while ((*data = *s) != 0) + { + ++data; + ++s; + } + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int copyAndAdvanceUpToBytes (const CharPointer& src, int maxBytes) throw() + { + return CharacterFunctions::copyAndAdvanceUpToBytes (*this, src, maxBytes); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxChars parameter specifies the maximum number of characters that can be + written to the destination buffer before stopping (including the terminating null). + */ + template + void copyAndAdvanceUpToNumChars (const CharPointer& src, int maxChars) throw() + { + CharacterFunctions::copyAndAdvanceUpToNumChars (*this, src, maxChars); + } + + /** Compares this string with another one. */ + template + int compare (const CharPointer& other) const throw() + { + return CharacterFunctions::compare (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const throw() + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareIgnoreCaseUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); + } + + /** Returns the character index of a substring, or -1 if it isn't found. */ + template + int indexOf (const CharPointer& stringToFind) const throw() + { + return CharacterFunctions::indexOf (*this, stringToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind) const throw() + { + return CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind, const bool ignoreCase) const throw() + { + return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) + : CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns true if the first character of this string is whitespace. */ + bool isWhitespace() const throw() { return CharacterFunctions::isWhitespace (operator*()) != 0; } + /** Returns true if the first character of this string is a digit. */ + bool isDigit() const throw() { return CharacterFunctions::isDigit (operator*()) != 0; } + /** Returns true if the first character of this string is a letter. */ + bool isLetter() const throw() { return CharacterFunctions::isLetter (operator*()) != 0; } + /** Returns true if the first character of this string is a letter or digit. */ + bool isLetterOrDigit() const throw() { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; } + /** Returns true if the first character of this string is upper-case. */ + bool isUpperCase() const throw() { return CharacterFunctions::isUpperCase (operator*()) != 0; } + /** Returns true if the first character of this string is lower-case. */ + bool isLowerCase() const throw() { return CharacterFunctions::isLowerCase (operator*()) != 0; } + + /** Returns an upper-case version of the first character of this string. */ + juce_wchar toUpperCase() const throw() { return CharacterFunctions::toUpperCase (operator*()); } + /** Returns a lower-case version of the first character of this string. */ + juce_wchar toLowerCase() const throw() { return CharacterFunctions::toLowerCase (operator*()); } + + /** Parses this string as a 32-bit integer. */ + int getIntValue32() const throw() { return CharacterFunctions::getIntValue (*this); } + /** Parses this string as a 64-bit integer. */ + int64 getIntValue64() const throw() { return CharacterFunctions::getIntValue (*this); } + + /** Parses this string as a floating point double. */ + double getDoubleValue() const throw() { return CharacterFunctions::getDoubleValue (*this); } + + /** Returns the first non-whitespace character in the string. */ + CharPointer_UTF16 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + +private: + CharType* data; + + static int findNullIndex (const CharType* const t) throw() + { + int n = 0; + + while (t[n] != 0) + ++n; + + return n; + } +}; + + +#endif // __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ diff --git a/src/text/juce_CharPointer_UTF32.h b/src/text/juce_CharPointer_UTF32.h new file mode 100644 index 0000000000..ca043e48b4 --- /dev/null +++ b/src/text/juce_CharPointer_UTF32.h @@ -0,0 +1,340 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ +#define __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ + + +//============================================================================== +/** + Wraps a pointer to a null-terminated UTF-32 character string, and provides + various methods to operate on the data. + @see CharPointer_UTF8, CharPointer_UTF16 +*/ +class CharPointer_UTF32 +{ +public: + typedef juce_wchar CharType; + + inline CharPointer_UTF32 (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_UTF32 (const CharPointer_UTF32& other) throw() + : data (other.data) + { + } + + inline CharPointer_UTF32& operator= (const CharPointer_UTF32& other) throw() + { + data = other.data; + return *this; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns true if this pointer is pointing to a null character. */ + inline bool isEmpty() const throw() { return *data == 0; } + + /** Returns the unicode character that this pointer is pointing to. */ + inline juce_wchar operator*() const throw() { return *data; } + + /** Moves this pointer along to the next character in the string. */ + inline CharPointer_UTF32& operator++() throw() + { + ++data; + return *this; + } + + /** Moves this pointer to the previous character in the string. */ + inline CharPointer_UTF32& operator--() throw() + { + --data; + return *this; + } + + /** Returns the character that this pointer is currently pointing to, and then + advances the pointer to point to the next character. */ + inline juce_wchar getAndAdvance() throw() { return *data++; } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF32 operator++ (int) throw() + { + CharPointer_UTF32 temp (*this); + ++*this; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + inline void operator+= (int numToSkip) throw() + { + data += numToSkip; + } + + inline void operator-= (int numToSkip) throw() + { + data -= numToSkip; + } + + /** Returns the character at a given character index from the start of the string. */ + inline juce_wchar operator[] (int characterIndex) const throw() + { + return data [characterIndex]; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_UTF32 operator+ (int numToSkip) const throw() + { + return CharPointer_UTF32 (data + numToSkip); + } + + /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ + CharPointer_UTF32 operator- (int numToSkip) const throw() + { + return CharPointer_UTF32 (data - numToSkip); + } + + /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ + inline void write (const juce_wchar charToWrite) throw() + { + *data++ = charToWrite; + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + #if JUCE_ANDROID + size_t n = 0; + while (data[n] == 0) + ++n; + return n; + #else + return wcslen (data); + #endif + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return sizeof (CharType) * (length() + 1); + } + + /** Returns the number of bytes that would be needed to represent the given + unicode character in this encoding format. + */ + static inline size_t getBytesRequiredFor (const juce_wchar) throw() + { + return sizeof (CharType); + } + + /** Returns the number of bytes that would be needed to represent the given + string in this encoding format. + The value returned does NOT include the terminating null character. + */ + template + static size_t getBytesRequiredFor (const CharPointer& text) throw() + { + return sizeof (CharType) * text.length(); + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_UTF32 findTerminatingNull() const throw() + { + return CharPointer_UTF32 (data + length()); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void copyAndAdvance (const CharPointer& src) throw() + { + CharacterFunctions::copyAndAdvance (*this, src); + } + + #if ! JUCE_ANDROID + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void copyAndAdvance (const CharPointer_UTF32& src) throw() + { + data = wcscpy (data, src.data); + } + #endif + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int copyAndAdvanceUpToBytes (const CharPointer& src, int maxBytes) throw() + { + return CharacterFunctions::copyAndAdvanceUpToBytes (*this, src, maxBytes); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxChars parameter specifies the maximum number of characters that can be + written to the destination buffer before stopping (including the terminating null). + */ + template + void copyAndAdvanceUpToNumChars (const CharPointer& src, int maxChars) throw() + { + CharacterFunctions::copyAndAdvanceUpToNumChars (*this, src, maxChars); + } + + /** Compares this string with another one. */ + template + int compare (const CharPointer& other) const throw() + { + return CharacterFunctions::compare (*this, other); + } + + #if ! JUCE_ANDROID + /** Compares this string with another one. */ + int compare (const CharPointer_UTF32& other) const throw() + { + return wcscmp (data, other.data); + } + #endif + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareIgnoreCaseUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); + } + + /** Returns the character index of a substring, or -1 if it isn't found. */ + template + int indexOf (const CharPointer& stringToFind) const throw() + { + return CharacterFunctions::indexOf (*this, stringToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind) const throw() + { + int i = 0; + + while (data[i] != 0) + { + if (data[i] == charToFind) + return i; + + ++i; + } + + return -1; + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind, const bool ignoreCase) const throw() + { + return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) + : CharacterFunctions::indexOfChar (*this, charToFind); + } + + #if JUCE_WINDOWS && ! DOXYGEN + int compareIgnoreCase (const CharPointer_UTF32& other) const throw() + { + return _wcsicmp (data, other.data); + } + + int compareIgnoreCaseUpTo (const CharPointer_UTF32& other, int maxChars) const throw() + { + return _wcsnicmp (data, other.data, maxChars); + } + + int indexOf (const CharPointer_UTF32& stringToFind) const throw() + { + const CharType* const t = wcsstr (data, stringToFind.getAddress()); + return t == 0 ? -1 : (t - data); + } + #endif + + /** Returns true if the first character of this string is whitespace. */ + bool isWhitespace() const { return CharacterFunctions::isWhitespace (*data) != 0; } + /** Returns true if the first character of this string is a digit. */ + bool isDigit() const { return CharacterFunctions::isDigit (*data) != 0; } + /** Returns true if the first character of this string is a letter. */ + bool isLetter() const { return CharacterFunctions::isLetter (*data) != 0; } + /** Returns true if the first character of this string is a letter or digit. */ + bool isLetterOrDigit() const { return CharacterFunctions::isLetterOrDigit (*data) != 0; } + /** Returns true if the first character of this string is upper-case. */ + bool isUpperCase() const { return CharacterFunctions::isUpperCase (*data) != 0; } + /** Returns true if the first character of this string is lower-case. */ + bool isLowerCase() const { return CharacterFunctions::isLowerCase (*data) != 0; } + + /** Returns an upper-case version of the first character of this string. */ + juce_wchar toUpperCase() const throw() { return CharacterFunctions::toUpperCase (*data); } + /** Returns a lower-case version of the first character of this string. */ + juce_wchar toLowerCase() const throw() { return CharacterFunctions::toLowerCase (*data); } + + /** Parses this string as a 32-bit integer. */ + int getIntValue32() const throw() + { + #if JUCE_WINDOWS + return _wtoi (data); + #else + return CharacterFunctions::getIntValue (*this); + #endif + } + + /** Parses this string as a 64-bit integer. */ + int64 getIntValue64() const throw() + { + #if JUCE_WINDOWS + return _wtoi64 (data); + #else + return CharacterFunctions::getIntValue (*this); + #endif + } + + /** Parses this string as a floating point double. */ + double getDoubleValue() const throw() { return CharacterFunctions::getDoubleValue (*this); } + + /** Returns the first non-whitespace character in the string. */ + CharPointer_UTF32 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + +private: + CharType* data; +}; + + +#endif // __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ diff --git a/src/text/juce_CharPointer_UTF8.h b/src/text/juce_CharPointer_UTF8.h new file mode 100644 index 0000000000..e0d6386f6d --- /dev/null +++ b/src/text/juce_CharPointer_UTF8.h @@ -0,0 +1,441 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ +#define __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ + +//============================================================================== +/** + Wraps a pointer to a null-terminated UTF-8 character string, and provides + various methods to operate on the data. + @see CharPointer_UTF16, CharPointer_UTF32 +*/ +class CharPointer_UTF8 +{ +public: + typedef char CharType; + + inline CharPointer_UTF8 (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_UTF8 (const CharPointer_UTF8& other) throw() + : data (other.data) + { + } + + inline CharPointer_UTF8& operator= (const CharPointer_UTF8& other) throw() + { + data = other.data; + return *this; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns true if this pointer is pointing to a null character. */ + inline bool isEmpty() const throw() { return *data == 0; } + + /** Returns the unicode character that this pointer is pointing to. */ + juce_wchar operator*() const throw() + { + const char byte = *data; + + if (byte >= 0) + return byte; + + juce_wchar n = byte; + juce_wchar mask = 0x7f; + juce_wchar bit = 0x40; + size_t numExtraValues = 0; + + while ((n & bit) != 0 && bit > 0x10) + { + mask >>= 1; + ++numExtraValues; + bit >>= 1; + } + + n &= mask; + + for (size_t i = 1; i <= numExtraValues; ++i) + { + const juce_wchar nextByte = data [i]; + + if ((nextByte & 0xc0) != 0x80) + break; + + n <<= 6; + n |= (nextByte & 0x3f); + } + + return n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF8& operator++() throw() + { + const char n = *data++; + + if (n < 0) + { + juce_wchar bit = 0x40; + + while ((n & bit) != 0 && bit > 0x10) + { + ++data; + bit >>= 1; + } + } + + return *this; + } + + /** Returns the character that this pointer is currently pointing to, and then + advances the pointer to point to the next character. */ + juce_wchar getAndAdvance() throw() + { + const char byte = *data++; + + if (byte >= 0) + return byte; + + uint32 n = (uint32) (uint8) byte; + uint32 mask = 0x7f; + uint32 bit = 0x40; + int numExtraValues = 0; + + while ((n & bit) != 0 && bit > 0x10) + { + mask >>= 1; + ++numExtraValues; + bit >>= 1; + } + + n &= mask; + + while (--numExtraValues >= 0) + { + const uint32 nextByte = (uint32) (uint8) *data++; + + if ((nextByte & 0xc0) != 0x80) + break; + + n <<= 6; + n |= (nextByte & 0x3f); + } + + return (juce_wchar) n; + } + + /** Moves this pointer along to the next character in the string. */ + CharPointer_UTF8 operator++ (int) throw() + { + CharPointer_UTF8 temp (*this); + ++*this; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + void operator+= (int numToSkip) throw() + { + jassert (numToSkip >= 0); + + while (--numToSkip >= 0) + ++*this; + } + + /** Returns the character at a given character index from the start of the string. */ + juce_wchar operator[] (int characterIndex) const throw() + { + CharPointer_UTF8 p (*this); + p += characterIndex; + return *p; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_UTF8 operator+ (int numToSkip) const throw() + { + CharPointer_UTF8 p (*this); + p += numToSkip; + return p; + } + + /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ + void write (const juce_wchar charToWrite) throw() + { + const uint32 c = (uint32) charToWrite; + + if (c >= 0x80) + { + int numExtraBytes = 1; + if (c >= 0x800) + { + ++numExtraBytes; + if (c >= 0x10000) + ++numExtraBytes; + } + + *data++ = (CharType) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6))); + + while (--numExtraBytes >= 0) + *data++ = (CharType) (0x80 | (0x3f & (c >> (numExtraBytes * 6)))); + } + else + { + *data++ = (CharType) c; + } + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + const CharType* d = data; + size_t count = 0; + + for (;;) + { + const int n = *d++; + + if ((n & 0x80) != 0) + { + int bit = 0x40; + + while ((n & bit) != 0) + { + ++d; + bit >>= 1; + + if (bit == 0) + break; // illegal utf-8 sequence + } + } + else if (n == 0) + break; + + ++count; + } + + return count; + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return strlen (data) + 1; + } + + /** Returns the number of bytes that would be needed to represent the given + unicode character in this encoding format. + */ + static size_t getBytesRequiredFor (const juce_wchar charToWrite) throw() + { + size_t num = 1; + const uint32 c = (uint32) charToWrite; + + if (c >= 0x80) + { + ++num; + if (c >= 0x800) + { + ++num; + if (c >= 0x10000) + ++num; + } + } + + return num; + } + + /** Returns the number of bytes that would be needed to represent the given + string in this encoding format. + The value returned does NOT include the terminating null character. + */ + template + static size_t getBytesRequiredFor (CharPointer text) throw() + { + size_t count = 0; + juce_wchar n; + + while ((n = text.getAndAdvance()) != 0) + count += getBytesRequiredFor (n); + + return count; + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_UTF8 findTerminatingNull() const throw() + { + return CharPointer_UTF8 (data + strlen (data)); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void copyAndAdvance (const CharPointer& src) throw() + { + CharacterFunctions::copyAndAdvance (*this, src); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void copyAndAdvance (const CharPointer_UTF8& src) throw() + { + data = (CharType*) strcpy ((char*) data, src.data); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int copyAndAdvanceUpToBytes (const CharPointer& src, int maxBytes) throw() + { + return CharacterFunctions::copyAndAdvanceUpToBytes (*this, src, maxBytes); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxChars parameter specifies the maximum number of characters that can be + written to the destination buffer before stopping (including the terminating null). + */ + template + void copyAndAdvanceUpToNumChars (const CharPointer& src, int maxChars) throw() + { + CharacterFunctions::copyAndAdvanceUpToNumChars (*this, src, maxChars); + } + + /** Compares this string with another one. */ + template + int compare (const CharPointer& other) const throw() + { + return CharacterFunctions::compare (*this, other); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const throw() + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + /** Compares this string with another one. */ + int compareIgnoreCase (const CharPointer_UTF8& other) const throw() + { + #if JUCE_WINDOWS + return stricmp (data, other.data); + #else + return strcasecmp (data, other.data); + #endif + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareIgnoreCaseUpTo (const CharPointer& other, int maxChars) const throw() + { + return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); + } + + /** Compares this string with another one, up to a specified number of characters. */ + int compareIgnoreCaseUpTo (const CharPointer_UTF8& other, int maxChars) const throw() + { + #if JUCE_WINDOWS + return strnicmp (data, other.data, maxChars); + #else + return strncasecmp (data, other.data, maxChars); + #endif + } + + /** Returns the character index of a substring, or -1 if it isn't found. */ + template + int indexOf (const CharPointer& stringToFind) const throw() + { + return CharacterFunctions::indexOf (*this, stringToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind) const throw() + { + return CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns the character index of a unicode character, or -1 if it isn't found. */ + int indexOf (const juce_wchar charToFind, const bool ignoreCase) const throw() + { + return ignoreCase ? CharacterFunctions::indexOfCharIgnoreCase (*this, charToFind) + : CharacterFunctions::indexOfChar (*this, charToFind); + } + + /** Returns true if the first character of this string is whitespace. */ + bool isWhitespace() const throw() { return *data == ' ' || (*data <= 13 && *data >= 9); } + /** Returns true if the first character of this string is a digit. */ + bool isDigit() const throw() { return *data >= '0' && *data <= '9'; } + /** Returns true if the first character of this string is a letter. */ + bool isLetter() const throw() { return CharacterFunctions::isLetter (operator*()) != 0; } + /** Returns true if the first character of this string is a letter or digit. */ + bool isLetterOrDigit() const throw() { return CharacterFunctions::isLetterOrDigit (operator*()) != 0; } + /** Returns true if the first character of this string is upper-case. */ + bool isUpperCase() const throw() { return CharacterFunctions::isUpperCase (operator*()) != 0; } + /** Returns true if the first character of this string is lower-case. */ + bool isLowerCase() const throw() { return CharacterFunctions::isLowerCase (operator*()) != 0; } + + /** Returns an upper-case version of the first character of this string. */ + juce_wchar toUpperCase() const throw() { return CharacterFunctions::toUpperCase (operator*()); } + /** Returns a lower-case version of the first character of this string. */ + juce_wchar toLowerCase() const throw() { return CharacterFunctions::toLowerCase (operator*()); } + + /** Parses this string as a 32-bit integer. */ + int getIntValue32() const throw() { return atoi (data); } + + /** Parses this string as a 64-bit integer. */ + int64 getIntValue64() const throw() + { + #if JUCE_LINUX || JUCE_ANDROID + return atoll (data); + #elif JUCE_WINDOWS + return _atoi64 (data); + #else + return CharacterFunctions::getIntValue (*this); + #endif + } + + /** Parses this string as a floating point double. */ + double getDoubleValue() const throw() { return CharacterFunctions::getDoubleValue (*this); } + + /** Returns the first non-whitespace character in the string. */ + CharPointer_UTF8 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + +private: + CharType* data; +}; + +#endif // __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ diff --git a/src/text/juce_CharacterFunctions.cpp b/src/text/juce_CharacterFunctions.cpp index fcc56840ed..cc582e211b 100644 --- a/src/text/juce_CharacterFunctions.cpp +++ b/src/text/juce_CharacterFunctions.cpp @@ -30,712 +30,44 @@ #pragma warning (disable: 4514 4996) #endif -#include +#if ! JUCE_ANDROID + #include +#endif + #include #include BEGIN_JUCE_NAMESPACE -#include "juce_CharacterFunctions.h" #include "juce_String.h" //============================================================================== -int CharacterFunctions::length (const char* const s) throw() -{ - return (int) strlen (s); -} - -int CharacterFunctions::length (const juce_wchar* const s) throw() -{ - return (int) wcslen (s); -} - -void CharacterFunctions::copy (char* dest, const char* src, const int maxChars) throw() -{ - strncpy (dest, src, maxChars); -} - -void CharacterFunctions::copy (juce_wchar* dest, const juce_wchar* src, int maxChars) throw() -{ - wcsncpy (dest, src, maxChars); -} - -void CharacterFunctions::copy (juce_wchar* dest, const char* src, const int maxChars) throw() -{ - mbstowcs (dest, src, maxChars); -} - -void CharacterFunctions::copy (char* dest, const juce_wchar* src, const int maxChars) throw() -{ - wcstombs (dest, src, maxChars); -} - -int CharacterFunctions::bytesRequiredForCopy (const juce_wchar* src) throw() -{ - return (int) wcstombs (0, src, 0); -} - -void CharacterFunctions::append (char* dest, const char* src) throw() -{ - strcat (dest, src); -} - -void CharacterFunctions::append (juce_wchar* dest, const juce_wchar* src) throw() -{ - wcscat (dest, src); -} - -int CharacterFunctions::compare (const char* const s1, const char* const s2) throw() -{ - return strcmp (s1, s2); -} - -int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - return wcscmp (s1, s2); -} - -int CharacterFunctions::compare (const char* const s1, const char* const s2, const int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - return strncmp (s1, s2, maxChars); -} - -int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - return wcsncmp (s1, s2, maxChars); -} - -int CharacterFunctions::compare (const juce_wchar* s1, const char* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - - for (;;) - { - const int diff = (int) (*s1 - (juce_wchar) (unsigned char) *s2); - - if (diff != 0) - return diff; - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -} - -int CharacterFunctions::compare (const char* s1, const juce_wchar* s2) throw() -{ - return -compare (s2, s1); -} - -int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return stricmp (s1, s2); -#else - return strcasecmp (s1, s2); -#endif -} - -int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return _wcsicmp (s1, s2); -#else - for (;;) - { - if (*s1 != *s2) - { - const int diff = toUpperCase (*s1) - toUpperCase (*s2); - - if (diff != 0) - return diff < 0 ? -1 : 1; - } - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -#endif -} - -int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const char* s2) throw() -{ - jassert (s1 != 0 && s2 != 0); - - for (;;) - { - if (*s1 != *s2) - { - const int diff = toUpperCase (*s1) - toUpperCase (*s2); - - if (diff != 0) - return diff < 0 ? -1 : 1; - } - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -} - -int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2, const int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return strnicmp (s1, s2, maxChars); -#else - return strncasecmp (s1, s2, maxChars); -#endif -} - -int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw() -{ - jassert (s1 != 0 && s2 != 0); - -#if JUCE_WINDOWS - return _wcsnicmp (s1, s2, maxChars); -#else - while (--maxChars >= 0) - { - if (*s1 != *s2) - { - const int diff = toUpperCase (*s1) - toUpperCase (*s2); - - if (diff != 0) - return diff < 0 ? -1 : 1; - } - else if (*s1 == 0) - break; - - ++s1; - ++s2; - } - - return 0; -#endif -} - -const char* CharacterFunctions::find (const char* const haystack, const char* const needle) throw() -{ - return strstr (haystack, needle); -} - -const juce_wchar* CharacterFunctions::find (const juce_wchar* haystack, const juce_wchar* const needle) throw() -{ - return wcsstr (haystack, needle); -} - -int CharacterFunctions::indexOfChar (const char* const haystack, const char needle, const bool ignoreCase) throw() -{ - if (haystack != 0) - { - int i = 0; - - if (ignoreCase) - { - const char n1 = toLowerCase (needle); - const char n2 = toUpperCase (needle); - - if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search - { - while (haystack[i] != 0) - { - if (haystack[i] == n1 || haystack[i] == n2) - return i; - - ++i; - } - - return -1; - } - - jassert (n1 == needle); - } - - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - } - - return -1; -} - -int CharacterFunctions::indexOfChar (const juce_wchar* const haystack, const juce_wchar needle, const bool ignoreCase) throw() -{ - if (haystack != 0) - { - int i = 0; - - if (ignoreCase) - { - const juce_wchar n1 = toLowerCase (needle); - const juce_wchar n2 = toUpperCase (needle); - - if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search - { - while (haystack[i] != 0) - { - if (haystack[i] == n1 || haystack[i] == n2) - return i; - - ++i; - } - - return -1; - } - - jassert (n1 == needle); - } - - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - } - - return -1; -} - -int CharacterFunctions::indexOfCharFast (const char* const haystack, const char needle) throw() -{ - jassert (haystack != 0); - - int i = 0; - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - - return -1; -} - -int CharacterFunctions::indexOfCharFast (const juce_wchar* const haystack, const juce_wchar needle) throw() -{ - jassert (haystack != 0); - - int i = 0; - while (haystack[i] != 0) - { - if (haystack[i] == needle) - return i; - - ++i; - } - - return -1; -} - -int CharacterFunctions::getIntialSectionContainingOnly (const char* const text, const char* const allowedChars) throw() -{ - return allowedChars == 0 ? 0 : (int) strspn (text, allowedChars); -} - -int CharacterFunctions::getIntialSectionContainingOnly (const juce_wchar* const text, const juce_wchar* const allowedChars) throw() -{ - if (allowedChars == 0) - return 0; - - int i = 0; - - for (;;) - { - if (indexOfCharFast (allowedChars, text[i]) < 0) - break; - - ++i; - } - - return i; -} - -int CharacterFunctions::ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw() -{ - return (int) strftime (dest, maxChars, format, tm); -} - -int CharacterFunctions::ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw() -{ - return (int) wcsftime (dest, maxChars, format, tm); -} - -int CharacterFunctions::getIntValue (const char* const s) throw() -{ - return atoi (s); -} - -int CharacterFunctions::getIntValue (const juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - return _wtoi (s); -#else - int v = 0; - - while (isWhitespace (*s)) - ++s; - - const bool isNeg = *s == '-'; - if (isNeg) - ++s; - - for (;;) - { - const wchar_t c = *s++; - - if (c >= '0' && c <= '9') - v = v * 10 + (int) (c - '0'); - else - break; - } - - return isNeg ? -v : v; -#endif -} - -int64 CharacterFunctions::getInt64Value (const char* s) throw() -{ -#if JUCE_LINUX - return atoll (s); -#elif JUCE_WINDOWS - return _atoi64 (s); -#else - int64 v = 0; - - while (isWhitespace (*s)) - ++s; - - const bool isNeg = *s == '-'; - if (isNeg) - ++s; - - for (;;) - { - const char c = *s++; - - if (c >= '0' && c <= '9') - v = v * 10 + (int64) (c - '0'); - else - break; - } - - return isNeg ? -v : v; -#endif -} - -int64 CharacterFunctions::getInt64Value (const juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - return _wtoi64 (s); -#else - int64 v = 0; - - while (isWhitespace (*s)) - ++s; - - const bool isNeg = *s == '-'; - if (isNeg) - ++s; - - for (;;) - { - const juce_wchar c = *s++; - - if (c >= '0' && c <= '9') - v = v * 10 + (int64) (c - '0'); - else - break; - } - - return isNeg ? -v : v; -#endif -} - -//============================================================================== -namespace -{ - double juce_mulexp10 (const double value, int exponent) throw() - { - if (exponent == 0) - return value; - - if (value == 0) - return 0; - - const bool negative = (exponent < 0); - if (negative) - exponent = -exponent; - - double result = 1.0, power = 10.0; - for (int bit = 1; exponent != 0; bit <<= 1) - { - if ((exponent & bit) != 0) - { - exponent ^= bit; - result *= power; - if (exponent == 0) - break; - } - power *= power; - } - - return negative ? (value / result) : (value * result); - } - - template - double juce_atof (const CharType* const original) throw() - { - double result[3] = { 0, 0, 0 }, accumulator[2] = { 0, 0 }; - int exponentAdjustment[2] = { 0, 0 }, exponentAccumulator[2] = { -1, -1 }; - int exponent = 0, decPointIndex = 0, digit = 0; - int lastDigit = 0, numSignificantDigits = 0; - bool isNegative = false, digitsFound = false; - const int maxSignificantDigits = 15 + 2; - - const CharType* s = original; - while (CharacterFunctions::isWhitespace (*s)) - ++s; - - switch (*s) - { - case '-': isNegative = true; // fall-through.. - case '+': ++s; - } - - if (*s == 'n' || *s == 'N' || *s == 'i' || *s == 'I') - return atof (String (original).toUTF8()); // Let the c library deal with NAN and INF - - for (;;) - { - if (CharacterFunctions::isDigit (*s)) - { - lastDigit = digit; - digit = *s++ - '0'; - digitsFound = true; - - if (decPointIndex != 0) - exponentAdjustment[1]++; - - if (numSignificantDigits == 0 && digit == 0) - continue; - - if (++numSignificantDigits > maxSignificantDigits) - { - if (digit > 5) - ++accumulator [decPointIndex]; - else if (digit == 5 && (lastDigit & 1) != 0) - ++accumulator [decPointIndex]; - - if (decPointIndex > 0) - exponentAdjustment[1]--; - else - exponentAdjustment[0]++; - - while (CharacterFunctions::isDigit (*s)) - { - ++s; - if (decPointIndex == 0) - exponentAdjustment[0]++; - } - } - else - { - const double maxAccumulatorValue = (double) ((std::numeric_limits::max() - 9) / 10); - if (accumulator [decPointIndex] > maxAccumulatorValue) - { - result [decPointIndex] = juce_mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex]) - + accumulator [decPointIndex]; - accumulator [decPointIndex] = 0; - exponentAccumulator [decPointIndex] = 0; - } - - accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit; - exponentAccumulator [decPointIndex]++; - } - } - else if (decPointIndex == 0 && *s == '.') - { - ++s; - decPointIndex = 1; - - if (numSignificantDigits > maxSignificantDigits) - { - while (CharacterFunctions::isDigit (*s)) - ++s; - break; - } - } - else - { - break; - } - } - - result[0] = juce_mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0]; - - if (decPointIndex != 0) - result[1] = juce_mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1]; - - if ((*s == 'e' || *s == 'E') && digitsFound) - { - bool negativeExponent = false; - - switch (*++s) - { - case '-': negativeExponent = true; // fall-through.. - case '+': ++s; - } - - while (CharacterFunctions::isDigit (*s)) - exponent = (exponent * 10) + (*s++ - '0'); - - if (negativeExponent) - exponent = -exponent; - } - - double r = juce_mulexp10 (result[0], exponent + exponentAdjustment[0]); - if (decPointIndex != 0) - r += juce_mulexp10 (result[1], exponent - exponentAdjustment[1]); - - return isNegative ? -r : r; - } -} - -double CharacterFunctions::getDoubleValue (const char* const s) throw() -{ - return juce_atof (s); -} - -double CharacterFunctions::getDoubleValue (const juce_wchar* const s) throw() -{ - return juce_atof (s); -} - -//============================================================================== -char CharacterFunctions::toUpperCase (const char character) throw() -{ - return (char) toupper (character); -} - juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) throw() { return towupper (character); } -void CharacterFunctions::toUpperCase (char* s) throw() -{ -#if JUCE_WINDOWS - strupr (s); -#else - while (*s != 0) - { - *s = toUpperCase (*s); - ++s; - } -#endif -} - -void CharacterFunctions::toUpperCase (juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - _wcsupr (s); -#else - while (*s != 0) - { - *s = toUpperCase (*s); - ++s; - } -#endif -} - -bool CharacterFunctions::isUpperCase (const char character) throw() +juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) throw() { - return isupper (character) != 0; + return towlower (character); } bool CharacterFunctions::isUpperCase (const juce_wchar character) throw() { -#if JUCE_WINDOWS + #if JUCE_WINDOWS return iswupper (character) != 0; -#else + #else return toLowerCase (character) != character; -#endif -} - -//============================================================================== -char CharacterFunctions::toLowerCase (const char character) throw() -{ - return (char) tolower (character); -} - -juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) throw() -{ - return towlower (character); -} - -void CharacterFunctions::toLowerCase (char* s) throw() -{ -#if JUCE_WINDOWS - strlwr (s); -#else - while (*s != 0) - { - *s = toLowerCase (*s); - ++s; - } -#endif -} - -void CharacterFunctions::toLowerCase (juce_wchar* s) throw() -{ -#if JUCE_WINDOWS - _wcslwr (s); -#else - while (*s != 0) - { - *s = toLowerCase (*s); - ++s; - } -#endif -} - -bool CharacterFunctions::isLowerCase (const char character) throw() -{ - return islower (character) != 0; + #endif } bool CharacterFunctions::isLowerCase (const juce_wchar character) throw() { -#if JUCE_WINDOWS + #if JUCE_WINDOWS return iswlower (character) != 0; -#else + #else return toUpperCase (character) != character; -#endif + #endif } //============================================================================== @@ -799,8 +131,55 @@ int CharacterFunctions::getHexDigitValue (const juce_wchar digit) throw() return -1; } +int CharacterFunctions::ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw() +{ + return (int) strftime (dest, maxChars, format, tm); +} + +int CharacterFunctions::ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw() +{ + #if JUCE_ANDROID + HeapBlock tempDest; + tempDest.calloc (maxChars + 2); + int result = ftime (tempDest.getData(), maxChars, String (format).toUTF8(), tm); + CharPointer_UTF32 (dest).copyAndAdvance (CharPointer_UTF8 (tempDest.getData())); + return result; + #else + return (int) wcsftime (dest, maxChars, format, tm); + #endif +} + #if JUCE_MSVC #pragma warning (pop) #endif +double CharacterFunctions::mulexp10 (const double value, int exponent) throw() +{ + if (exponent == 0) + return value; + + if (value == 0) + return 0; + + const bool negative = (exponent < 0); + if (negative) + exponent = -exponent; + + double result = 1.0, power = 10.0; + for (int bit = 1; exponent != 0; bit <<= 1) + { + if ((exponent & bit) != 0) + { + exponent ^= bit; + result *= power; + if (exponent == 0) + break; + } + power *= power; + } + + return negative ? (value / result) : (value * result); +} + + END_JUCE_NAMESPACE diff --git a/src/text/juce_CharacterFunctions.h b/src/text/juce_CharacterFunctions.h index 70da944ac5..4b91495199 100644 --- a/src/text/juce_CharacterFunctions.h +++ b/src/text/juce_CharacterFunctions.h @@ -28,30 +28,35 @@ //============================================================================== -#define JUCE_T(stringLiteral) (L##stringLiteral) -typedef juce_wchar tchar; - +#if JUCE_ANDROID && ! DOXYGEN + typedef uint32 juce_wchar; + #define JUCE_T(stringLiteral) CharPointer_UTF8 (stringLiteral) +#else + /** A platform-independent unicode character type. */ + typedef wchar_t juce_wchar; + #define JUCE_T(stringLiteral) (L##stringLiteral) +#endif #if ! JUCE_DONT_DEFINE_MACROS + /** The 'T' macro allows a literal string to be compiled as unicode. -/** The 'T' macro allows a literal string to be compiled as unicode. - - If you write your string literals in the form T("xyz"), it will be compiled as L"xyz" - or "xyz", depending on which representation is best for the String class to work with. - - Because the 'T' symbol is occasionally used inside 3rd-party library headers which you - may need to include after juce.h, you can use the juce_withoutMacros.h file (in - the juce/src directory) to avoid defining this macro. See the comments in - juce_withoutMacros.h for more info. -*/ -#define T(stringLiteral) JUCE_T(stringLiteral) + If you write your string literals in the form T("xyz"), it will be compiled as L"xyz" + or "xyz", depending on which representation is best for the String class to work with. + Because the 'T' symbol is occasionally used inside 3rd-party library headers which you + may need to include after juce.h, you can use the juce_withoutMacros.h file (in + the juce/src directory) to avoid defining this macro. See the comments in + juce_withoutMacros.h for more info. + */ + #define T(stringLiteral) JUCE_T(stringLiteral) #endif +#undef max +#undef min + //============================================================================== /** - A set of methods for manipulating characters and character strings, with - duplicate methods to handle 8-bit and unicode characters. + A set of methods for manipulating characters and character strings. These are defined as wrappers around the basic C string handlers, to provide a clean, cross-platform layer, (because various platforms differ in the @@ -62,75 +67,13 @@ typedef juce_wchar tchar; class JUCE_API CharacterFunctions { public: - static int length (const char* s) throw(); - static int length (const juce_wchar* s) throw(); - - static void copy (char* dest, const char* src, int maxBytes) throw(); - static void copy (juce_wchar* dest, const juce_wchar* src, int maxChars) throw(); - - static void copy (juce_wchar* dest, const char* src, int maxChars) throw(); - static void copy (char* dest, const juce_wchar* src, int maxBytes) throw(); - static int bytesRequiredForCopy (const juce_wchar* src) throw(); - - static void append (char* dest, const char* src) throw(); - static void append (juce_wchar* dest, const juce_wchar* src) throw(); - - static int compare (const char* s1, const char* s2) throw(); - static int compare (const juce_wchar* s1, const juce_wchar* s2) throw(); - static int compare (const juce_wchar* s1, const char* s2) throw(); - static int compare (const char* s1, const juce_wchar* s2) throw(); - - static int compare (const char* s1, const char* s2, int maxChars) throw(); - static int compare (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw(); - - static int compareIgnoreCase (const char* s1, const char* s2) throw(); - static int compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2) throw(); - static int compareIgnoreCase (const juce_wchar* s1, const char* s2) throw(); - - static int compareIgnoreCase (const char* s1, const char* s2, int maxChars) throw(); - static int compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw(); - - static const char* find (const char* haystack, const char* needle) throw(); - static const juce_wchar* find (const juce_wchar* haystack, const juce_wchar* needle) throw(); - - static int indexOfChar (const char* haystack, char needle, bool ignoreCase) throw(); - static int indexOfChar (const juce_wchar* haystack, juce_wchar needle, bool ignoreCase) throw(); - - static int indexOfCharFast (const char* haystack, char needle) throw(); - static int indexOfCharFast (const juce_wchar* haystack, juce_wchar needle) throw(); - - static int getIntialSectionContainingOnly (const char* text, const char* allowedChars) throw(); - static int getIntialSectionContainingOnly (const juce_wchar* text, const juce_wchar* allowedChars) throw(); - - static int ftime (char* dest, int maxChars, const char* format, const struct tm* tm) throw(); - static int ftime (juce_wchar* dest, int maxChars, const juce_wchar* format, const struct tm* tm) throw(); - - static int getIntValue (const char* s) throw(); - static int getIntValue (const juce_wchar* s) throw(); - - static int64 getInt64Value (const char* s) throw(); - static int64 getInt64Value (const juce_wchar* s) throw(); - - static double getDoubleValue (const char* s) throw(); - static double getDoubleValue (const juce_wchar* s) throw(); - //============================================================================== - static char toUpperCase (char character) throw(); static juce_wchar toUpperCase (juce_wchar character) throw(); - static void toUpperCase (char* s) throw(); + static juce_wchar toLowerCase (juce_wchar character) throw(); - static void toUpperCase (juce_wchar* s) throw(); - static bool isUpperCase (char character) throw(); static bool isUpperCase (juce_wchar character) throw(); - - static char toLowerCase (char character) throw(); - static juce_wchar toLowerCase (juce_wchar character) throw(); - static void toLowerCase (char* s) throw(); - static void toLowerCase (juce_wchar* s) throw(); - static bool isLowerCase (char character) throw(); static bool isLowerCase (juce_wchar character) throw(); - //============================================================================== static bool isWhitespace (char character) throw(); static bool isWhitespace (juce_wchar character) throw(); @@ -143,10 +86,359 @@ public: static bool isLetterOrDigit (char character) throw(); static bool isLetterOrDigit (juce_wchar character) throw(); - /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legel - hex digit. - */ + /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */ static int getHexDigitValue (juce_wchar digit) throw(); + + //============================================================================== + template + static double getDoubleValue (const CharPointerType& text) throw() + { + double result[3] = { 0, 0, 0 }, accumulator[2] = { 0, 0 }; + int exponentAdjustment[2] = { 0, 0 }, exponentAccumulator[2] = { -1, -1 }; + int exponent = 0, decPointIndex = 0, digit = 0; + int lastDigit = 0, numSignificantDigits = 0; + bool isNegative = false, digitsFound = false; + const int maxSignificantDigits = 15 + 2; + + CharPointerType s (text.findEndOfWhitespace()); + juce_wchar c = *s; + + switch (c) + { + case '-': isNegative = true; // fall-through.. + case '+': c = *++s; + } + + switch (c) + { + case 'n': + case 'N': + if ((s[1] == 'a' || s[1] == 'A') && (s[2] == 'n' || s[2] == 'N')) + return std::numeric_limits::quiet_NaN(); + break; + + case 'i': + case 'I': + if ((s[1] == 'n' || s[1] == 'N') && (s[2] == 'f' || s[2] == 'F')) + return std::numeric_limits::infinity(); + break; + } + + for (;;) + { + if (s.isDigit()) + { + lastDigit = digit; + digit = s.getAndAdvance() - '0'; + digitsFound = true; + + if (decPointIndex != 0) + exponentAdjustment[1]++; + + if (numSignificantDigits == 0 && digit == 0) + continue; + + if (++numSignificantDigits > maxSignificantDigits) + { + if (digit > 5) + ++accumulator [decPointIndex]; + else if (digit == 5 && (lastDigit & 1) != 0) + ++accumulator [decPointIndex]; + + if (decPointIndex > 0) + exponentAdjustment[1]--; + else + exponentAdjustment[0]++; + + while (s.isDigit()) + { + ++s; + if (decPointIndex == 0) + exponentAdjustment[0]++; + } + } + else + { + const double maxAccumulatorValue = (double) ((std::numeric_limits::max() - 9) / 10); + if (accumulator [decPointIndex] > maxAccumulatorValue) + { + result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex]) + + accumulator [decPointIndex]; + accumulator [decPointIndex] = 0; + exponentAccumulator [decPointIndex] = 0; + } + + accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit; + exponentAccumulator [decPointIndex]++; + } + } + else if (decPointIndex == 0 && *s == '.') + { + ++s; + decPointIndex = 1; + + if (numSignificantDigits > maxSignificantDigits) + { + while (s.isDigit()) + ++s; + break; + } + } + else + { + break; + } + } + + result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0]; + + if (decPointIndex != 0) + result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1]; + + c = *s; + if ((c == 'e' || c == 'E') && digitsFound) + { + bool negativeExponent = false; + + switch (*++s) + { + case '-': negativeExponent = true; // fall-through.. + case '+': ++s; + } + + while (s.isDigit()) + exponent = (exponent * 10) + (s.getAndAdvance() - '0'); + + if (negativeExponent) + exponent = -exponent; + } + + double r = mulexp10 (result[0], exponent + exponentAdjustment[0]); + if (decPointIndex != 0) + r += mulexp10 (result[1], exponent - exponentAdjustment[1]); + + return isNegative ? -r : r; + } + + //============================================================================== + template + static IntType getIntValue (const CharPointerType& text) throw() + { + IntType v = 0; + CharPointerType s (text.findEndOfWhitespace()); + + const bool isNeg = *s == '-'; + if (isNeg) + ++s; + + for (;;) + { + const juce_wchar c = s.getAndAdvance(); + + if (c >= '0' && c <= '9') + v = v * 10 + (IntType) (c - '0'); + else + break; + } + + return isNeg ? -v : v; + } + + //============================================================================== + static int ftime (char* dest, int maxChars, const char* format, const struct tm* tm) throw(); + static int ftime (juce_wchar* dest, int maxChars, const juce_wchar* format, const struct tm* tm) throw(); + + //============================================================================== + template + static void copyAndAdvance (DestCharPointerType& dest, SrcCharPointerType src) throw() + { + juce_wchar c; + + do + { + c = src.getAndAdvance(); + dest.write (c); + } + while (c != 0); + } + + template + static int copyAndAdvanceUpToBytes (DestCharPointerType& dest, SrcCharPointerType src, int maxBytes) throw() + { + int numBytesDone = 0; + + for (;;) + { + const juce_wchar c = src.getAndAdvance(); + const size_t bytesNeeded = DestCharPointerType::getBytesRequiredFor (c); + + maxBytes -= bytesNeeded; + if (maxBytes < 0) + break; + + numBytesDone += bytesNeeded; + dest.write (c); + if (c == 0) + break; + } + + return numBytesDone; + } + + template + static void copyAndAdvanceUpToNumChars (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) throw() + { + while (--maxChars >= 0) + { + const juce_wchar c = src.getAndAdvance(); + dest.write (c); + if (c == 0) + break; + } + } + + template + static int compare (CharPointerType1 s1, CharPointerType2 s2) throw() + { + for (;;) + { + const int c1 = (int) s1.getAndAdvance(); + const int c2 = (int) s2.getAndAdvance(); + + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } + + return 0; + } + + template + static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) throw() + { + while (--maxChars >= 0) + { + const int c1 = (int) s1.getAndAdvance(); + const int c2 = (int) s2.getAndAdvance(); + + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } + + return 0; + } + + template + static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) throw() + { + for (;;) + { + int c1 = s1.toUpperCase(); + int c2 = s2.toUpperCase(); + ++s1; + ++s2; + + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } + + return 0; + } + + template + static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) throw() + { + while (--maxChars >= 0) + { + int c1 = s1.toUpperCase(); + int c2 = s2.toUpperCase(); + ++s1; + ++s2; + + const int diff = c1 - c2; + if (diff != 0) + return diff < 0 ? -1 : 1; + else if (c1 == 0) + break; + } + + return 0; + } + + template + static int indexOf (CharPointerType1 haystack, const CharPointerType2& needle) throw() + { + int index = 0; + const int needleLength = needle.length(); + + for (;;) + { + if (haystack.compareUpTo (needle, needleLength) == 0) + return index; + + if (haystack.getAndAdvance() == 0) + return -1; + + ++index; + } + } + + template + static int indexOfChar (Type text, const juce_wchar charToFind) throw() + { + int i = 0; + + while (! text.isEmpty()) + { + if (text.getAndAdvance() == charToFind) + return i; + + ++i; + } + + return -1; + } + + template + static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) throw() + { + charToFind = CharacterFunctions::toLowerCase (charToFind); + int i = 0; + + while (! text.isEmpty()) + { + if (text.toLowerCase() == charToFind) + return i; + + ++text; + ++i; + } + + return -1; + } + + template + static Type findEndOfWhitespace (const Type& text) throw() + { + Type p (text); + + while (p.isWhitespace()) + ++p; + + return p; + } + +private: + static double mulexp10 (const double value, int exponent) throw(); }; + #endif // __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ diff --git a/src/text/juce_String.cpp b/src/text/juce_String.cpp index dd87d3dad4..439c761ad3 100644 --- a/src/text/juce_String.cpp +++ b/src/text/juce_String.cpp @@ -27,7 +27,7 @@ #if JUCE_MSVC #pragma warning (push) - #pragma warning (disable: 4514) + #pragma warning (disable: 4514 4996) #endif #include @@ -38,10 +38,6 @@ BEGIN_JUCE_NAMESPACE #include "../memory/juce_Atomic.h" #include "../io/streams/juce_OutputStream.h" -#if JUCE_MSVC - #pragma warning (pop) -#endif - #if defined (JUCE_STRINGS_ARE_UNICODE) && ! JUCE_STRINGS_ARE_UNICODE #error "JUCE_STRINGS_ARE_UNICODE is deprecated! All strings are now unicode by default." #endif @@ -77,7 +73,7 @@ public: static juce_wchar* createCopy (const char* const src, const size_t numChars) { juce_wchar* const dest = createUninitialised (numChars); - CharacterFunctions::copy (dest, src, (int) numChars); + CharPointer_UTF32 (dest).copyAndAdvanceUpToNumChars (CharPointer_UTF8 (src), numChars); dest [numChars] = 0; return dest; } @@ -235,7 +231,7 @@ String::String (const String& stringToCopy, const size_t charsToAllocate) String::String (const char* const t) { if (t != 0 && *t != 0) - text = StringHolder::createCopy (t, CharacterFunctions::length (t)); + text = StringHolder::createCopy (t, CharPointer_UTF8 (t).length()); else text = StringHolder::getEmpty(); } @@ -243,7 +239,7 @@ String::String (const char* const t) String::String (const juce_wchar* const t) { if (t != 0 && *t != 0) - text = StringHolder::createCopy (t, CharacterFunctions::length (t)); + text = StringHolder::createCopy (t, CharPointer_UTF32 (t).length()); else text = StringHolder::getEmpty(); } @@ -363,21 +359,21 @@ namespace NumberToStringConverters return dp; } - juce_wchar* doubleToString (juce_wchar* buffer, int numChars, double n, int numDecPlaces, size_t& len) throw() + char* doubleToString (char* buffer, int numChars, double n, int numDecPlaces, size_t& len) throw() { if (numDecPlaces > 0 && n > -1.0e20 && n < 1.0e20) { - juce_wchar* const end = buffer + numChars; - juce_wchar* t = end; + char* const end = buffer + numChars; + char* t = end; int64 v = (int64) (pow (10.0, numDecPlaces) * std::abs (n) + 0.5); - *--t = (juce_wchar) 0; + *--t = (char) 0; while (numDecPlaces >= 0 || v > 0) { if (numDecPlaces == 0) - *--t = getDecimalPoint(); + *--t = (char) getDecimalPoint(); - *--t = (juce_wchar) ('0' + (v % 10)); + *--t = (char) ('0' + (v % 10)); v /= 10; --numDecPlaces; @@ -391,15 +387,7 @@ namespace NumberToStringConverters } else { - #if JUCE_WINDOWS - #if JUCE_VC7_OR_EARLIER || JUCE_MINGW - len = _snwprintf (buffer, numChars, L"%.9g", n); - #else - len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n); - #endif - #else - len = swprintf (buffer, numChars, L"%.9g", n); - #endif + len = sprintf (buffer, "%.9g", n); return buffer; } } @@ -456,24 +444,24 @@ String::String (const uint64 number) String::String (const float number, const int numberOfDecimalPlaces) { - juce_wchar buffer [48]; + char buffer [48]; size_t len; - juce_wchar* start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); - createInternal (start, len); + char* const start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); + text = StringHolder::createCopy (start, len); } String::String (const double number, const int numberOfDecimalPlaces) { - juce_wchar buffer [48]; + char buffer [48]; size_t len; - juce_wchar* start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), number, numberOfDecimalPlaces, len); - createInternal (start, len); + char* const start = NumberToStringConverters::doubleToString (buffer, numElementsInArray (buffer), number, numberOfDecimalPlaces, len); + text = StringHolder::createCopy (start, len); } //============================================================================== int String::length() const throw() { - return CharacterFunctions::length (text); + return CharPointer_UTF32 (text).length(); } int String::hashCode() const throw() @@ -551,60 +539,62 @@ JUCE_API bool JUCE_CALLTYPE operator<= (const String& string1, const String& str bool String::equalsIgnoreCase (const juce_wchar* t) const throw() { - return t != 0 ? CharacterFunctions::compareIgnoreCase (text, t) == 0 + return t != 0 ? CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF32 (t)) == 0 : isEmpty(); } bool String::equalsIgnoreCase (const char* t) const throw() { - return t != 0 ? CharacterFunctions::compareIgnoreCase (text, t) == 0 + return t != 0 ? CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF8 (t)) == 0 : isEmpty(); } bool String::equalsIgnoreCase (const String& other) const throw() { return text == other.text - || CharacterFunctions::compareIgnoreCase (text, other.text) == 0; + || CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF32 (other.text)) == 0; } int String::compare (const String& other) const throw() { - return (text == other.text) ? 0 : CharacterFunctions::compare (text, other.text); + return (text == other.text) ? 0 : CharPointer_UTF32 (text).compare (CharPointer_UTF32 (other.text)); } int String::compare (const char* other) const throw() { - return other == 0 ? isEmpty() : CharacterFunctions::compare (text, other); + return other == 0 ? isEmpty() : CharPointer_UTF32 (text).compare (CharPointer_UTF8 (other)); } int String::compare (const juce_wchar* other) const throw() { - return other == 0 ? isEmpty() : CharacterFunctions::compare (text, other); + return other == 0 ? isEmpty() : CharPointer_UTF32 (text).compare (CharPointer_UTF32 (other)); } int String::compareIgnoreCase (const String& other) const throw() { - return (text == other.text) ? 0 : CharacterFunctions::compareIgnoreCase (text, other.text); + return (text == other.text) ? 0 : CharPointer_UTF32 (text).compareIgnoreCase (CharPointer_UTF32 (other.text)); } int String::compareLexicographically (const String& other) const throw() { - const juce_wchar* s1 = text; - while (*s1 != 0 && ! CharacterFunctions::isLetterOrDigit (*s1)) + CharPointer_UTF32 s1 (text); + + while (! (s1.isEmpty() || s1.isLetterOrDigit())) ++s1; - const juce_wchar* s2 = other.text; - while (*s2 != 0 && ! CharacterFunctions::isLetterOrDigit (*s2)) + CharPointer_UTF32 s2 (other.text); + + while (! (s2.isEmpty() || s2.isLetterOrDigit())) ++s2; - return CharacterFunctions::compareIgnoreCase (s1, s2); + return s1.compareIgnoreCase (s2); } //============================================================================== String& String::operator+= (const juce_wchar* const t) { if (t != 0) - appendInternal (t, CharacterFunctions::length (t)); + appendInternal (t, CharPointer_UTF32 (t).length()); return *this; } @@ -640,15 +630,6 @@ String& String::operator+= (const int number) return *this; } -String& String::operator+= (const unsigned int number) -{ - juce_wchar buffer [16]; - juce_wchar* const end = buffer + numElementsInArray (buffer); - juce_wchar* const start = NumberToStringConverters::uintToString (end, number); - appendInternal (start, (int) (end - start)); - return *this; -} - void String::append (const juce_wchar* const other, const int howMany) { if (howMany > 0) @@ -745,21 +726,11 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const int number) return string1 += number; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const unsigned int number) -{ - return string1 += number; -} - JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const long number) { return string1 += (int) number; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const unsigned long number) -{ - return string1 += (unsigned int) number; -} - JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const float number) { return string1 += String (number); @@ -812,8 +783,7 @@ int String::lastIndexOfChar (const juce_wchar character) const throw() int String::indexOf (const String& t) const throw() { - const juce_wchar* const r = CharacterFunctions::find (text, t.text); - return r == 0 ? -1 : (int) (r - text); + return t.isEmpty() ? 0 : CharPointer_UTF32 (text).indexOf (CharPointer_UTF32 (t.text)); } int String::indexOfChar (const int startIndex, @@ -843,13 +813,16 @@ int String::indexOfAnyOf (const String& charactersToLookFor, if (startIndex > 0 && startIndex >= length()) return -1; - const juce_wchar* t = text + jmax (0, startIndex); + CharPointer_UTF32 t (text); + int i = jmax (0, startIndex); + t += i; - while (*t != 0) + while (! t.isEmpty()) { - if (CharacterFunctions::indexOfChar (charactersToLookFor.text, *t, ignoreCase) >= 0) - return (int) (t - text); + if (CharPointer_UTF32 (charactersToLookFor.text).indexOf (*t, ignoreCase) >= 0) + return i; + ++i; ++t; } @@ -861,22 +834,21 @@ int String::indexOf (const int startIndex, const String& other) const throw() if (startIndex > 0 && startIndex >= length()) return -1; - const juce_wchar* const found = CharacterFunctions::find (text + jmax (0, startIndex), other.text); - - return found == 0 ? -1 : (int) (found - text); + int i = CharPointer_UTF32 (text + jmax (0, startIndex)).indexOf (CharPointer_UTF32 (other.text)); + return i >= 0 ? i + startIndex : -1; } int String::indexOfIgnoreCase (const String& other) const throw() { - if (other.isNotEmpty()) - { - const int len = other.length(); - const int end = length() - len; + if (other.isEmpty()) + return 0; - for (int i = 0; i <= end; ++i) - if (CharacterFunctions::compareIgnoreCase (text + i, other.text, len) == 0) - return i; - } + const int len = other.length(); + const int end = length() - len; + + for (int i = 0; i <= end; ++i) + if (CharPointer_UTF32 (text + i).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), len) == 0) + return i; return -1; } @@ -889,7 +861,7 @@ int String::indexOfIgnoreCase (const int startIndex, const String& other) const const int end = length() - len; for (int i = jmax (0, startIndex); i <= end; ++i) - if (CharacterFunctions::compareIgnoreCase (text + i, other.text, len) == 0) + if (CharPointer_UTF32 (text + i).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), len) == 0) return i; } @@ -909,9 +881,10 @@ int String::lastIndexOf (const String& other) const throw() while (i >= 0) { - if (CharacterFunctions::compare (n--, other.text, len) == 0) + if (CharPointer_UTF32 (n).compareUpTo (CharPointer_UTF32 (other.text), len) == 0) return i; + --n; --i; } } @@ -933,9 +906,10 @@ int String::lastIndexOfIgnoreCase (const String& other) const throw() while (i >= 0) { - if (CharacterFunctions::compareIgnoreCase (n--, other.text, len) == 0) + if (CharPointer_UTF32 (n).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), len) == 0) return i; + --n; --i; } } @@ -947,7 +921,7 @@ int String::lastIndexOfIgnoreCase (const String& other) const throw() int String::lastIndexOfAnyOf (const String& charactersToLookFor, const bool ignoreCase) const throw() { for (int i = length(); --i >= 0;) - if (CharacterFunctions::indexOfChar (charactersToLookFor.text, text[i], ignoreCase) >= 0) + if (CharPointer_UTF32 (charactersToLookFor.text).indexOf (text[i], ignoreCase) >= 0) return i; return -1; @@ -983,18 +957,16 @@ int String::indexOfWholeWord (const String& word) const throw() { if (word.isNotEmpty()) { + CharPointer_UTF32 t (text); const int wordLen = word.length(); - const int end = length() - wordLen; - const juce_wchar* t = text; + const int end = t.length() - wordLen; for (int i = 0; i <= end; ++i) { - if (CharacterFunctions::compare (t, word.text, wordLen) == 0 - && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) - && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) - { + if (t.compareUpTo (CharPointer_UTF32 (word.text), wordLen) == 0 + && (i == 0 || ! (t - 1).isLetterOrDigit()) + && ! (t + wordLen).isLetterOrDigit()) return i; - } ++t; } @@ -1007,18 +979,16 @@ int String::indexOfWholeWordIgnoreCase (const String& word) const throw() { if (word.isNotEmpty()) { + CharPointer_UTF32 t (text); const int wordLen = word.length(); - const int end = length() - wordLen; - const juce_wchar* t = text; + const int end = t.length() - wordLen; for (int i = 0; i <= end; ++i) { - if (CharacterFunctions::compareIgnoreCase (t, word.text, wordLen) == 0 - && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1))) - && ! CharacterFunctions::isLetterOrDigit (t [wordLen])) - { + if (t.compareIgnoreCaseUpTo (CharPointer_UTF32 (word.text), wordLen) == 0 + && (i == 0 || ! (t + -1).isLetterOrDigit()) + && ! (t + wordLen).isLetterOrDigit()) return i; - } ++t; } @@ -1040,43 +1010,43 @@ bool String::containsWholeWordIgnoreCase (const String& wordToLookFor) const thr //============================================================================== namespace WildCardHelpers { - int indexOfMatch (const juce_wchar* const wildcard, - const juce_wchar* const test, + int indexOfMatch (CharPointer_UTF32 wildcard, + CharPointer_UTF32 test, const bool ignoreCase) throw() { int start = 0; - while (test [start] != 0) + while (! test.isEmpty()) { - int i = 0; + CharPointer_UTF32 t (test); + CharPointer_UTF32 w (wildcard); for (;;) { - const juce_wchar wc = wildcard [i]; - const juce_wchar c = test [i + start]; + const juce_wchar wc = *w; + const juce_wchar tc = *t; - if (wc == c - || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) - || (wc == '?' && c != 0)) + if (wc == tc + || (ignoreCase && w.toLowerCase() == t.toLowerCase()) + || (wc == '?' && tc != 0)) { if (wc == 0) return start; - ++i; + ++t; + ++w; } else { - if (wc == '*' && (wildcard [i + 1] == 0 - || indexOfMatch (wildcard + i + 1, test + start + i, ignoreCase) >= 0)) - { + if (wc == '*' && (w[1] == 0 || indexOfMatch (w + 1, t, ignoreCase) >= 0)) return start; - } break; } } ++start; + ++test; } return -1; @@ -1085,26 +1055,27 @@ namespace WildCardHelpers bool String::matchesWildcard (const String& wildcard, const bool ignoreCase) const throw() { - int i = 0; + CharPointer_UTF32 w (wildcard.text); + CharPointer_UTF32 t (text); for (;;) { - const juce_wchar wc = wildcard.text [i]; - const juce_wchar c = text [i]; + const juce_wchar wc = *w; + const juce_wchar tc = *t; - if (wc == c - || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c)) - || (wc == '?' && c != 0)) + if (wc == tc + || (ignoreCase && w.toLowerCase() == t.toLowerCase()) + || (wc == '?' && tc != 0)) { if (wc == 0) return true; - ++i; + ++w; + ++t; } else { - return wc == '*' && (wildcard [i + 1] == 0 - || WildCardHelpers::indexOfMatch (wildcard.text + i + 1, text + i, ignoreCase) >= 0); + return wc == '*' && (w[1] == 0 || WildCardHelpers::indexOfMatch (w + 1, t, ignoreCase) >= 0); } } } @@ -1283,12 +1254,12 @@ const String String::replaceCharacters (const String& charactersToReplace, //============================================================================== bool String::startsWith (const String& other) const throw() { - return CharacterFunctions::compare (text, other.text, other.length()) == 0; + return CharPointer_UTF32 (text).compareUpTo (CharPointer_UTF32 (other.text), other.length()) == 0; } bool String::startsWithIgnoreCase (const String& other) const throw() { - return CharacterFunctions::compareIgnoreCase (text, other.text, other.length()) == 0; + return CharPointer_UTF32 (text).compareIgnoreCaseUpTo (CharPointer_UTF32 (other.text), other.length()) == 0; } bool String::startsWithChar (const juce_wchar character) const throw() @@ -1312,7 +1283,7 @@ bool String::endsWith (const String& other) const throw() const int otherLen = other.length(); return thisLen >= otherLen - && CharacterFunctions::compare (text + thisLen - otherLen, other.text) == 0; + && CharPointer_UTF32 (text + thisLen - otherLen).compare (CharPointer_UTF32 (other.text)) == 0; } bool String::endsWithIgnoreCase (const String& other) const throw() @@ -1321,21 +1292,49 @@ bool String::endsWithIgnoreCase (const String& other) const throw() const int otherLen = other.length(); return thisLen >= otherLen - && CharacterFunctions::compareIgnoreCase (text + thisLen - otherLen, other.text) == 0; + && CharPointer_UTF32 (text + thisLen - otherLen).compareIgnoreCase (CharPointer_UTF32 (other.text)) == 0; } //============================================================================== const String String::toUpperCase() const { - String result (*this, size_t()); - CharacterFunctions::toUpperCase (result.text); + String result (Preallocation (this->length())); + + CharPointer_UTF32 dest (result.text); + CharPointer_UTF32 src (text); + + for (;;) + { + const juce_wchar c = src.toUpperCase(); + dest.write (c); + + if (c == 0) + break; + + ++src; + } + return result; } const String String::toLowerCase() const { - String result (*this, size_t()); - CharacterFunctions::toLowerCase (result.text); + String result (Preallocation (this->length())); + + CharPointer_UTF32 dest (result.text); + CharPointer_UTF32 src (text); + + for (;;) + { + const juce_wchar c = src.toLowerCase(); + dest.write (c); + + if (c == 0) + break; + + ++src; + } + return result; } @@ -1493,13 +1492,13 @@ const String String::trim() const int start = 0; - while (CharacterFunctions::isWhitespace (text [start])) + while (CharPointer_UTF32 (text + start).isWhitespace()) ++start; const int len = length(); int end = len - 1; - while ((end >= start) && CharacterFunctions::isWhitespace (text [end])) + while ((end >= start) && CharPointer_UTF32 (text + end).isWhitespace()) --end; ++end; @@ -1517,15 +1516,15 @@ const String String::trimStart() const if (isEmpty()) return empty; - const juce_wchar* t = text; + CharPointer_UTF32 t (text); - while (CharacterFunctions::isWhitespace (*t)) + while (t.isWhitespace()) ++t; - if (t == text) + if (t.getAddress() == text) return *this; - return String (t); + return String (t.getAddress()); } const String String::trimEnd() const @@ -1533,12 +1532,13 @@ const String String::trimEnd() const if (isEmpty()) return empty; - const juce_wchar* endT = text + (length() - 1); + CharPointer_UTF32 endT (text); + endT = endT.findTerminatingNull() - 1; - while ((endT >= text) && CharacterFunctions::isWhitespace (*endT)) + while ((endT.getAddress() >= text) && endT.isWhitespace()) --endT; - return String (text, (int) (++endT - text)); + return String (text, 1 + (int) (endT.getAddress() - text)); } const String String::trimCharactersAtStart (const String& charactersToTrim) const @@ -1645,10 +1645,10 @@ const String String::initialSectionNotContaining (const String& charactersToStop bool String::containsOnly (const String& chars) const throw() { - const juce_wchar* t = text; + CharPointer_UTF32 t (text); - while (*t != 0) - if (! chars.containsChar (*t++)) + while (! t.isEmpty()) + if (! chars.containsChar (t.getAndAdvance())) return false; return true; @@ -1667,12 +1667,16 @@ bool String::containsAnyOf (const String& chars) const throw() bool String::containsNonWhitespaceChars() const throw() { - const juce_wchar* t = text; + CharPointer_UTF32 t (text); - while (*t != 0) - if (! CharacterFunctions::isWhitespace (*t++)) + while (! t.isEmpty()) + { + if (! t.isWhitespace()) return true; + ++t; + } + return false; } @@ -1703,6 +1707,11 @@ const String String::formatted (const juce_wchar* const pf, ... ) #if JUCE_MSVC #pragma warning (pop) #endif +#elif JUCE_ANDROID + HeapBlock temp (bufferSize); + const int num = (int) vsnprintf (temp.getData(), bufferSize - 1, String (pf).toUTF8(), args); + if (num > 0) + CharPointer_UTF32 (result.text).copyAndAdvance (CharPointer_UTF8 (temp.getData())); #else const int num = (int) vswprintf (result.text, bufferSize - 1, pf, args); #endif @@ -1724,28 +1733,27 @@ const String String::formatted (const juce_wchar* const pf, ... ) //============================================================================== int String::getIntValue() const throw() { - return CharacterFunctions::getIntValue (text); + return CharPointer_UTF32 (text).getIntValue32(); } int String::getTrailingIntValue() const throw() { int n = 0; int mult = 1; - const juce_wchar* t = text + length(); + CharPointer_UTF32 t (text); + t = t.findTerminatingNull(); - while (--t >= text) + while ((--t).getAddress() >= text) { - const juce_wchar c = *t; - - if (! CharacterFunctions::isDigit (c)) + if (! t.isDigit()) { - if (c == '-') + if (*t == '-') n = -n; break; } - n += mult * (c - '0'); + n += mult * (*t - '0'); mult *= 10; } @@ -1754,20 +1762,20 @@ int String::getTrailingIntValue() const throw() int64 String::getLargeIntValue() const throw() { - return CharacterFunctions::getInt64Value (text); + return CharPointer_UTF32 (text).getIntValue64(); } float String::getFloatValue() const throw() { - return (float) CharacterFunctions::getDoubleValue (text); + return (float) getDoubleValue(); } double String::getDoubleValue() const throw() { - return CharacterFunctions::getDoubleValue (text); + return CharPointer_UTF32 (text).getDoubleValue(); } -static const juce_wchar* const hexDigits = JUCE_T("0123456789abcdef"); +static const char* const hexDigits = "0123456789abcdef"; const String String::toHexString (const int number) { @@ -1779,7 +1787,7 @@ const String String::toHexString (const int number) do { - *--t = hexDigits [v & 15]; + *--t = (juce_wchar) hexDigits [v & 15]; v >>= 4; } while (v != 0); @@ -1797,7 +1805,7 @@ const String String::toHexString (const int64 number) do { - *--t = hexDigits [(int) (v & 15)]; + *--t = (juce_wchar) hexDigits [(int) (v & 15)]; v >>= 4; } while (v != 0); @@ -1825,8 +1833,8 @@ const String String::toHexString (const unsigned char* data, const int size, con for (int i = 0; i < size; ++i) { - *d++ = hexDigits [(*data) >> 4]; - *d++ = hexDigits [(*data) & 0xf]; + *d++ = (juce_wchar) hexDigits [(*data) >> 4]; + *d++ = (juce_wchar) hexDigits [(*data) & 0xf]; ++data; if (groupSize > 0 && (i % groupSize) == (groupSize - 1) && i < (size - 1)) @@ -1840,18 +1848,14 @@ const String String::toHexString (const unsigned char* data, const int size, con int String::getHexValue32() const throw() { int result = 0; - const juce_wchar* c = text; + CharPointer_UTF32 t (text); - for (;;) + while (! t.isEmpty()) { - const int hexValue = CharacterFunctions::getHexDigitValue (*c); + const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (hexValue >= 0) result = (result << 4) | hexValue; - else if (*c == 0) - break; - - ++c; } return result; @@ -1860,18 +1864,14 @@ int String::getHexValue32() const throw() int64 String::getHexValue64() const throw() { int64 result = 0; - const juce_wchar* c = text; + CharPointer_UTF32 t (text); - for (;;) + while (! t.isEmpty()) { - const int hexValue = CharacterFunctions::getHexDigitValue (*c); + const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (hexValue >= 0) result = (result << 4) | hexValue; - else if (*c == 0) - break; - - ++c; } return result; @@ -1926,123 +1926,42 @@ const String String::createStringFromData (const void* const data_, const int si } //============================================================================== +void* String::createSpaceAtEndOfBuffer (const size_t numExtraBytes) const +{ + const int currentLen = length() + 1; + + String& mutableThis = const_cast (*this); + mutableThis.text = StringHolder::makeUniqueWithSize (mutableThis.text, currentLen + 1 + numExtraBytes / sizeof (juce_wchar)); + + return mutableThis.text + currentLen; +} + const char* String::toUTF8() const { if (isEmpty()) - { return reinterpret_cast (text); - } - else - { - const int currentLen = length() + 1; - const int utf8BytesNeeded = getNumBytesAsUTF8(); - - String* const mutableThis = const_cast (this); - mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar)); - char* const otherCopy = reinterpret_cast (mutableThis->text + currentLen); + const int utf8BytesNeeded = getNumBytesAsUTF8(); + char* const utf8Area = static_cast (createSpaceAtEndOfBuffer (utf8BytesNeeded)); -#if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) - *(juce_wchar*) (otherCopy + (utf8BytesNeeded & ~(sizeof (juce_wchar) - 1))) = 0; -#endif - copyToUTF8 (otherCopy, std::numeric_limits::max()); + #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) + *(juce_wchar*) (utf8Area + (utf8BytesNeeded & ~(sizeof (juce_wchar) - 1))) = 0; + #endif - return otherCopy; - } + copyToUTF8 (utf8Area, std::numeric_limits::max()); + return utf8Area; } int String::copyToUTF8 (char* const buffer, const int maxBufferSizeBytes) const throw() { jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! - int num = 0, index = 0; - - for (;;) - { - const uint32 c = (uint32) text [index++]; - - if (c >= 0x80) - { - int numExtraBytes = 1; - - if (c >= 0x800) - { - ++numExtraBytes; - - if (c >= 0x10000) - ++numExtraBytes; - } - - if (buffer != 0) - { - if (num + numExtraBytes >= maxBufferSizeBytes) - { - buffer [num++] = 0; - break; - } - else - { - buffer [num++] = (uint8) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6))); - - while (--numExtraBytes >= 0) - buffer [num++] = (uint8) (0x80 | (0x3f & (c >> (numExtraBytes * 6)))); - } - } - else - { - num += numExtraBytes + 1; - } - } - else - { - if (buffer != 0) - { - if (num + 1 >= maxBufferSizeBytes) - { - buffer [num++] = 0; - break; - } - - buffer [num] = (uint8) c; - } - - ++num; - } - - if (c == 0) - break; - } - - return num; + return CharPointer_UTF8 (buffer).copyAndAdvanceUpToBytes (CharPointer_UTF32 (text), maxBufferSizeBytes); } int String::getNumBytesAsUTF8() const throw() { - int num = 0; - const juce_wchar* t = text; - - for (;;) - { - const uint32 c = (uint32) *t; - - if (c >= 0x80) - { - ++num; - if (c >= 0x800) - { - ++num; - if (c >= 0x10000) - ++num; - } - } - else if (c == 0) - break; - - ++num; - ++t; - } - - return num; + return CharPointer_UTF8::getBytesRequiredFor (CharPointer_UTF32 (text)); } const String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) @@ -2111,41 +2030,41 @@ const String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) //============================================================================== const char* String::toCString() const { + #if JUCE_ANDROID + return toUTF8(); + #else if (isEmpty()) - { return reinterpret_cast (text); - } - else - { - const int len = length(); - String* const mutableThis = const_cast (this); - mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, (len + 1) * 2); - - char* otherCopy = reinterpret_cast (mutableThis->text + len + 1); - CharacterFunctions::copy (otherCopy, text, len); - otherCopy [len] = 0; - return otherCopy; - } -} -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514 4996) -#endif + const int len = getNumBytesAsCString(); + char* const extraSpace = static_cast (createSpaceAtEndOfBuffer (len + 1)); + wcstombs (extraSpace, text, len); + extraSpace [len] = 0; + return extraSpace; + #endif +} int String::getNumBytesAsCString() const throw() { + #if JUCE_ANDROID + return getNumBytesAsUTF8(); + #else return (int) wcstombs (0, text, 0); + #endif } int String::copyToCString (char* destBuffer, const int maxBufferSizeBytes) const throw() { + #if JUCE_ANDROID + return copyToUTF8 (destBuffer, maxBufferSizeBytes); + #else const int numBytes = (int) wcstombs (destBuffer, text, maxBufferSizeBytes); if (destBuffer != 0 && numBytes >= 0) destBuffer [numBytes] = 0; return numBytes; + #endif } #if JUCE_MSVC @@ -2223,9 +2142,12 @@ public: expect (s1.startsWithIgnoreCase ("aB") && s1.endsWithIgnoreCase ("CD")); expect (s1.endsWith ("bcd") && ! s1.endsWith ("aabcd")); expect (s1.indexOf (String::empty) == 0); + expect (s1.indexOfIgnoreCase (String::empty) == 0); expect (s1.startsWith (String::empty) && s1.endsWith (String::empty) && s1.contains (String::empty)); expect (s1.contains ("cd") && s1.contains ("ab") && s1.contains ("abcd")); - expect (s1.containsChar ('a') && ! s1.containsChar (0)); + expect (s1.containsChar ('a')); + expect (! s1.containsChar ('x')); + expect (! s1.containsChar (0)); expect (String ("abc foo bar").containsWholeWord ("abc") && String ("abc foo bar").containsWholeWord ("abc")); } @@ -2254,7 +2176,7 @@ public: expect (String ("45454545x").lastIndexOf (L"45") == 6); expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7); expect (String ("45454545x").lastIndexOfAnyOf (L"456x") == 8); - expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("Ab") == 6); + expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("aB") == 6); expect (s.indexOfChar (L'4') == 4); expect (s + s == "012345678012345678"); expect (s.startsWith (s)); @@ -2346,6 +2268,7 @@ public: expect (! s5.containsWholeWordIgnoreCase (L"Wordx")); expect (!s5.containsWholeWordIgnoreCase ("xWord2")); expect (s5.containsNonWhitespaceChars()); + expect (s5.containsOnly ("ordw23 ")); expect (! String (" \n\r\t").containsNonWhitespaceChars()); expect (s5.matchesWildcard (L"wor*", false)); @@ -2354,50 +2277,50 @@ public: expect (s5.matchesWildcard ("*word?", true)); expect (s5.matchesWildcard (L"Word*3", true)); - expect (s5.fromFirstOccurrenceOf (String::empty, true, false) == s5); - expect (s5.fromFirstOccurrenceOf ("xword2", true, false) == s5.substring (100)); - expect (s5.fromFirstOccurrenceOf (L"word2", true, false) == s5.substring (5)); - expect (s5.fromFirstOccurrenceOf ("Word2", true, true) == s5.substring (5)); - expect (s5.fromFirstOccurrenceOf ("word2", false, false) == s5.getLastCharacters (6)); - expect (s5.fromFirstOccurrenceOf (L"Word2", false, true) == s5.getLastCharacters (6)); + expectEquals (s5.fromFirstOccurrenceOf (String::empty, true, false), s5); + expectEquals (s5.fromFirstOccurrenceOf ("xword2", true, false), s5.substring (100)); + expectEquals (s5.fromFirstOccurrenceOf (L"word2", true, false), s5.substring (5)); + expectEquals (s5.fromFirstOccurrenceOf ("Word2", true, true), s5.substring (5)); + expectEquals (s5.fromFirstOccurrenceOf ("word2", false, false), s5.getLastCharacters (6)); + expectEquals (s5.fromFirstOccurrenceOf (L"Word2", false, true), s5.getLastCharacters (6)); - expect (s5.fromLastOccurrenceOf (String::empty, true, false) == s5); - expect (s5.fromLastOccurrenceOf (L"wordx", true, false) == s5); - expect (s5.fromLastOccurrenceOf ("word", true, false) == s5.getLastCharacters (5)); - expect (s5.fromLastOccurrenceOf (L"worD", true, true) == s5.getLastCharacters (5)); - expect (s5.fromLastOccurrenceOf ("word", false, false) == s5.getLastCharacters (1)); - expect (s5.fromLastOccurrenceOf (L"worD", false, true) == s5.getLastCharacters (1)); + expectEquals (s5.fromLastOccurrenceOf (String::empty, true, false), s5); + expectEquals (s5.fromLastOccurrenceOf (L"wordx", true, false), s5); + expectEquals (s5.fromLastOccurrenceOf ("word", true, false), s5.getLastCharacters (5)); + expectEquals (s5.fromLastOccurrenceOf (L"worD", true, true), s5.getLastCharacters (5)); + expectEquals (s5.fromLastOccurrenceOf ("word", false, false), s5.getLastCharacters (1)); + expectEquals (s5.fromLastOccurrenceOf (L"worD", false, true), s5.getLastCharacters (1)); expect (s5.upToFirstOccurrenceOf (String::empty, true, false).isEmpty()); - expect (s5.upToFirstOccurrenceOf ("word4", true, false) == s5); - expect (s5.upToFirstOccurrenceOf (L"word2", true, false) == s5.substring (0, 10)); - expect (s5.upToFirstOccurrenceOf ("Word2", true, true) == s5.substring (0, 10)); - expect (s5.upToFirstOccurrenceOf (L"word2", false, false) == s5.substring (0, 5)); - expect (s5.upToFirstOccurrenceOf ("Word2", false, true) == s5.substring (0, 5)); - - expect (s5.upToLastOccurrenceOf (String::empty, true, false) == s5); - expect (s5.upToLastOccurrenceOf ("zword", true, false) == s5); - expect (s5.upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); - expect (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false) == s5.dropLastCharacters (1)); - expect (s5.upToLastOccurrenceOf ("Word", true, true) == s5.dropLastCharacters (1)); - expect (s5.upToLastOccurrenceOf ("word", false, false) == s5.dropLastCharacters (5)); - expect (s5.upToLastOccurrenceOf ("Word", false, true) == s5.dropLastCharacters (5)); - - expect (s5.replace ("word", L"xyz", false) == String ("xyz xyz2 xyz3")); + expectEquals (s5.upToFirstOccurrenceOf ("word4", true, false), s5); + expectEquals (s5.upToFirstOccurrenceOf (L"word2", true, false), s5.substring (0, 10)); + expectEquals (s5.upToFirstOccurrenceOf ("Word2", true, true), s5.substring (0, 10)); + expectEquals (s5.upToFirstOccurrenceOf (L"word2", false, false), s5.substring (0, 5)); + expectEquals (s5.upToFirstOccurrenceOf ("Word2", false, true), s5.substring (0, 5)); + + expectEquals (s5.upToLastOccurrenceOf (String::empty, true, false), s5); + expectEquals (s5.upToLastOccurrenceOf ("zword", true, false), s5); + expectEquals (s5.upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); + expectEquals (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1)); + expectEquals (s5.upToLastOccurrenceOf ("Word", true, true), s5.dropLastCharacters (1)); + expectEquals (s5.upToLastOccurrenceOf ("word", false, false), s5.dropLastCharacters (5)); + expectEquals (s5.upToLastOccurrenceOf ("Word", false, true), s5.dropLastCharacters (5)); + + expectEquals (s5.replace ("word", L"xyz", false), String ("xyz xyz2 xyz3")); expect (s5.replace (L"Word", "xyz", true) == "xyz xyz2 xyz3"); expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz"); expect (s5.replace ("Word", "", true) == " 2 3"); - expect (s5.replace ("Word2", L"xyz", true) == String ("word xyz word3")); + expectEquals (s5.replace ("Word2", L"xyz", true), String ("word xyz word3")); expect (s5.replaceCharacter (L'w', 'x') != s5); - expect (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w') == s5); + expectEquals (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w'), s5); expect (s5.replaceCharacters ("wo", "xy") != s5); - expect (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo") == s5); - expect (s5.retainCharacters ("1wordxya") == String ("wordwordword")); + expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo"), s5); + expectEquals (s5.retainCharacters ("1wordxya"), String ("wordwordword")); expect (s5.retainCharacters (String::empty).isEmpty()); expect (s5.removeCharacters ("1wordxya") == " 2 3"); - expect (s5.removeCharacters (String::empty) == s5); + expectEquals (s5.removeCharacters (String::empty), s5); expect (s5.initialSectionContainingOnly ("word") == L"word"); - expect (s5.initialSectionNotContaining (String ("xyz ")) == String ("word")); + expectEquals (s5.initialSectionNotContaining (String ("xyz ")), String ("word")); expect (! s5.isQuotedString()); expect (s5.quoted().isQuotedString()); expect (! s5.quoted().unquoted().isQuotedString()); @@ -2405,14 +2328,14 @@ public: expect (String ("'x").isQuotedString()); String s6 (" \t xyz \t\r\n"); - expect (s6.trim() == String ("xyz")); + expectEquals (s6.trim(), String ("xyz")); expect (s6.trim().trim() == "xyz"); - expect (s5.trim() == s5); - expect (s6.trimStart().trimEnd() == s6.trim()); - expect (s6.trimStart().trimEnd() == s6.trimEnd().trimStart()); - expect (s6.trimStart().trimStart().trimEnd().trimEnd() == s6.trimEnd().trimStart()); + expectEquals (s5.trim(), s5); + expectEquals (s6.trimStart().trimEnd(), s6.trim()); + expectEquals (s6.trimStart().trimEnd(), s6.trimEnd().trimStart()); + expectEquals (s6.trimStart().trimStart().trimEnd().trimEnd(), s6.trimEnd().trimStart()); expect (s6.trimStart() != s6.trimEnd()); - expect (("\t\r\n " + s6 + "\t\n \r").trim() == s6.trim()); + expectEquals (("\t\r\n " + s6 + "\t\n \r").trim(), s6.trim()); expect (String::repeatedString ("xyz", 3) == L"xyzxyzxyz"); } @@ -2425,12 +2348,12 @@ public: char buffer [100]; memset (buffer, 0xff, sizeof (buffer)); s.copyToUTF8 (buffer, 100); - expect (String::fromUTF8 (buffer, 100) == s); + expectEquals (String::fromUTF8 (buffer, 100), s); juce_wchar bufferUnicode [100]; memset (bufferUnicode, 0xff, sizeof (bufferUnicode)); s.copyToUnicode (bufferUnicode, 100); - expect (String (bufferUnicode, 100) == s); + expectEquals (String (bufferUnicode, 100), s); } { diff --git a/src/text/juce_String.h b/src/text/juce_String.h index 19aff2aa5b..871e19326e 100644 --- a/src/text/juce_String.h +++ b/src/text/juce_String.h @@ -27,6 +27,20 @@ #define __JUCE_STRING_JUCEHEADER__ #include "juce_CharacterFunctions.h" + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4514 4996) +#endif + +#include "juce_CharPointer_UTF8.h" +#include "juce_CharPointer_UTF16.h" +#include "juce_CharPointer_UTF32.h" + +#if JUCE_MSVC + #pragma warning (pop) +#endif + class OutputStream; @@ -115,8 +129,6 @@ public: String& operator+= (juce_wchar characterToAppend); /** Appends a decimal number at the end of this string. */ String& operator+= (int numberToAppend); - /** Appends a decimal number at the end of this string. */ - String& operator+= (unsigned int numberToAppend); /** Appends a string at the end of this one. @@ -963,7 +975,8 @@ public: */ const char* toCString() const; - /** Returns the number of bytes + /** Returns the number of bytes required to represent this string as C-string. + The number returned does NOT include the trailing zero. */ int getNumBytesAsCString() const throw(); @@ -1054,6 +1067,7 @@ private: void createInternal (const juce_wchar* text, size_t numChars); void appendInternal (const juce_wchar* text, int numExtraChars); + void* createSpaceAtEndOfBuffer (size_t numExtraBytes) const; // This private cast operator should prevent strings being accidentally cast // to bools (this is possible because the compiler can add an implicit cast @@ -1099,12 +1113,8 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, short number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int number); /** Appends a decimal number at the end of a string. */ -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, unsigned int number); -/** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, long number); /** Appends a decimal number at the end of a string. */ -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, unsigned long number); -/** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, float number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, double number); diff --git a/src/text/juce_StringArray.cpp b/src/text/juce_StringArray.cpp index 21a6480a31..3b65941ce6 100644 --- a/src/text/juce_StringArray.cpp +++ b/src/text/juce_StringArray.cpp @@ -470,11 +470,13 @@ void StringArray::appendNumbersToDuplicates (const bool ignoreCase, const juce_wchar* preNumberString, const juce_wchar* postNumberString) { + String defaultPre (" ("), defaultPost (")"); // (these aren't literals because of non-unicode literals on Android) + if (preNumberString == 0) - preNumberString = L" ("; + preNumberString = defaultPre; if (postNumberString == 0) - postNumberString = L")"; + postNumberString = defaultPost; for (int i = 0; i < size() - 1; ++i) { diff --git a/src/text/juce_XmlDocument.cpp b/src/text/juce_XmlDocument.cpp index da6dd6fdd1..5b9c31ebd3 100644 --- a/src/text/juce_XmlDocument.cpp +++ b/src/text/juce_XmlDocument.cpp @@ -35,12 +35,14 @@ BEGIN_JUCE_NAMESPACE //============================================================================== XmlDocument::XmlDocument (const String& documentText) : originalText (documentText), + input (0), ignoreEmptyTextElements (true) { } XmlDocument::XmlDocument (const File& file) - : ignoreEmptyTextElements (true), + : input (0), + ignoreEmptyTextElements (true), inputSource (new FileInputSource (file)) { } @@ -122,7 +124,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle } } - input = textToParse; + input = static_cast (textToParse); lastError = String::empty; errorOccurred = false; outOfData = false; @@ -136,7 +138,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle { skipHeader(); - if (input != 0) + if (input.getAddress() != 0) { ScopedPointer result (readNextElement (! onlyReadOuterDocumentElement)); @@ -198,18 +200,16 @@ int XmlDocument::findNextTokenLength() throw() void XmlDocument::skipHeader() { - const juce_wchar* const found = CharacterFunctions::find (input, JUCE_T("= 0) { - input = found; - input = CharacterFunctions::find (input, JUCE_T("?>")); - - if (input == 0) + const int headerEnd = (input + headerStart).indexOf (CharPointer_UTF8 ("?>")); + if (headerEnd < 0) return; #if JUCE_DEBUG - const String header (found, input - found); + const String header ((input + headerStart).getAddress(), headerEnd - headerStart); const String encoding (header.fromFirstOccurrenceOf ("encoding", false, true) .fromFirstOccurrenceOf ("=", false, false) .fromFirstOccurrenceOf ("\"", false, false) @@ -225,16 +225,17 @@ void XmlDocument::skipHeader() jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-")); #endif - input += 2; + input += headerEnd + 2; } skipNextWhiteSpace(); - const juce_wchar* docType = CharacterFunctions::find (input, JUCE_T("")); + const int closeComment = input.indexOf (CharPointer_UTF8 ("-->")); - if (closeComment == 0) + if (closeComment < 0) { outOfData = true; break; } - input = closeComment + 3; + input += closeComment + 3; continue; } else if (input[1] == '?') { - const juce_wchar* const closeBracket = CharacterFunctions::find (input, JUCE_T("?>")); + const int closeBracket = input.indexOf (CharPointer_UTF8 ("?>")); - if (closeBracket == 0) + if (closeBracket < 0) { outOfData = true; break; } - input = closeBracket + 2; + input += closeBracket + 2; continue; } } @@ -324,7 +324,7 @@ void XmlDocument::readQuotedString (String& result) else { --input; - const juce_wchar* const start = input; + const CharPointer_UTF32 start (input); for (;;) { @@ -332,14 +332,14 @@ void XmlDocument::readQuotedString (String& result) if (character == quote) { - result.append (start, (int) (input - start)); + result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); ++input; return; } else if (character == '&') { - result.append (start, (int) (input - start)); + result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); break; } else if (character == 0) @@ -363,11 +363,11 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (outOfData) return 0; - input = CharacterFunctions::find (input, JUCE_T("<")); + const int openBracket = input.indexOf ((juce_wchar) '<'); - if (input != 0) + if (openBracket >= 0) { - ++input; + input += openBracket + 1; int tagLen = findNextTokenLength(); if (tagLen == 0) @@ -383,7 +383,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) } } - node = new XmlElement (String (input, tagLen)); + node = new XmlElement (String (input.getAddress(), tagLen)); input += tagLen; LinkedListPointer::Appender attributeAppender (node->attributes); @@ -419,7 +419,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (attNameLen > 0) { - const juce_wchar* attNameStart = input; + const CharPointer_UTF32 attNameStart (input); input += attNameLen; skipNextWhiteSpace(); @@ -433,7 +433,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (nextChar == '"' || nextChar == '\'') { XmlElement::XmlAttributeNode* const newAtt - = new XmlElement::XmlAttributeNode (String (attNameStart, attNameLen), + = new XmlElement::XmlAttributeNode (String (attNameStart.getAddress(), attNameLen), String::empty); readQuotedString (newAtt->value); @@ -462,7 +462,7 @@ void XmlDocument::readChildElements (XmlElement* parent) for (;;) { - const juce_wchar* const preWhitespaceInput = input; + const CharPointer_UTF32 preWhitespaceInput (input); skipNextWhiteSpace(); if (outOfData) @@ -476,8 +476,11 @@ void XmlDocument::readChildElements (XmlElement* parent) if (input[1] == '/') { // our close tag.. - input = CharacterFunctions::find (input, JUCE_T(">")); - ++input; + const int closeTag = input.indexOf ((juce_wchar) '>'); + + if (closeTag >= 0) + input += closeTag + 1; + break; } else if (input[1] == '!' @@ -490,7 +493,7 @@ void XmlDocument::readChildElements (XmlElement* parent) && input[8] == '[') { input += 9; - const juce_wchar* const inputStart = input; + const CharPointer_UTF32 inputStart (input); int len = 0; @@ -514,7 +517,7 @@ void XmlDocument::readChildElements (XmlElement* parent) ++len; } - childAppender.append (XmlElement::createTextElement (String (inputStart, len))); + childAppender.append (XmlElement::createTextElement (String (inputStart.getAddress(), len))); } else { @@ -553,10 +556,10 @@ void XmlDocument::readChildElements (XmlElement* parent) if (entity.startsWithChar ('<') && entity [1] != 0) { - const juce_wchar* const oldInput = input; + const CharPointer_UTF32 oldInput (input); const bool oldOutOfData = outOfData; - input = entity; + input = static_cast (entity); outOfData = false; for (;;) @@ -579,7 +582,7 @@ void XmlDocument::readChildElements (XmlElement* parent) } else { - const juce_wchar* start = input; + const CharPointer_UTF32 start (input); int len = 0; for (;;) @@ -601,7 +604,7 @@ void XmlDocument::readChildElements (XmlElement* parent) ++len; } - textElementContent.append (start, len); + textElementContent.append (start.getAddress(), len); } } @@ -618,27 +621,27 @@ void XmlDocument::readEntity (String& result) // skip over the ampersand ++input; - if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("amp;"), 4) == 0) + if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("amp;"), 4) == 0) { input += 4; result += '&'; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("quot;"), 5) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("quot;"), 5) == 0) { input += 5; result += '"'; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("apos;"), 5) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("apos;"), 5) == 0) { input += 5; result += '\''; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("lt;"), 3) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("lt;"), 3) == 0) { input += 3; result += '<'; } - else if (CharacterFunctions::compareIgnoreCase (input, JUCE_T("gt;"), 3) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("gt;"), 3) == 0) { input += 3; result += '>'; @@ -698,20 +701,19 @@ void XmlDocument::readEntity (String& result) } else { - const juce_wchar* const entityNameStart = input; - const juce_wchar* const closingSemiColon = CharacterFunctions::find (input, JUCE_T(";")); + const CharPointer_UTF32 entityNameStart (input); + const int closingSemiColon = input.indexOf ((juce_wchar) ';'); - if (closingSemiColon == 0) + if (closingSemiColon < 0) { outOfData = true; result += '&'; } else { - input = closingSemiColon + 1; + input += closingSemiColon + 1; - result += expandExternalEntity (String (entityNameStart, - (int) (closingSemiColon - entityNameStart))); + result += expandExternalEntity (String (entityNameStart.getAddress(), closingSemiColon)); } } } diff --git a/src/text/juce_XmlDocument.h b/src/text/juce_XmlDocument.h index a868b5a6d2..915451edb2 100644 --- a/src/text/juce_XmlDocument.h +++ b/src/text/juce_XmlDocument.h @@ -153,7 +153,7 @@ public: //============================================================================== private: String originalText; - const juce_wchar* input; + CharPointer_UTF32 input; bool outOfData, errorOccurred; String lastError, dtdText; diff --git a/src/text/juce_XmlElement.cpp b/src/text/juce_XmlElement.cpp index 823598c096..122cca5a9e 100644 --- a/src/text/juce_XmlElement.cpp +++ b/src/text/juce_XmlElement.cpp @@ -779,7 +779,7 @@ bool XmlElement::isTextElement() const throw() return tagName.isEmpty(); } -static const juce_wchar* const juce_xmltextContentAttributeName = L"text"; +static const String juce_xmltextContentAttributeName ("text"); const String& XmlElement::getText() const throw() { diff --git a/src/utilities/juce_PropertiesFile.cpp b/src/utilities/juce_PropertiesFile.cpp index 269e0706f8..cf961b1729 100644 --- a/src/utilities/juce_PropertiesFile.cpp +++ b/src/utilities/juce_PropertiesFile.cpp @@ -295,21 +295,17 @@ const File PropertiesFile::getDefaultAppSettingsFile (const String& applicationN // mustn't have illegal characters in this name.. jassert (applicationName == File::createLegalFileName (applicationName)); -#if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS File dir (commonToAllUsers ? "/Library/Preferences" : "~/Library/Preferences"); if (folderName.isNotEmpty()) dir = dir.getChildFile (folderName); -#endif - -#ifdef JUCE_LINUX + #elif JUCE_LINUX || JUCE_ANDROID const File dir ((commonToAllUsers ? "/var/" : "~/") + (folderName.isNotEmpty() ? folderName : ("." + applicationName))); -#endif - -#if JUCE_WINDOWS + #elif JUCE_WINDOWS File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory : File::userApplicationDataDirectory)); @@ -318,8 +314,7 @@ const File PropertiesFile::getDefaultAppSettingsFile (const String& applicationN dir = dir.getChildFile (folderName.isNotEmpty() ? folderName : applicationName); - -#endif + #endif return dir.getChildFile (applicationName) .withFileExtension (fileNameSuffix);