From c10c810aee25f825e960f703f81763ec1ed9be79 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Mon, 31 Jan 2011 09:28:06 +0000 Subject: [PATCH] Minor fixes for mac strings, WAV format, DrawableButton hit tests, win32 clipboard. New class: CharPointer_ASCII. Changes to the callback methods for ValueTree::Listener, to provide more detailed information about the event. --- Builds/MacOSX/Juce.xcodeproj/project.pbxproj | 2 + Builds/VisualStudio2005/Juce.vcproj | 1 + Builds/VisualStudio2008/Juce.vcproj | 1 + Builds/VisualStudio2008_DLL/Juce.vcproj | 1 + Builds/VisualStudio2010/Juce.vcxproj | 1 + Builds/VisualStudio2010/Juce.vcxproj.filters | 3 + Builds/iOS/Juce.xcodeproj/project.pbxproj | 2 + Juce.jucer | 2 + .../jucer_GroupInformationComponent.cpp | 14 +- .../Project/jucer_GroupInformationComponent.h | 4 +- .../Source/Project/jucer_Project.cpp | 12 +- .../Source/Project/jucer_Project.h | 4 +- .../Project/jucer_ProjectTreeViewBase.cpp | 29 +- .../Project/jucer_ProjectTreeViewBase.h | 5 +- juce_amalgamated.cpp | 257 +++++--- juce_amalgamated.h | 563 +++++++++++++++++- .../juce_WavAudioFormat.cpp | 60 +- src/containers/juce_ValueTree.cpp | 75 ++- src/containers/juce_ValueTree.h | 38 +- src/core/juce_StandardHeader.h | 2 +- .../buttons/juce_DrawableButton.cpp | 1 + .../components/controls/juce_TextEditor.cpp | 4 + src/gui/components/controls/juce_TextEditor.h | 8 +- .../layout/juce_ComponentBuilder.cpp | 12 +- .../components/layout/juce_ComponentBuilder.h | 6 +- src/gui/components/menus/juce_PopupMenu.cpp | 13 +- .../graphics/drawables/juce_DrawableShape.cpp | 6 + src/juce_core_includes.h | 3 + src/native/mac/juce_mac_Files.mm | 2 +- src/native/windows/juce_win32_Misc.cpp | 2 +- src/text/juce_CharPointer_ASCII.h | 382 ++++++++++++ src/text/juce_CharPointer_UTF16.h | 37 ++ src/text/juce_CharPointer_UTF32.h | 21 +- src/text/juce_CharPointer_UTF8.h | 53 +- src/text/juce_CharacterFunctions.cpp | 1 + src/text/juce_String.cpp | 57 +- src/text/juce_String.h | 36 +- src/text/juce_XmlDocument.cpp | 24 +- src/text/juce_XmlDocument.h | 2 +- 39 files changed, 1541 insertions(+), 205 deletions(-) create mode 100644 src/text/juce_CharPointer_ASCII.h diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj index 3c9be7d63b..c4a5b64040 100644 --- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj @@ -1035,6 +1035,7 @@ 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; }; + 72F5ED2E8B945988C37EA9CF = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_ASCII.h; path = ../../src/text/juce_CharPointer_ASCII.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; }; @@ -1861,6 +1862,7 @@ 4007410FACA2F865FD8EF769, 663746215E9BA6C761172B85, C3FD9D93626F80A45F9B6DDE, + 72F5ED2E8B945988C37EA9CF, 8273A206FB309671284959DD, BF888BC540B64D5C61E46A34, 4A97C8D2FF6454DDD3AF4BE5, diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj index 750f9e7fa9..7d44fa08f8 100644 --- a/Builds/VisualStudio2005/Juce.vcproj +++ b/Builds/VisualStudio2005/Juce.vcproj @@ -959,6 +959,7 @@ + diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj index 2bdf1e7208..477a1d35e8 100644 --- a/Builds/VisualStudio2008/Juce.vcproj +++ b/Builds/VisualStudio2008/Juce.vcproj @@ -959,6 +959,7 @@ + diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj index 392199c9bf..7322381ce9 100644 --- a/Builds/VisualStudio2008_DLL/Juce.vcproj +++ b/Builds/VisualStudio2008_DLL/Juce.vcproj @@ -961,6 +961,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj index a25dcffc23..b3c4a813e7 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj +++ b/Builds/VisualStudio2010/Juce.vcxproj @@ -777,6 +777,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters index 66b57290c0..7409e030d8 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj.filters +++ b/Builds/VisualStudio2010/Juce.vcxproj.filters @@ -2265,6 +2265,9 @@ 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 473e91ec8b..3d1aefc0c0 100644 --- a/Builds/iOS/Juce.xcodeproj/project.pbxproj +++ b/Builds/iOS/Juce.xcodeproj/project.pbxproj @@ -1035,6 +1035,7 @@ 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; }; + 72F5ED2E8B945988C37EA9CF = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_CharPointer_ASCII.h; path = ../../src/text/juce_CharPointer_ASCII.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; }; @@ -1861,6 +1862,7 @@ 4007410FACA2F865FD8EF769, 663746215E9BA6C761172B85, C3FD9D93626F80A45F9B6DDE, + 72F5ED2E8B945988C37EA9CF, 8273A206FB309671284959DD, BF888BC540B64D5C61E46A34, 4A97C8D2FF6454DDD3AF4BE5, diff --git a/Juce.jucer b/Juce.jucer index a57e48a41f..ff3554e823 100644 --- a/Juce.jucer +++ b/Juce.jucer @@ -1477,6 +1477,8 @@ file="src/text/juce_CharPointer_UTF16.h"/> + ::max())); +} + +String::String (const char* const t, const size_t maxChars) + : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t), maxChars)) +{ + /* If you get an assertion here, then you're trying to create a string from 8-bit data + that contains values greater than 127. These can NOT be correctly converted to unicode + because there's no way for the String class to know what encoding was used to + create them. The source data could be UTF-8, ASCII or one of many local code-pages. + + To get around this problem, you must be more explicit when you pass an ambiguous 8-bit + string to the String class - so for example if your source data is actually UTF-8, + you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to + correctly convert the multi-byte characters to unicode. It's *highly* recommended that + you use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent these strings in a way that isn't dependent on + the compiler, source code editor and platform. + */ + jassert (CharPointer_ASCII::isValidString (t, (int) maxChars)); } String::String (const juce_wchar* const t) @@ -11589,6 +11622,11 @@ String::String (const juce_wchar* const t) { } +String::String (const juce_wchar* const t, const size_t maxChars) + : text (StringHolder::createFromCharPointer (CharPointer_UTF32 (t), maxChars)) +{ +} + String::String (const CharPointer_UTF8& t) : text (StringHolder::createFromCharPointer (t)) { @@ -11609,6 +11647,11 @@ String::String (const CharPointer_UTF32& t, const size_t maxChars) { } +String::String (const CharPointer_ASCII& t) + : text (StringHolder::createFromCharPointer (t)) +{ +} + #if JUCE_WINDOWS String::String (const wchar_t* const t) : text (StringHolder::createFromCharPointer (CharPointer_UTF16 (t))) @@ -11621,16 +11664,6 @@ String::String (const wchar_t* const t, size_t maxChars) } #endif -String::String (const char* const t, const size_t maxChars) - : text (StringHolder::createFromCharPointer (CharPointer_UTF8 (t), maxChars)) -{ -} - -String::String (const juce_wchar* const t, const size_t maxChars) - : text (StringHolder::createFromCharPointer (CharPointer_UTF32 (t), maxChars)) -{ -} - const String String::charToString (const juce_wchar character) { String result (Preallocation (1)); @@ -12004,7 +12037,7 @@ String& String::operator+= (const juce_wchar ch) } #if JUCE_WINDOWS -String& String::operator+= (wchar_t ch) +String& String::operator+= (const wchar_t ch) { return operator+= ((juce_wchar) ch); } @@ -14551,7 +14584,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle } } - input = static_cast (textToParse); + input = textToParse.getCharPointer(); lastError = String::empty; errorOccurred = false; outOfData = false; @@ -14662,7 +14695,7 @@ void XmlDocument::skipHeader() return; input += docTypeIndex + 9; - const CharPointer_UTF32 docType (input); + const String::CharPointerType docType (input); int n = 1; @@ -14751,7 +14784,7 @@ void XmlDocument::readQuotedString (String& result) else { --input; - const CharPointer_UTF32 start (input); + const String::CharPointerType start (input); for (;;) { @@ -14759,14 +14792,14 @@ void XmlDocument::readQuotedString (String& result) if (character == quote) { - result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); + result.appendCharPointer (start, (int) (input.getAddress() - start.getAddress())); ++input; return; } else if (character == '&') { - result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); + result.appendCharPointer (start, (int) (input.getAddress() - start.getAddress())); break; } else if (character == 0) @@ -14846,7 +14879,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (attNameLen > 0) { - const CharPointer_UTF32 attNameStart (input); + const String::CharPointerType attNameStart (input); input += attNameLen; skipNextWhiteSpace(); @@ -14889,7 +14922,7 @@ void XmlDocument::readChildElements (XmlElement* parent) for (;;) { - const CharPointer_UTF32 preWhitespaceInput (input); + const String::CharPointerType preWhitespaceInput (input); skipNextWhiteSpace(); if (outOfData) @@ -14920,7 +14953,7 @@ void XmlDocument::readChildElements (XmlElement* parent) && input[8] == '[') { input += 9; - const CharPointer_UTF32 inputStart (input); + const String::CharPointerType inputStart (input); int len = 0; @@ -14983,10 +15016,10 @@ void XmlDocument::readChildElements (XmlElement* parent) if (entity.startsWithChar ('<') && entity [1] != 0) { - const CharPointer_UTF32 oldInput (input); + const String::CharPointerType oldInput (input); const bool oldOutOfData = outOfData; - input = static_cast (entity); + input = entity.getCharPointer(); outOfData = false; for (;;) @@ -15009,7 +15042,7 @@ void XmlDocument::readChildElements (XmlElement* parent) } else { - const CharPointer_UTF32 start (input); + const String::CharPointerType start (input); int len = 0; for (;;) @@ -15128,7 +15161,7 @@ void XmlDocument::readEntity (String& result) } else { - const CharPointer_UTF32 entityNameStart (input); + const String::CharPointerType entityNameStart (input); const int closingSemiColon = input.indexOf ((juce_wchar) ';'); if (closingSemiColon < 0) @@ -17525,24 +17558,68 @@ void ValueTree::SharedObject::sendPropertyChangeMessage (const Identifier& prope } } -void ValueTree::SharedObject::sendChildChangeMessage (ValueTree& tree) +void ValueTree::SharedObject::sendChildAddedMessage (ValueTree& tree, ValueTree& child) { for (int i = valueTreesWithListeners.size(); --i >= 0;) { ValueTree* const v = valueTreesWithListeners[i]; if (v != 0) - v->listeners.call (&ValueTree::Listener::valueTreeChildrenChanged, tree); + v->listeners.call (&ValueTree::Listener::valueTreeChildAdded, tree, child); } } -void ValueTree::SharedObject::sendChildChangeMessage() +void ValueTree::SharedObject::sendChildAddedMessage (ValueTree child) { ValueTree tree (this); ValueTree::SharedObject* t = this; while (t != 0) { - t->sendChildChangeMessage (tree); + t->sendChildAddedMessage (tree, child); + t = t->parent; + } +} + +void ValueTree::SharedObject::sendChildRemovedMessage (ValueTree& tree, ValueTree& child) +{ + for (int i = valueTreesWithListeners.size(); --i >= 0;) + { + ValueTree* const v = valueTreesWithListeners[i]; + if (v != 0) + v->listeners.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child); + } +} + +void ValueTree::SharedObject::sendChildRemovedMessage (ValueTree child) +{ + ValueTree tree (this); + ValueTree::SharedObject* t = this; + + while (t != 0) + { + t->sendChildRemovedMessage (tree, child); + t = t->parent; + } +} + +void ValueTree::SharedObject::sendChildOrderChangedMessage (ValueTree& tree) +{ + for (int i = valueTreesWithListeners.size(); --i >= 0;) + { + ValueTree* const v = valueTreesWithListeners[i]; + if (v != 0) + v->listeners.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree); + } +} + +void ValueTree::SharedObject::sendChildOrderChangedMessage() +{ + ValueTree tree (this); + ValueTree::SharedObject* t = this; + + while (t != 0) + { + t->sendChildOrderChangedMessage (tree); t = t->parent; } } @@ -17708,7 +17785,7 @@ void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoMana { children.insert (index, child); child->parent = this; - sendChildChangeMessage(); + sendChildAddedMessage (ValueTree (child)); child->sendParentChangeMessage(); } else @@ -17738,7 +17815,7 @@ void ValueTree::SharedObject::removeChild (const int childIndex, UndoManager* co { children.remove (childIndex); child->parent = 0; - sendChildChangeMessage(); + sendChildRemovedMessage (ValueTree (child)); child->sendParentChangeMessage(); } else @@ -17765,7 +17842,7 @@ void ValueTree::SharedObject::moveChild (int currentIndex, int newIndex, UndoMan if (undoManager == 0) { children.move (currentIndex, newIndex); - sendChildChangeMessage(); + sendChildOrderChangedMessage(); } else { @@ -17784,16 +17861,19 @@ void ValueTree::SharedObject::reorderChildren (const ReferenceCountedArray = 0); + moveChild (oldIndex, i, undoManager); } } } @@ -17990,8 +18070,10 @@ public: sendChangeMessage (false); } - void valueTreeChildrenChanged (ValueTree&) {} - void valueTreeParentChanged (ValueTree&) {} + void valueTreeChildAdded (ValueTree&, ValueTree&) {} + void valueTreeChildRemoved (ValueTree&, ValueTree&) {} + void valueTreeChildOrderChanged (ValueTree&) {} + void valueTreeParentChanged (ValueTree&) {} private: ValueTree tree; @@ -23055,8 +23137,7 @@ public: : AudioFormatWriter (out, TRANS (wavFormatName), sampleRate_, numChannels_, bits), lengthInSamples (0), bytesWritten (0), - writeFailed (false), - isRF64 (false) + writeFailed (false) { using namespace WavFileHelpers; @@ -23123,7 +23204,7 @@ private: MemoryBlock tempBlock, bwavChunk, smplChunk; uint64 lengthInSamples, bytesWritten; int64 headerPosition; - bool writeFailed, isRF64; + bool writeFailed; static int getChannelMask (const int numChannels) throw() { @@ -23152,14 +23233,15 @@ private: const int bytesPerFrame = numChannels * bitsPerSample / 8; int64 audioDataSize = bytesPerFrame * lengthInSamples; - int64 riffChunkSize = 4 /* 'WAVE' */ + 8 + 40 /* WAVEFORMATEX */ + const bool isRF64 = (bytesWritten >= literal64bit (0x100000000)); + + int64 riffChunkSize = 4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */ + 8 + audioDataSize + (audioDataSize & 1) + (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0) + (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0) - + (8 + 28); // (JUNK chunk) + + (8 + 28); // (ds64 chunk) riffChunkSize += (riffChunkSize & 0x1); - isRF64 = (riffChunkSize > 0xffffffff); output->writeInt (chunkName (isRF64 ? "RF64" : "RIFF")); output->writeInt (isRF64 ? -1 : (int) riffChunkSize); @@ -23167,10 +23249,9 @@ private: if (! isRF64) { - // write Junk chunk output->writeInt (chunkName ("JUNK")); - output->writeInt (28); - output->writeRepeatedByte (0, 28); + output->writeInt (28 + 24); + output->writeRepeatedByte (0, 28 /* ds64 */ + 24 /* extra waveformatex */); } else { @@ -23183,29 +23264,44 @@ private: } output->writeInt (chunkName ("fmt ")); - output->writeInt (40); // WAVEFORMATEX chunk size - output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE + + if (isRF64) + { + output->writeInt (40); // chunk size + output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE + } + else + { + output->writeInt (16); // chunk size + output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/ + : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); + } + output->writeShort ((short) numChannels); output->writeInt ((int) sampleRate); output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec output->writeShort ((short) bytesPerFrame); // nBlockAlign output->writeShort ((short) bitsPerSample); // wBitsPerSample - output->writeShort (22); // cbSize (size of the extension) - output->writeShort ((short) bitsPerSample); // wValidBitsPerSample - output->writeInt (getChannelMask (numChannels)); - const ExtensibleWavSubFormat pcmFormat - = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + if (isRF64) + { + output->writeShort (22); // cbSize (size of the extension) + output->writeShort ((short) bitsPerSample); // wValidBitsPerSample + output->writeInt (getChannelMask (numChannels)); - const ExtensibleWavSubFormat IEEEFloatFormat - = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + const ExtensibleWavSubFormat pcmFormat + = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; + const ExtensibleWavSubFormat IEEEFloatFormat + = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - output->writeInt ((int) subFormat.data1); - output->writeShort ((short) subFormat.data2); - output->writeShort ((short) subFormat.data3); - output->write (subFormat.data4, sizeof (subFormat.data4)); + const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; + + output->writeInt ((int) subFormat.data1); + output->writeShort ((short) subFormat.data2); + output->writeShort ((short) subFormat.data3); + output->write (subFormat.data4, sizeof (subFormat.data4)); + } if (bwavChunk.getSize() > 0) { @@ -43750,6 +43846,7 @@ void DrawableButton::buttonStateChanged() if (currentImage != 0) { + currentImage->setInterceptsMouseClicks (false, false); addAndMakeVisible (currentImage); DrawableButton::resized(); } @@ -54423,6 +54520,11 @@ void TextEditor::coalesceSimilarSections() } } +void TextEditor::Listener::textEditorTextChanged (TextEditor&) {} +void TextEditor::Listener::textEditorReturnKeyPressed (TextEditor&) {} +void TextEditor::Listener::textEditorEscapeKeyPressed (TextEditor&) {} +void TextEditor::Listener::textEditorFocusLost (TextEditor&) {} + END_JUCE_NAMESPACE /*** End of inlined file: juce_TextEditor.cpp ***/ @@ -61303,7 +61405,17 @@ void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifi ComponentBuilderHelpers::updateComponent (*this, tree); } -void ComponentBuilder::valueTreeChildrenChanged (ValueTree& tree) +void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&) +{ + ComponentBuilderHelpers::updateComponent (*this, tree); +} + +void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&) +{ + ComponentBuilderHelpers::updateComponent (*this, tree); +} + +void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree) { ComponentBuilderHelpers::updateComponent (*this, tree); } @@ -68738,17 +68850,21 @@ class PopupMenu::ItemComponent : public Component { public: - ItemComponent (const PopupMenu::Item& itemInfo_, int standardItemHeight) + ItemComponent (const PopupMenu::Item& itemInfo_, int standardItemHeight, Component* const parent) : itemInfo (itemInfo_), isHighlighted (false) { if (itemInfo.customComp != 0) addAndMakeVisible (itemInfo.customComp); + parent->addAndMakeVisible (this); + int itemW = 80; int itemH = 16; getIdealSize (itemW, itemH, standardItemHeight); setSize (itemW, jlimit (2, 600, itemH)); + + addMouseListener (parent, false); } ~ItemComponent() @@ -68877,12 +68993,7 @@ public: setOpaque (getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque() || ! Desktop::canUseSemiTransparentWindows()); for (int i = 0; i < menu.items.size(); ++i) - { - PopupMenu::ItemComponent* const itemComp = new PopupMenu::ItemComponent (*menu.items.getUnchecked(i), standardItemHeight); - items.add (itemComp); - addAndMakeVisible (itemComp); - itemComp->addMouseListener (this, false); - } + items.add (new PopupMenu::ItemComponent (*menu.items.getUnchecked(i), standardItemHeight, this)); calculateWindowPos (target, alignToRectangle); setTopLeftPosition (windowPos.getX(), windowPos.getY()); @@ -86175,6 +86286,12 @@ const Rectangle DrawableShape::getDrawableBounds() const bool DrawableShape::hitTest (int x, int y) { + bool allowsClicksOnThisComponent, allowsClicksOnChildComponents; + getInterceptsMouseClicks (allowsClicksOnThisComponent, allowsClicksOnChildComponents); + + if (! allowsClicksOnThisComponent) + return false; + const float globalX = (float) (x - originRelativeToComponent.getX()); const float globalY = (float) (y - originRelativeToComponent.getY()); @@ -244209,7 +244326,7 @@ void SystemClipboard::copyTextToClipboard (const String& text) { if (EmptyClipboard() != 0) { - const int bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()); + const int bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()) + 4; if (bytesNeeded > 0) { @@ -263990,7 +264107,7 @@ namespace FileHelpers FSRef ref; LSItemInfoRecord info; - return FSPathMakeRefWithOptions ((const UInt8*) path.toUTF8(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr + return FSPathMakeRefWithOptions ((const UInt8*) path.toUTF8().getAddress(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr && LSCopyItemInfoForRef (&ref, kLSRequestBasicFlagsOnly, &info) == noErr && (info.flags & kLSItemInfoIsInvisible) != 0; #endif diff --git a/juce_amalgamated.h b/juce_amalgamated.h index e311ae6102..8fefffd118 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 21 +#define JUCE_BUILDNUMBER 23 /** Current Juce version number. @@ -2496,9 +2496,9 @@ public: if (byte >= 0) return byte; - juce_wchar n = byte; - juce_wchar mask = 0x7f; - juce_wchar bit = 0x40; + uint32 n = (uint32) (uint8) byte; + uint32 mask = 0x7f; + uint32 bit = 0x40; size_t numExtraValues = 0; while ((n & bit) != 0 && bit > 0x10) @@ -2521,7 +2521,7 @@ public: n |= (nextByte & 0x3f); } - return n; + return (juce_wchar) n; } /** Moves this pointer along to the next character in the string. */ @@ -2881,6 +2881,51 @@ public: /** Returns the first non-whitespace character in the string. */ CharPointer_UTF8 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 0x10ffff; + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + while (--maxBytesToRead >= 0 && *dataToTest != 0) + { + const char byte = *dataToTest; + + if (byte < 0) + { + uint32 n = (uint32) (uint8) byte; + uint32 mask = 0x7f; + uint32 bit = 0x40; + int numExtraValues = 0; + + while ((n & bit) != 0) + { + if (bit <= 0x10) + return false; + + mask >>= 1; + ++numExtraValues; + bit >>= 1; + } + + n &= mask; + + while (--numExtraValues >= 0) + { + const uint32 nextByte = (uint32) (uint8) *dataToTest++; + + if ((nextByte & 0xc0) != 0x80) + return false; + } + } + } + + return true; + } + /** These values are the byte-order-mark (BOM) values for a UTF-8 stream. */ enum { @@ -3268,6 +3313,43 @@ public: /** Returns the first non-whitespace character in the string. */ CharPointer_UTF16 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 0x10ffff + && (((unsigned int) character) < 0xd800 || ((unsigned int) character) > 0xdfff); + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + maxBytesToRead /= sizeof (CharType); + + while (--maxBytesToRead >= 0 && *dataToTest != 0) + { + const uint32 n = (uint32) (uint16) *dataToTest++; + + if (n >= 0xd800) + { + if (n > 0x10ffff) + return false; + + if (n <= 0xdfff) + { + if (n > 0xdc00) + return false; + + const uint32 nextChar = (uint32) (uint16) *dataToTest++; + + if (nextChar < 0xdc00 || nextChar > 0xdfff) + return false; + } + } + } + + return true; + } + /** These values are the byte-order-mark (BOM) values for a UTF-16 stream. */ enum { @@ -3377,7 +3459,7 @@ public: CharPointer_UTF32 operator++ (int) throw() { CharPointer_UTF32 temp (*this); - ++*this; + ++data; return temp; } @@ -3612,6 +3694,25 @@ public: /** Returns the first non-whitespace character in the string. */ CharPointer_UTF32 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 0x10ffff; + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + maxBytesToRead /= sizeof (CharType); + + while (--maxBytesToRead >= 0 && *dataToTest != 0) + if (! canRepresent (*dataToTest++)) + return false; + + return true; + } + + /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF32 atomicSwap (const CharPointer_UTF32& newValue) { return CharPointer_UTF32 (reinterpret_cast &> (data).exchange (newValue.data)); @@ -3624,6 +3725,364 @@ private: #endif // __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ /*** End of inlined file: juce_CharPointer_UTF32.h ***/ + +/*** Start of inlined file: juce_CharPointer_ASCII.h ***/ +#ifndef __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ +#define __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ + +/** + Wraps a pointer to a null-terminated ASCII character string, and provides + various methods to operate on the data. + + A valid ASCII string is assumed to not contain any characters above 127. + + @see CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32 +*/ +class CharPointer_ASCII +{ +public: + typedef char CharType; + + inline explicit CharPointer_ASCII (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_ASCII (const CharPointer_ASCII& other) throw() + : data (other.data) + { + } + + inline CharPointer_ASCII& operator= (const CharPointer_ASCII& other) throw() + { + data = other.data; + return *this; + } + + inline CharPointer_ASCII& operator= (const CharType* text) throw() + { + data = const_cast (text); + return *this; + } + + /** This is a pointer comparison, it doesn't compare the actual text. */ + inline bool operator== (const CharPointer_ASCII& other) const throw() + { + return data == other.data; + } + + /** This is a pointer comparison, it doesn't compare the actual text. */ + inline bool operator!= (const CharPointer_ASCII& other) const throw() + { + return data == other.data; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns the address that this pointer is pointing to. */ + inline operator const CharType*() 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_ASCII& operator++() throw() + { + ++data; + return *this; + } + + /** Moves this pointer to the previous character in the string. */ + inline CharPointer_ASCII& 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_ASCII operator++ (int) throw() + { + CharPointer_ASCII temp (*this); + ++data; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + inline void operator+= (const int numToSkip) throw() + { + data += numToSkip; + } + + inline void operator-= (const int numToSkip) throw() + { + data -= numToSkip; + } + + /** Returns the character at a given character index from the start of the string. */ + inline juce_wchar operator[] (const int characterIndex) const throw() + { + return (juce_wchar) (unsigned char) data [characterIndex]; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_ASCII operator+ (const int numToSkip) const throw() + { + return CharPointer_ASCII (data + numToSkip); + } + + /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ + CharPointer_ASCII operator- (const int numToSkip) const throw() + { + return CharPointer_ASCII (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++ = (char) charToWrite; + } + + inline void replaceChar (const juce_wchar newChar) throw() + { + *data = (char) newChar; + } + + /** Writes a null character to this string (leaving the pointer's position unchanged). */ + inline void writeNull() const throw() + { + *data = 0; + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + return (size_t) strlen (data); + } + + /** Returns the number of characters in this string, or the given value, whichever is lower. */ + size_t lengthUpTo (const size_t maxCharsToCount) const throw() + { + return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return 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 1; + } + + /** 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 text.length(); + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_ASCII findTerminatingNull() const throw() + { + return CharPointer_ASCII (data + length()); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void writeAll (const CharPointer& src) throw() + { + CharacterFunctions::copyAll (*this, src); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void writeAll (const CharPointer_ASCII& src) throw() + { + strcpy (data, src.data); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxDestBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int writeWithDestByteLimit (const CharPointer& src, const int maxDestBytes) throw() + { + return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); + } + + /** 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 writeWithCharLimit (const CharPointer& src, const int maxChars) throw() + { + CharacterFunctions::copyWithCharLimit (*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. */ + int compare (const CharPointer_ASCII& other) const throw() + { + return strcmp (data, other.data); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, const int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one, up to a specified number of characters. */ + int compareUpTo (const CharPointer_ASCII& other, const int maxChars) const throw() + { + return strncmp (data, other.data, (size_t) maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + int compareIgnoreCase (const CharPointer_ASCII& other) const + { + #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, const 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] == (char) 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); + } + + /** 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() { 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_ASCII findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 128; + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + while (--maxBytesToRead >= 0) + { + if (*dataToTest <= 0) + return *dataToTest == 0; + + ++dataToTest; + } + + return true; + } + +private: + CharType* data; +}; + +#endif // __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ +/*** End of inlined file: juce_CharPointer_ASCII.h ***/ + #if JUCE_MSVC #pragma warning (pop) #endif @@ -3644,7 +4103,6 @@ class JUCE_API String public: /** Creates an empty string. - @see empty */ String() throw(); @@ -3652,15 +4110,36 @@ public: /** Creates a copy of another string. */ String (const String& other) throw(); - /** Creates a string from a zero-terminated text string. - The string is assumed to be stored in the default system encoding. + /** Creates a string from a zero-terminated ascii text string. + + The string passed-in must not contain any characters with a value above 127, because + these can't be converted to unicode without knowing the original encoding that was + used to create the string. If you attempt to pass-in values above 127, you'll get an + assertion. + + To create strings with extended characters from UTF-8, you should explicitly call + String (CharPointer_UTF8 ("my utf8 string..")). It's *highly* recommended that you + use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent unicode strings in a way that isn't dependent + on the compiler, source code editor and platform. */ String (const char* text); - /** Creates a string from an string of characters. + /** Creates a string from a string of 8-bit ascii characters. - This will use up the the first maxChars characters of the string (or - less if the string is actually shorter) + The string passed-in must not contain any characters with a value above 127, because + these can't be converted to unicode without knowing the original encoding that was + used to create the string. If you attempt to pass-in values above 127, you'll get an + assertion. + + To create strings with extended characters from UTF-8, you should explicitly call + String (CharPointer_UTF8 ("my utf8 string..")). It's *highly* recommended that you + use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent unicode strings in a way that isn't dependent + on the compiler, source code editor and platform. + + This will use up the the first maxChars characters of the string (or less if the string + is actually shorter). */ String (const char* text, size_t maxChars); @@ -3686,6 +4165,9 @@ public: /** Creates a string from a UTF-32 character string */ String (const CharPointer_UTF32& text, size_t maxChars); + /** Creates a string from an ASCII character string */ + String (const CharPointer_ASCII& text); + #if JUCE_WINDOWS /** Creates a string from a UTF-16 character string */ String (const wchar_t* text); @@ -15559,16 +16041,34 @@ public: virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) = 0; - /** This method is called when a child sub-tree is added or removed. - - The tree parameter indicates the tree whose child was added or removed. + /** This method is called when a child sub-tree is added. Note that when you register a listener to a tree, it will receive this callback for - child changes in that tree, and also in any of its children, (recursively, at any depth). + child changes in both that tree and any of its children, (recursively, at any depth). If your tree has sub-trees but you only want to know about changes to the top level tree, - simply check the tree parameter in this callback to make sure it's the tree you're interested in. + just check the parentTree parameter to make sure it's the one that you're interested in. */ - virtual void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) = 0; + virtual void valueTreeChildAdded (ValueTree& parentTree, + ValueTree& childWhichHasBeenAdded) = 0; + + /** This method is called when a child sub-tree is removed. + + Note that when you register a listener to a tree, it will receive this callback for + child changes in both that tree and any of its children, (recursively, at any depth). + If your tree has sub-trees but you only want to know about changes to the top level tree, + just check the parentTree parameter to make sure it's the one that you're interested in. + */ + virtual void valueTreeChildRemoved (ValueTree& parentTree, + ValueTree& childWhichHasBeenRemoved) = 0; + + /** This method is called when a tree's children have been re-shuffled. + + Note that when you register a listener to a tree, it will receive this callback for + child changes in both that tree and any of its children, (recursively, at any depth). + If your tree has sub-trees but you only want to know about changes to the top level tree, + just check the parameter to make sure it's the tree that you're interested in. + */ + virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved) = 0; /** This method is called when a tree has been added or removed from a parent node. @@ -15660,8 +16160,12 @@ private: void sendPropertyChangeMessage (const Identifier& property); void sendPropertyChangeMessage (ValueTree& tree, const Identifier& property); - void sendChildChangeMessage(); - void sendChildChangeMessage (ValueTree& tree); + void sendChildAddedMessage (ValueTree& parent, ValueTree& child); + void sendChildAddedMessage (ValueTree child); + void sendChildRemovedMessage (ValueTree& parent, ValueTree& child); + void sendChildRemovedMessage (ValueTree child); + void sendChildOrderChangedMessage (ValueTree& parent); + void sendChildOrderChangedMessage(); void sendParentChangeMessage(); const var& getProperty (const Identifier& name) const; const var getProperty (const Identifier& name, const var& defaultReturnValue) const; @@ -20054,6 +20558,9 @@ private: #endif #ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ +#endif +#ifndef __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ + #endif #ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ @@ -20368,7 +20875,7 @@ public: private: String originalText; - CharPointer_UTF32 input; + String::CharPointerType input; bool outOfData, errorOccurred; String lastError, dtdText; @@ -40505,16 +41012,16 @@ public: virtual ~Listener() {} /** Called when the user changes the text in some way. */ - virtual void textEditorTextChanged (TextEditor& editor) = 0; + virtual void textEditorTextChanged (TextEditor& editor); /** Called when the user presses the return key. */ - virtual void textEditorReturnKeyPressed (TextEditor& editor) = 0; + virtual void textEditorReturnKeyPressed (TextEditor& editor); /** Called when the user presses the escape key. */ - virtual void textEditorEscapeKeyPressed (TextEditor& editor) = 0; + virtual void textEditorEscapeKeyPressed (TextEditor& editor); /** Called when the text editor loses focus. */ - virtual void textEditorFocusLost (TextEditor& editor) = 0; + virtual void textEditorFocusLost (TextEditor& editor); }; /** Registers a listener to be told when things happen to the text. @@ -47918,7 +48425,11 @@ public: /** @internal */ void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property); /** @internal */ - void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged); + void valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded); + /** @internal */ + void valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved); + /** @internal */ + void valueTreeChildOrderChanged (ValueTree& parentTree); /** @internal */ void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged); diff --git a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp index 12c077e151..64401dd6e7 100644 --- a/src/audio/audio_file_formats/juce_WavAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_WavAudioFormat.cpp @@ -498,8 +498,7 @@ public: : AudioFormatWriter (out, TRANS (wavFormatName), sampleRate_, numChannels_, bits), lengthInSamples (0), bytesWritten (0), - writeFailed (false), - isRF64 (false) + writeFailed (false) { using namespace WavFileHelpers; @@ -567,7 +566,7 @@ private: MemoryBlock tempBlock, bwavChunk, smplChunk; uint64 lengthInSamples, bytesWritten; int64 headerPosition; - bool writeFailed, isRF64; + bool writeFailed; static int getChannelMask (const int numChannels) throw() { @@ -596,14 +595,15 @@ private: const int bytesPerFrame = numChannels * bitsPerSample / 8; int64 audioDataSize = bytesPerFrame * lengthInSamples; - int64 riffChunkSize = 4 /* 'WAVE' */ + 8 + 40 /* WAVEFORMATEX */ + const bool isRF64 = (bytesWritten >= literal64bit (0x100000000)); + + int64 riffChunkSize = 4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */ + 8 + audioDataSize + (audioDataSize & 1) + (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0) + (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0) - + (8 + 28); // (JUNK chunk) + + (8 + 28); // (ds64 chunk) riffChunkSize += (riffChunkSize & 0x1); - isRF64 = (riffChunkSize > 0xffffffff); output->writeInt (chunkName (isRF64 ? "RF64" : "RIFF")); output->writeInt (isRF64 ? -1 : (int) riffChunkSize); @@ -611,10 +611,9 @@ private: if (! isRF64) { - // write Junk chunk output->writeInt (chunkName ("JUNK")); - output->writeInt (28); - output->writeRepeatedByte (0, 28); + output->writeInt (28 + 24); + output->writeRepeatedByte (0, 28 /* ds64 */ + 24 /* extra waveformatex */); } else { @@ -627,29 +626,44 @@ private: } output->writeInt (chunkName ("fmt ")); - output->writeInt (40); // WAVEFORMATEX chunk size - output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE + + if (isRF64) + { + output->writeInt (40); // chunk size + output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE + } + else + { + output->writeInt (16); // chunk size + output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/ + : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); + } + output->writeShort ((short) numChannels); output->writeInt ((int) sampleRate); output->writeInt ((int) (bytesPerFrame * sampleRate)); // nAvgBytesPerSec output->writeShort ((short) bytesPerFrame); // nBlockAlign output->writeShort ((short) bitsPerSample); // wBitsPerSample - output->writeShort (22); // cbSize (size of the extension) - output->writeShort ((short) bitsPerSample); // wValidBitsPerSample - output->writeInt (getChannelMask (numChannels)); - const ExtensibleWavSubFormat pcmFormat - = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + if (isRF64) + { + output->writeShort (22); // cbSize (size of the extension) + output->writeShort ((short) bitsPerSample); // wValidBitsPerSample + output->writeInt (getChannelMask (numChannels)); - const ExtensibleWavSubFormat IEEEFloatFormat - = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + const ExtensibleWavSubFormat pcmFormat + = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; + const ExtensibleWavSubFormat IEEEFloatFormat + = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - output->writeInt ((int) subFormat.data1); - output->writeShort ((short) subFormat.data2); - output->writeShort ((short) subFormat.data3); - output->write (subFormat.data4, sizeof (subFormat.data4)); + const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; + + output->writeInt ((int) subFormat.data1); + output->writeShort ((short) subFormat.data2); + output->writeShort ((short) subFormat.data3); + output->write (subFormat.data4, sizeof (subFormat.data4)); + } if (bwavChunk.getSize() > 0) { diff --git a/src/containers/juce_ValueTree.cpp b/src/containers/juce_ValueTree.cpp index 3213260231..c615f3047f 100644 --- a/src/containers/juce_ValueTree.cpp +++ b/src/containers/juce_ValueTree.cpp @@ -250,24 +250,68 @@ void ValueTree::SharedObject::sendPropertyChangeMessage (const Identifier& prope } } -void ValueTree::SharedObject::sendChildChangeMessage (ValueTree& tree) +void ValueTree::SharedObject::sendChildAddedMessage (ValueTree& tree, ValueTree& child) { for (int i = valueTreesWithListeners.size(); --i >= 0;) { ValueTree* const v = valueTreesWithListeners[i]; if (v != 0) - v->listeners.call (&ValueTree::Listener::valueTreeChildrenChanged, tree); + v->listeners.call (&ValueTree::Listener::valueTreeChildAdded, tree, child); } } -void ValueTree::SharedObject::sendChildChangeMessage() +void ValueTree::SharedObject::sendChildAddedMessage (ValueTree child) { ValueTree tree (this); ValueTree::SharedObject* t = this; while (t != 0) { - t->sendChildChangeMessage (tree); + t->sendChildAddedMessage (tree, child); + t = t->parent; + } +} + +void ValueTree::SharedObject::sendChildRemovedMessage (ValueTree& tree, ValueTree& child) +{ + for (int i = valueTreesWithListeners.size(); --i >= 0;) + { + ValueTree* const v = valueTreesWithListeners[i]; + if (v != 0) + v->listeners.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child); + } +} + +void ValueTree::SharedObject::sendChildRemovedMessage (ValueTree child) +{ + ValueTree tree (this); + ValueTree::SharedObject* t = this; + + while (t != 0) + { + t->sendChildRemovedMessage (tree, child); + t = t->parent; + } +} + +void ValueTree::SharedObject::sendChildOrderChangedMessage (ValueTree& tree) +{ + for (int i = valueTreesWithListeners.size(); --i >= 0;) + { + ValueTree* const v = valueTreesWithListeners[i]; + if (v != 0) + v->listeners.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree); + } +} + +void ValueTree::SharedObject::sendChildOrderChangedMessage() +{ + ValueTree tree (this); + ValueTree::SharedObject* t = this; + + while (t != 0) + { + t->sendChildOrderChangedMessage (tree); t = t->parent; } } @@ -434,7 +478,7 @@ void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoMana { children.insert (index, child); child->parent = this; - sendChildChangeMessage(); + sendChildAddedMessage (ValueTree (child)); child->sendParentChangeMessage(); } else @@ -464,7 +508,7 @@ void ValueTree::SharedObject::removeChild (const int childIndex, UndoManager* co { children.remove (childIndex); child->parent = 0; - sendChildChangeMessage(); + sendChildRemovedMessage (ValueTree (child)); child->sendParentChangeMessage(); } else @@ -491,7 +535,7 @@ void ValueTree::SharedObject::moveChild (int currentIndex, int newIndex, UndoMan if (undoManager == 0) { children.move (currentIndex, newIndex); - sendChildChangeMessage(); + sendChildOrderChangedMessage(); } else { @@ -510,16 +554,19 @@ void ValueTree::SharedObject::reorderChildren (const ReferenceCountedArray = 0); + moveChild (oldIndex, i, undoManager); } } } @@ -719,8 +766,10 @@ public: sendChangeMessage (false); } - void valueTreeChildrenChanged (ValueTree&) {} - void valueTreeParentChanged (ValueTree&) {} + void valueTreeChildAdded (ValueTree&, ValueTree&) {} + void valueTreeChildRemoved (ValueTree&, ValueTree&) {} + void valueTreeChildOrderChanged (ValueTree&) {} + void valueTreeParentChanged (ValueTree&) {} private: ValueTree tree; diff --git a/src/containers/juce_ValueTree.h b/src/containers/juce_ValueTree.h index b0a169c2b4..177342874e 100644 --- a/src/containers/juce_ValueTree.h +++ b/src/containers/juce_ValueTree.h @@ -368,16 +368,34 @@ public: virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) = 0; - /** This method is called when a child sub-tree is added or removed. - - The tree parameter indicates the tree whose child was added or removed. + /** This method is called when a child sub-tree is added. Note that when you register a listener to a tree, it will receive this callback for - child changes in that tree, and also in any of its children, (recursively, at any depth). + child changes in both that tree and any of its children, (recursively, at any depth). If your tree has sub-trees but you only want to know about changes to the top level tree, - simply check the tree parameter in this callback to make sure it's the tree you're interested in. + just check the parentTree parameter to make sure it's the one that you're interested in. */ - virtual void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) = 0; + virtual void valueTreeChildAdded (ValueTree& parentTree, + ValueTree& childWhichHasBeenAdded) = 0; + + /** This method is called when a child sub-tree is removed. + + Note that when you register a listener to a tree, it will receive this callback for + child changes in both that tree and any of its children, (recursively, at any depth). + If your tree has sub-trees but you only want to know about changes to the top level tree, + just check the parentTree parameter to make sure it's the one that you're interested in. + */ + virtual void valueTreeChildRemoved (ValueTree& parentTree, + ValueTree& childWhichHasBeenRemoved) = 0; + + /** This method is called when a tree's children have been re-shuffled. + + Note that when you register a listener to a tree, it will receive this callback for + child changes in both that tree and any of its children, (recursively, at any depth). + If your tree has sub-trees but you only want to know about changes to the top level tree, + just check the parameter to make sure it's the tree that you're interested in. + */ + virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved) = 0; /** This method is called when a tree has been added or removed from a parent node. @@ -470,8 +488,12 @@ private: void sendPropertyChangeMessage (const Identifier& property); void sendPropertyChangeMessage (ValueTree& tree, const Identifier& property); - void sendChildChangeMessage(); - void sendChildChangeMessage (ValueTree& tree); + void sendChildAddedMessage (ValueTree& parent, ValueTree& child); + void sendChildAddedMessage (ValueTree child); + void sendChildRemovedMessage (ValueTree& parent, ValueTree& child); + void sendChildRemovedMessage (ValueTree child); + void sendChildOrderChangedMessage (ValueTree& parent); + void sendChildOrderChangedMessage(); void sendParentChangeMessage(); const var& getProperty (const Identifier& name) const; const var getProperty (const Identifier& name, const var& defaultReturnValue) const; diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index c59a4a102b..474e199530 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 21 +#define JUCE_BUILDNUMBER 23 /** Current Juce version number. diff --git a/src/gui/components/buttons/juce_DrawableButton.cpp b/src/gui/components/buttons/juce_DrawableButton.cpp index ae7bec5fcd..38aca24e22 100644 --- a/src/gui/components/buttons/juce_DrawableButton.cpp +++ b/src/gui/components/buttons/juce_DrawableButton.cpp @@ -180,6 +180,7 @@ void DrawableButton::buttonStateChanged() if (currentImage != 0) { + currentImage->setInterceptsMouseClicks (false, false); addAndMakeVisible (currentImage); DrawableButton::resized(); } diff --git a/src/gui/components/controls/juce_TextEditor.cpp b/src/gui/components/controls/juce_TextEditor.cpp index 95a9fe78e8..848f5f0762 100644 --- a/src/gui/components/controls/juce_TextEditor.cpp +++ b/src/gui/components/controls/juce_TextEditor.cpp @@ -2621,5 +2621,9 @@ void TextEditor::coalesceSimilarSections() } } +void TextEditor::Listener::textEditorTextChanged (TextEditor&) {} +void TextEditor::Listener::textEditorReturnKeyPressed (TextEditor&) {} +void TextEditor::Listener::textEditorEscapeKeyPressed (TextEditor&) {} +void TextEditor::Listener::textEditorFocusLost (TextEditor&) {} END_JUCE_NAMESPACE diff --git a/src/gui/components/controls/juce_TextEditor.h b/src/gui/components/controls/juce_TextEditor.h index 69444b6f1d..77fb6f3e0b 100644 --- a/src/gui/components/controls/juce_TextEditor.h +++ b/src/gui/components/controls/juce_TextEditor.h @@ -313,16 +313,16 @@ public: virtual ~Listener() {} /** Called when the user changes the text in some way. */ - virtual void textEditorTextChanged (TextEditor& editor) = 0; + virtual void textEditorTextChanged (TextEditor& editor); /** Called when the user presses the return key. */ - virtual void textEditorReturnKeyPressed (TextEditor& editor) = 0; + virtual void textEditorReturnKeyPressed (TextEditor& editor); /** Called when the user presses the escape key. */ - virtual void textEditorEscapeKeyPressed (TextEditor& editor) = 0; + virtual void textEditorEscapeKeyPressed (TextEditor& editor); /** Called when the text editor loses focus. */ - virtual void textEditorFocusLost (TextEditor& editor) = 0; + virtual void textEditorFocusLost (TextEditor& editor); }; /** Registers a listener to be told when things happen to the text. diff --git a/src/gui/components/layout/juce_ComponentBuilder.cpp b/src/gui/components/layout/juce_ComponentBuilder.cpp index 6dfc19c493..e616d22f22 100644 --- a/src/gui/components/layout/juce_ComponentBuilder.cpp +++ b/src/gui/components/layout/juce_ComponentBuilder.cpp @@ -201,7 +201,17 @@ void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifi ComponentBuilderHelpers::updateComponent (*this, tree); } -void ComponentBuilder::valueTreeChildrenChanged (ValueTree& tree) +void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&) +{ + ComponentBuilderHelpers::updateComponent (*this, tree); +} + +void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&) +{ + ComponentBuilderHelpers::updateComponent (*this, tree); +} + +void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree) { ComponentBuilderHelpers::updateComponent (*this, tree); } diff --git a/src/gui/components/layout/juce_ComponentBuilder.h b/src/gui/components/layout/juce_ComponentBuilder.h index 17acbf34f9..3d56c8603c 100644 --- a/src/gui/components/layout/juce_ComponentBuilder.h +++ b/src/gui/components/layout/juce_ComponentBuilder.h @@ -229,7 +229,11 @@ public: /** @internal */ void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property); /** @internal */ - void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged); + void valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded); + /** @internal */ + void valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved); + /** @internal */ + void valueTreeChildOrderChanged (ValueTree& parentTree); /** @internal */ void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged); diff --git a/src/gui/components/menus/juce_PopupMenu.cpp b/src/gui/components/menus/juce_PopupMenu.cpp index fb37eb91cd..f91ffa803a 100644 --- a/src/gui/components/menus/juce_PopupMenu.cpp +++ b/src/gui/components/menus/juce_PopupMenu.cpp @@ -135,17 +135,21 @@ class PopupMenu::ItemComponent : public Component { public: //============================================================================== - ItemComponent (const PopupMenu::Item& itemInfo_, int standardItemHeight) + ItemComponent (const PopupMenu::Item& itemInfo_, int standardItemHeight, Component* const parent) : itemInfo (itemInfo_), isHighlighted (false) { if (itemInfo.customComp != 0) addAndMakeVisible (itemInfo.customComp); + parent->addAndMakeVisible (this); + int itemW = 80; int itemH = 16; getIdealSize (itemW, itemH, standardItemHeight); setSize (itemW, jlimit (2, 600, itemH)); + + addMouseListener (parent, false); } ~ItemComponent() @@ -277,12 +281,7 @@ public: setOpaque (getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque() || ! Desktop::canUseSemiTransparentWindows()); for (int i = 0; i < menu.items.size(); ++i) - { - PopupMenu::ItemComponent* const itemComp = new PopupMenu::ItemComponent (*menu.items.getUnchecked(i), standardItemHeight); - items.add (itemComp); - addAndMakeVisible (itemComp); - itemComp->addMouseListener (this, false); - } + items.add (new PopupMenu::ItemComponent (*menu.items.getUnchecked(i), standardItemHeight, this)); calculateWindowPos (target, alignToRectangle); setTopLeftPosition (windowPos.getX(), windowPos.getY()); diff --git a/src/gui/graphics/drawables/juce_DrawableShape.cpp b/src/gui/graphics/drawables/juce_DrawableShape.cpp index 4b0a479a72..32ae271f12 100644 --- a/src/gui/graphics/drawables/juce_DrawableShape.cpp +++ b/src/gui/graphics/drawables/juce_DrawableShape.cpp @@ -198,6 +198,12 @@ const Rectangle DrawableShape::getDrawableBounds() const bool DrawableShape::hitTest (int x, int y) { + bool allowsClicksOnThisComponent, allowsClicksOnChildComponents; + getInterceptsMouseClicks (allowsClicksOnThisComponent, allowsClicksOnChildComponents); + + if (! allowsClicksOnThisComponent) + return false; + const float globalX = (float) (x - originRelativeToComponent.getX()); const float globalY = (float) (y - originRelativeToComponent.getY()); diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h index 8b058b01a5..827013e991 100644 --- a/src/juce_core_includes.h +++ b/src/juce_core_includes.h @@ -233,6 +233,9 @@ #ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ #include "text/juce_CharacterFunctions.h" #endif +#ifndef __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ + #include "text/juce_CharPointer_ASCII.h" +#endif #ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ #include "text/juce_CharPointer_UTF16.h" #endif diff --git a/src/native/mac/juce_mac_Files.mm b/src/native/mac/juce_mac_Files.mm index 2ae784b6e6..e6df621fc6 100644 --- a/src/native/mac/juce_mac_Files.mm +++ b/src/native/mac/juce_mac_Files.mm @@ -92,7 +92,7 @@ namespace FileHelpers FSRef ref; LSItemInfoRecord info; - return FSPathMakeRefWithOptions ((const UInt8*) path.toUTF8(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr + return FSPathMakeRefWithOptions ((const UInt8*) path.toUTF8().getAddress(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr && LSCopyItemInfoForRef (&ref, kLSRequestBasicFlagsOnly, &info) == noErr && (info.flags & kLSItemInfoIsInvisible) != 0; #endif diff --git a/src/native/windows/juce_win32_Misc.cpp b/src/native/windows/juce_win32_Misc.cpp index 90966329e6..ad1aefbb4f 100644 --- a/src/native/windows/juce_win32_Misc.cpp +++ b/src/native/windows/juce_win32_Misc.cpp @@ -35,7 +35,7 @@ void SystemClipboard::copyTextToClipboard (const String& text) { if (EmptyClipboard() != 0) { - const int bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()); + const int bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer()) + 4; if (bytesNeeded > 0) { diff --git a/src/text/juce_CharPointer_ASCII.h b/src/text/juce_CharPointer_ASCII.h new file mode 100644 index 0000000000..569bd11c45 --- /dev/null +++ b/src/text/juce_CharPointer_ASCII.h @@ -0,0 +1,382 @@ +/* + ============================================================================== + + 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_ASCII_JUCEHEADER__ +#define __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ + + +//============================================================================== +/** + Wraps a pointer to a null-terminated ASCII character string, and provides + various methods to operate on the data. + + A valid ASCII string is assumed to not contain any characters above 127. + + @see CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32 +*/ +class CharPointer_ASCII +{ +public: + typedef char CharType; + + inline explicit CharPointer_ASCII (const CharType* const rawPointer) throw() + : data (const_cast (rawPointer)) + { + } + + inline CharPointer_ASCII (const CharPointer_ASCII& other) throw() + : data (other.data) + { + } + + inline CharPointer_ASCII& operator= (const CharPointer_ASCII& other) throw() + { + data = other.data; + return *this; + } + + inline CharPointer_ASCII& operator= (const CharType* text) throw() + { + data = const_cast (text); + return *this; + } + + /** This is a pointer comparison, it doesn't compare the actual text. */ + inline bool operator== (const CharPointer_ASCII& other) const throw() + { + return data == other.data; + } + + /** This is a pointer comparison, it doesn't compare the actual text. */ + inline bool operator!= (const CharPointer_ASCII& other) const throw() + { + return data == other.data; + } + + /** Returns the address that this pointer is pointing to. */ + inline CharType* getAddress() const throw() { return data; } + + /** Returns the address that this pointer is pointing to. */ + inline operator const CharType*() 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_ASCII& operator++() throw() + { + ++data; + return *this; + } + + /** Moves this pointer to the previous character in the string. */ + inline CharPointer_ASCII& 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_ASCII operator++ (int) throw() + { + CharPointer_ASCII temp (*this); + ++data; + return temp; + } + + /** Moves this pointer forwards by the specified number of characters. */ + inline void operator+= (const int numToSkip) throw() + { + data += numToSkip; + } + + inline void operator-= (const int numToSkip) throw() + { + data -= numToSkip; + } + + /** Returns the character at a given character index from the start of the string. */ + inline juce_wchar operator[] (const int characterIndex) const throw() + { + return (juce_wchar) (unsigned char) data [characterIndex]; + } + + /** Returns a pointer which is moved forwards from this one by the specified number of characters. */ + CharPointer_ASCII operator+ (const int numToSkip) const throw() + { + return CharPointer_ASCII (data + numToSkip); + } + + /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ + CharPointer_ASCII operator- (const int numToSkip) const throw() + { + return CharPointer_ASCII (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++ = (char) charToWrite; + } + + inline void replaceChar (const juce_wchar newChar) throw() + { + *data = (char) newChar; + } + + /** Writes a null character to this string (leaving the pointer's position unchanged). */ + inline void writeNull() const throw() + { + *data = 0; + } + + /** Returns the number of characters in this string. */ + size_t length() const throw() + { + return (size_t) strlen (data); + } + + /** Returns the number of characters in this string, or the given value, whichever is lower. */ + size_t lengthUpTo (const size_t maxCharsToCount) const throw() + { + return CharacterFunctions::lengthUpTo (*this, maxCharsToCount); + } + + /** Returns the number of bytes that are used to represent this string. + This includes the terminating null character. + */ + size_t sizeInBytes() const throw() + { + return 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 1; + } + + /** 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 text.length(); + } + + /** Returns a pointer to the null character that terminates this string. */ + CharPointer_ASCII findTerminatingNull() const throw() + { + return CharPointer_ASCII (data + length()); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + template + void writeAll (const CharPointer& src) throw() + { + CharacterFunctions::copyAll (*this, src); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. */ + void writeAll (const CharPointer_ASCII& src) throw() + { + strcpy (data, src.data); + } + + /** Copies a source string to this pointer, advancing this pointer as it goes. + The maxDestBytes parameter specifies the maximum number of bytes that can be written + to the destination buffer before stopping. + */ + template + int writeWithDestByteLimit (const CharPointer& src, const int maxDestBytes) throw() + { + return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); + } + + /** 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 writeWithCharLimit (const CharPointer& src, const int maxChars) throw() + { + CharacterFunctions::copyWithCharLimit (*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. */ + int compare (const CharPointer_ASCII& other) const throw() + { + return strcmp (data, other.data); + } + + /** Compares this string with another one, up to a specified number of characters. */ + template + int compareUpTo (const CharPointer& other, const int maxChars) const throw() + { + return CharacterFunctions::compareUpTo (*this, other, maxChars); + } + + /** Compares this string with another one, up to a specified number of characters. */ + int compareUpTo (const CharPointer_ASCII& other, const int maxChars) const throw() + { + return strncmp (data, other.data, (size_t) maxChars); + } + + /** Compares this string with another one. */ + template + int compareIgnoreCase (const CharPointer& other) const + { + return CharacterFunctions::compareIgnoreCase (*this, other); + } + + int compareIgnoreCase (const CharPointer_ASCII& other) const + { + #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, const 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] == (char) 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); + } + + /** 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() { 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_ASCII findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 128; + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + while (--maxBytesToRead >= 0) + { + if (*dataToTest <= 0) + return *dataToTest == 0; + + ++dataToTest; + } + + return true; + } + +private: + CharType* data; +}; + + +#endif // __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ diff --git a/src/text/juce_CharPointer_UTF16.h b/src/text/juce_CharPointer_UTF16.h index db7f2c3865..fbba22c72f 100644 --- a/src/text/juce_CharPointer_UTF16.h +++ b/src/text/juce_CharPointer_UTF16.h @@ -395,6 +395,43 @@ public: /** Returns the first non-whitespace character in the string. */ CharPointer_UTF16 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 0x10ffff + && (((unsigned int) character) < 0xd800 || ((unsigned int) character) > 0xdfff); + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + maxBytesToRead /= sizeof (CharType); + + while (--maxBytesToRead >= 0 && *dataToTest != 0) + { + const uint32 n = (uint32) (uint16) *dataToTest++; + + if (n >= 0xd800) + { + if (n > 0x10ffff) + return false; + + if (n <= 0xdfff) + { + if (n > 0xdc00) + return false; + + const uint32 nextChar = (uint32) (uint16) *dataToTest++; + + if (nextChar < 0xdc00 || nextChar > 0xdfff) + return false; + } + } + } + + return true; + } + /** These values are the byte-order-mark (BOM) values for a UTF-16 stream. */ enum { diff --git a/src/text/juce_CharPointer_UTF32.h b/src/text/juce_CharPointer_UTF32.h index 2e2926286c..a6872e89a7 100644 --- a/src/text/juce_CharPointer_UTF32.h +++ b/src/text/juce_CharPointer_UTF32.h @@ -106,7 +106,7 @@ public: CharPointer_UTF32 operator++ (int) throw() { CharPointer_UTF32 temp (*this); - ++*this; + ++data; return temp; } @@ -341,6 +341,25 @@ public: /** Returns the first non-whitespace character in the string. */ CharPointer_UTF32 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 0x10ffff; + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + maxBytesToRead /= sizeof (CharType); + + while (--maxBytesToRead >= 0 && *dataToTest != 0) + if (! canRepresent (*dataToTest++)) + return false; + + return true; + } + + /** Atomically swaps this pointer for a new value, returning the previous value. */ CharPointer_UTF32 atomicSwap (const CharPointer_UTF32& newValue) { return CharPointer_UTF32 (reinterpret_cast &> (data).exchange (newValue.data)); diff --git a/src/text/juce_CharPointer_UTF8.h b/src/text/juce_CharPointer_UTF8.h index 33f1bec346..53b84854e1 100644 --- a/src/text/juce_CharPointer_UTF8.h +++ b/src/text/juce_CharPointer_UTF8.h @@ -88,9 +88,9 @@ public: if (byte >= 0) return byte; - juce_wchar n = byte; - juce_wchar mask = 0x7f; - juce_wchar bit = 0x40; + uint32 n = (uint32) (uint8) byte; + uint32 mask = 0x7f; + uint32 bit = 0x40; size_t numExtraValues = 0; while ((n & bit) != 0 && bit > 0x10) @@ -113,7 +113,7 @@ public: n |= (nextByte & 0x3f); } - return n; + return (juce_wchar) n; } /** Moves this pointer along to the next character in the string. */ @@ -473,6 +473,51 @@ public: /** Returns the first non-whitespace character in the string. */ CharPointer_UTF8 findEndOfWhitespace() const throw() { return CharacterFunctions::findEndOfWhitespace (*this); } + /** Returns true if the given unicode character can be represented in this encoding. */ + static bool canRepresent (juce_wchar character) throw() + { + return ((unsigned int) character) < (unsigned int) 0x10ffff; + } + + /** Returns true if this data contains a valid string in this encoding. */ + static bool isValidString (const CharType* dataToTest, int maxBytesToRead) + { + while (--maxBytesToRead >= 0 && *dataToTest != 0) + { + const char byte = *dataToTest; + + if (byte < 0) + { + uint32 n = (uint32) (uint8) byte; + uint32 mask = 0x7f; + uint32 bit = 0x40; + int numExtraValues = 0; + + while ((n & bit) != 0) + { + if (bit <= 0x10) + return false; + + mask >>= 1; + ++numExtraValues; + bit >>= 1; + } + + n &= mask; + + while (--numExtraValues >= 0) + { + const uint32 nextByte = (uint32) (uint8) *dataToTest++; + + if ((nextByte & 0xc0) != 0x80) + return false; + } + } + } + + return true; + } + /** These values are the byte-order-mark (BOM) values for a UTF-8 stream. */ enum { diff --git a/src/text/juce_CharacterFunctions.cpp b/src/text/juce_CharacterFunctions.cpp index a89fe8600d..cdab05eea4 100644 --- a/src/text/juce_CharacterFunctions.cpp +++ b/src/text/juce_CharacterFunctions.cpp @@ -40,6 +40,7 @@ BEGIN_JUCE_NAMESPACE #include "juce_String.h" +#include "../memory/juce_HeapBlock.h" //============================================================================== juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) throw() diff --git a/src/text/juce_String.cpp b/src/text/juce_String.cpp index 1cf151e4fa..54c91f21c6 100644 --- a/src/text/juce_String.cpp +++ b/src/text/juce_String.cpp @@ -250,8 +250,41 @@ String::String (const String& stringToCopy, const size_t charsToAllocate) } String::String (const char* const t) - : text (StringHolder::createFromCharPointer (CharPointer_UTF8 (t))) + : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t))) { + /* If you get an assertion here, then you're trying to create a string from 8-bit data + that contains values greater than 127. These can NOT be correctly converted to unicode + because there's no way for the String class to know what encoding was used to + create them. The source data could be UTF-8, ASCII or one of many local code-pages. + + To get around this problem, you must be more explicit when you pass an ambiguous 8-bit + string to the String class - so for example if your source data is actually UTF-8, + you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to + correctly convert the multi-byte characters to unicode. It's *highly* recommended that + you use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent these strings in a way that isn't dependent on + the compiler, source code editor and platform. + */ + jassert (CharPointer_ASCII::isValidString (t, std::numeric_limits::max())); +} + +String::String (const char* const t, const size_t maxChars) + : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t), maxChars)) +{ + /* If you get an assertion here, then you're trying to create a string from 8-bit data + that contains values greater than 127. These can NOT be correctly converted to unicode + because there's no way for the String class to know what encoding was used to + create them. The source data could be UTF-8, ASCII or one of many local code-pages. + + To get around this problem, you must be more explicit when you pass an ambiguous 8-bit + string to the String class - so for example if your source data is actually UTF-8, + you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to + correctly convert the multi-byte characters to unicode. It's *highly* recommended that + you use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent these strings in a way that isn't dependent on + the compiler, source code editor and platform. + */ + jassert (CharPointer_ASCII::isValidString (t, (int) maxChars)); } String::String (const juce_wchar* const t) @@ -259,6 +292,11 @@ String::String (const juce_wchar* const t) { } +String::String (const juce_wchar* const t, const size_t maxChars) + : text (StringHolder::createFromCharPointer (CharPointer_UTF32 (t), maxChars)) +{ +} + String::String (const CharPointer_UTF8& t) : text (StringHolder::createFromCharPointer (t)) { @@ -279,6 +317,11 @@ String::String (const CharPointer_UTF32& t, const size_t maxChars) { } +String::String (const CharPointer_ASCII& t) + : text (StringHolder::createFromCharPointer (t)) +{ +} + #if JUCE_WINDOWS String::String (const wchar_t* const t) : text (StringHolder::createFromCharPointer (CharPointer_UTF16 (t))) @@ -291,16 +334,6 @@ String::String (const wchar_t* const t, size_t maxChars) } #endif -String::String (const char* const t, const size_t maxChars) - : text (StringHolder::createFromCharPointer (CharPointer_UTF8 (t), maxChars)) -{ -} - -String::String (const juce_wchar* const t, const size_t maxChars) - : text (StringHolder::createFromCharPointer (CharPointer_UTF32 (t), maxChars)) -{ -} - const String String::charToString (const juce_wchar character) { String result (Preallocation (1)); @@ -679,7 +712,7 @@ String& String::operator+= (const juce_wchar ch) } #if JUCE_WINDOWS -String& String::operator+= (wchar_t ch) +String& String::operator+= (const wchar_t ch) { return operator+= ((juce_wchar) ch); } diff --git a/src/text/juce_String.h b/src/text/juce_String.h index 1cdf9dc477..1cef9b0822 100644 --- a/src/text/juce_String.h +++ b/src/text/juce_String.h @@ -37,6 +37,7 @@ #include "juce_CharPointer_UTF8.h" #include "juce_CharPointer_UTF16.h" #include "juce_CharPointer_UTF32.h" +#include "juce_CharPointer_ASCII.h" #if JUCE_MSVC #pragma warning (pop) @@ -60,7 +61,6 @@ class JUCE_API String public: //============================================================================== /** Creates an empty string. - @see empty */ String() throw(); @@ -68,15 +68,36 @@ public: /** Creates a copy of another string. */ String (const String& other) throw(); - /** Creates a string from a zero-terminated text string. - The string is assumed to be stored in the default system encoding. + /** Creates a string from a zero-terminated ascii text string. + + The string passed-in must not contain any characters with a value above 127, because + these can't be converted to unicode without knowing the original encoding that was + used to create the string. If you attempt to pass-in values above 127, you'll get an + assertion. + + To create strings with extended characters from UTF-8, you should explicitly call + String (CharPointer_UTF8 ("my utf8 string..")). It's *highly* recommended that you + use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent unicode strings in a way that isn't dependent + on the compiler, source code editor and platform. */ String (const char* text); - /** Creates a string from an string of characters. + /** Creates a string from a string of 8-bit ascii characters. - This will use up the the first maxChars characters of the string (or - less if the string is actually shorter) + The string passed-in must not contain any characters with a value above 127, because + these can't be converted to unicode without knowing the original encoding that was + used to create the string. If you attempt to pass-in values above 127, you'll get an + assertion. + + To create strings with extended characters from UTF-8, you should explicitly call + String (CharPointer_UTF8 ("my utf8 string..")). It's *highly* recommended that you + use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent unicode strings in a way that isn't dependent + on the compiler, source code editor and platform. + + This will use up the the first maxChars characters of the string (or less if the string + is actually shorter). */ String (const char* text, size_t maxChars); @@ -102,6 +123,9 @@ public: /** Creates a string from a UTF-32 character string */ String (const CharPointer_UTF32& text, size_t maxChars); + /** Creates a string from an ASCII character string */ + String (const CharPointer_ASCII& text); + #if JUCE_WINDOWS /** Creates a string from a UTF-16 character string */ String (const wchar_t* text); diff --git a/src/text/juce_XmlDocument.cpp b/src/text/juce_XmlDocument.cpp index f1e8868a9d..de7dbcfd55 100644 --- a/src/text/juce_XmlDocument.cpp +++ b/src/text/juce_XmlDocument.cpp @@ -124,7 +124,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle } } - input = static_cast (textToParse); + input = textToParse.getCharPointer(); lastError = String::empty; errorOccurred = false; outOfData = false; @@ -235,7 +235,7 @@ void XmlDocument::skipHeader() return; input += docTypeIndex + 9; - const CharPointer_UTF32 docType (input); + const String::CharPointerType docType (input); int n = 1; @@ -324,7 +324,7 @@ void XmlDocument::readQuotedString (String& result) else { --input; - const CharPointer_UTF32 start (input); + const String::CharPointerType start (input); for (;;) { @@ -332,14 +332,14 @@ void XmlDocument::readQuotedString (String& result) if (character == quote) { - result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); + result.appendCharPointer (start, (int) (input.getAddress() - start.getAddress())); ++input; return; } else if (character == '&') { - result.append (start.getAddress(), (int) (input.getAddress() - start.getAddress())); + result.appendCharPointer (start, (int) (input.getAddress() - start.getAddress())); break; } else if (character == 0) @@ -419,7 +419,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (attNameLen > 0) { - const CharPointer_UTF32 attNameStart (input); + const String::CharPointerType attNameStart (input); input += attNameLen; skipNextWhiteSpace(); @@ -462,7 +462,7 @@ void XmlDocument::readChildElements (XmlElement* parent) for (;;) { - const CharPointer_UTF32 preWhitespaceInput (input); + const String::CharPointerType preWhitespaceInput (input); skipNextWhiteSpace(); if (outOfData) @@ -493,7 +493,7 @@ void XmlDocument::readChildElements (XmlElement* parent) && input[8] == '[') { input += 9; - const CharPointer_UTF32 inputStart (input); + const String::CharPointerType inputStart (input); int len = 0; @@ -556,10 +556,10 @@ void XmlDocument::readChildElements (XmlElement* parent) if (entity.startsWithChar ('<') && entity [1] != 0) { - const CharPointer_UTF32 oldInput (input); + const String::CharPointerType oldInput (input); const bool oldOutOfData = outOfData; - input = static_cast (entity); + input = entity.getCharPointer(); outOfData = false; for (;;) @@ -582,7 +582,7 @@ void XmlDocument::readChildElements (XmlElement* parent) } else { - const CharPointer_UTF32 start (input); + const String::CharPointerType start (input); int len = 0; for (;;) @@ -701,7 +701,7 @@ void XmlDocument::readEntity (String& result) } else { - const CharPointer_UTF32 entityNameStart (input); + const String::CharPointerType entityNameStart (input); const int closingSemiColon = input.indexOf ((juce_wchar) ';'); if (closingSemiColon < 0) diff --git a/src/text/juce_XmlDocument.h b/src/text/juce_XmlDocument.h index 915451edb2..a43dc6447d 100644 --- a/src/text/juce_XmlDocument.h +++ b/src/text/juce_XmlDocument.h @@ -153,7 +153,7 @@ public: //============================================================================== private: String originalText; - CharPointer_UTF32 input; + String::CharPointerType input; bool outOfData, errorOccurred; String lastError, dtdText;