| @@ -90,6 +90,10 @@ void WebBrowserComponent::goForward() | |||
| } | |||
| void WebBrowserComponent::refresh() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| void WebBrowserComponent::paint (Graphics& g) | |||
| { | |||
| @@ -543,6 +543,9 @@ private: | |||
| void swapRGBOrder (const int x, const int y, const int w, int h) const | |||
| { | |||
| #if JUCE_BIG_ENDIAN | |||
| jassert (pixelStride == 4); | |||
| #endif | |||
| jassert (Rectangle (0, 0, juceImage.getWidth(), juceImage.getHeight()) | |||
| .contains (Rectangle (x, y, w, h))); | |||
| @@ -555,9 +558,18 @@ private: | |||
| for (int i = w; --i >= 0;) | |||
| { | |||
| const uint8 temp = p[0]; | |||
| #if JUCE_BIG_ENDIAN | |||
| const uint8 oldp3 = p[3]; | |||
| const uint8 oldp1 = p[1]; | |||
| p[3] = p[0]; | |||
| p[0] = oldp1; | |||
| p[1] = p[2]; | |||
| p[2] = oldp3; | |||
| #else | |||
| const uint8 oldp0 = p[0]; | |||
| p[0] = p[2]; | |||
| p[2] = temp; | |||
| p[2] = oldp0; | |||
| #endif | |||
| p += pixelStride; | |||
| } | |||
| @@ -147,6 +147,11 @@ public: | |||
| { | |||
| [webView stopLoading: nil]; | |||
| } | |||
| void refresh() | |||
| { | |||
| [webView reload: nil]; | |||
| } | |||
| private: | |||
| WebView* webView; | |||
| @@ -206,6 +211,11 @@ void WebBrowserComponent::goForward() | |||
| browser->goForward(); | |||
| } | |||
| void WebBrowserComponent::refresh() | |||
| { | |||
| browser->refresh(); | |||
| } | |||
| //============================================================================== | |||
| void WebBrowserComponent::paint (Graphics& g) | |||
| { | |||
| @@ -70,7 +70,7 @@ static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPAR | |||
| return 0; | |||
| } | |||
| void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet); | |||
| void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet) throw(); | |||
| static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) | |||
| { | |||
| @@ -77,7 +77,7 @@ bool juce_canWriteToFile (const String& fileName) throw() | |||
| } | |||
| bool juce_setFileReadOnly (const String& fileName, | |||
| bool isReadOnly) | |||
| bool isReadOnly) throw() | |||
| { | |||
| DWORD attr = GetFileAttributes (fileName); | |||
| @@ -272,6 +272,12 @@ void WebBrowserComponent::goForward() | |||
| browser->browser->GoForward(); | |||
| } | |||
| void WebBrowserComponent::refresh() | |||
| { | |||
| if (browser->browser != 0) | |||
| browser->browser->Refresh(); | |||
| } | |||
| //============================================================================== | |||
| void WebBrowserComponent::paint (Graphics& g) | |||
| { | |||
| @@ -5351,6 +5351,12 @@ bool juce_findFileNext (void* handle, String& resultFile, | |||
| void juce_findFileClose (void* handle) throw(); | |||
| static const String juce_addTrailingSeparator (const String& path) throw() | |||
| { | |||
| return path.endsWithChar (File::separator) ? path | |||
| : path + File::separator; | |||
| } | |||
| static const String parseAbsolutePath (String path) throw() | |||
| { | |||
| if (path.isEmpty()) | |||
| @@ -5742,10 +5748,7 @@ const File File::getChildFile (String relativePath) const throw() | |||
| } | |||
| } | |||
| if (! path.endsWithChar (separator)) | |||
| path += separator; | |||
| return File (path + relativePath); | |||
| return File (juce_addTrailingSeparator (path) + relativePath); | |||
| } | |||
| } | |||
| @@ -5902,9 +5905,7 @@ int File::findChildFiles (OwnedArray<File>& results, | |||
| // find child files or directories in this directory first.. | |||
| if (isDirectory()) | |||
| { | |||
| String path (fullPath); | |||
| if (! path.endsWithChar (separator)) | |||
| path += separator; | |||
| const String path (juce_addTrailingSeparator (fullPath)); | |||
| String filename; | |||
| bool isDirectory, isHidden; | |||
| @@ -5999,6 +6000,37 @@ int File::getNumberOfChildFiles (const int whatToLookFor, | |||
| return count; | |||
| } | |||
| bool File::containsSubDirectories() const throw() | |||
| { | |||
| bool result = false; | |||
| if (isDirectory()) | |||
| { | |||
| String filename; | |||
| bool isDirectory, isHidden; | |||
| void* const handle = juce_findFileStart (juce_addTrailingSeparator (fullPath), | |||
| T("*"), filename, | |||
| &isDirectory, &isHidden, 0, 0, 0, 0); | |||
| if (handle != 0) | |||
| { | |||
| do | |||
| { | |||
| if (isDirectory) | |||
| { | |||
| result = true; | |||
| break; | |||
| } | |||
| } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0)); | |||
| juce_findFileClose (handle); | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| const File File::getNonexistentChildFile (const String& prefix_, | |||
| const String& suffix, | |||
| bool putNumbersInBrackets) const throw() | |||
| @@ -6272,11 +6304,8 @@ const String File::getRelativePathFrom (const File& dir) const throw() | |||
| thisPath [len] = 0; | |||
| } | |||
| String dirPath ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName() | |||
| : dir.fullPath); | |||
| if (! dirPath.endsWithChar (separator)) | |||
| dirPath += separator; | |||
| String dirPath (juce_addTrailingSeparator ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName() | |||
| : dir.fullPath)); | |||
| const int len = jmin (thisPath.length(), dirPath.length()); | |||
| int commonBitLength = 0; | |||
| @@ -11152,11 +11181,15 @@ bool String::startsWithIgnoreCase (const tchar* const other) const throw() | |||
| bool String::startsWithChar (const tchar character) const throw() | |||
| { | |||
| jassert (character != 0); // strings can't contain a null character! | |||
| return text->text[0] == character; | |||
| } | |||
| bool String::endsWithChar (const tchar character) const throw() | |||
| { | |||
| jassert (character != 0); // strings can't contain a null character! | |||
| return text->text[0] != 0 | |||
| && text->text [length() - 1] == character; | |||
| } | |||
| @@ -11502,6 +11535,17 @@ bool String::containsAnyOf (const tchar* const chars) const throw() | |||
| return false; | |||
| } | |||
| bool String::containsNonWhitespaceChars() const throw() | |||
| { | |||
| const tchar* t = text->text; | |||
| while (*t != 0) | |||
| if (! CharacterFunctions::isWhitespace (*t++)) | |||
| return true; | |||
| return false; | |||
| } | |||
| int String::getIntValue() const throw() | |||
| { | |||
| return CharacterFunctions::getIntValue (text->text); | |||
| @@ -12096,7 +12140,7 @@ void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw( | |||
| if (removeWhitespaceStrings) | |||
| { | |||
| for (int i = size(); --i >= 0;) | |||
| if (((const String*) strings.getUnchecked(i))->trim().isEmpty()) | |||
| if (! ((const String*) strings.getUnchecked(i))->containsNonWhitespaceChars()) | |||
| remove (i); | |||
| } | |||
| else | |||
| @@ -12525,6 +12569,7 @@ static bool isXmlIdentifierChar_Slow (const tchar c) throw() | |||
| XmlDocument::XmlDocument (const String& documentText) throw() | |||
| : originalText (documentText), | |||
| ignoreEmptyTextElements (true), | |||
| inputSource (0) | |||
| { | |||
| } | |||
| @@ -12548,6 +12593,11 @@ void XmlDocument::setInputSource (InputSource* const newSource) throw() | |||
| } | |||
| } | |||
| void XmlDocument::setEmptyTextElementsIgnored (const bool shouldBeIgnored) throw() | |||
| { | |||
| ignoreEmptyTextElements = shouldBeIgnored; | |||
| } | |||
| XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) | |||
| { | |||
| String textToParse (originalText); | |||
| @@ -13097,9 +13147,8 @@ void XmlDocument::readChildElements (XmlElement* parent) throw() | |||
| } | |||
| } | |||
| textElementContent = textElementContent.trim(); | |||
| if (textElementContent.isNotEmpty()) | |||
| if (ignoreEmptyTextElements ? textElementContent.containsNonWhitespaceChars() | |||
| : textElementContent.isNotEmpty()) | |||
| e->setText (textElementContent); | |||
| } | |||
| } | |||
| @@ -13412,7 +13461,7 @@ XmlElement::XmlElement (const String& tagName_) throw() | |||
| attributes (0) | |||
| { | |||
| // the tag name mustn't be empty, or it'll look like a text element! | |||
| jassert (tagName_.trim().isNotEmpty()) | |||
| jassert (tagName_.containsNonWhitespaceChars()) | |||
| } | |||
| XmlElement::XmlElement (int /*dummy*/) throw() | |||
| @@ -14684,9 +14733,10 @@ static CriticalSection runningThreadsLock; | |||
| void Thread::threadEntryPoint (Thread* const thread) throw() | |||
| { | |||
| runningThreadsLock.enter(); | |||
| runningThreads.add (thread); | |||
| runningThreadsLock.exit(); | |||
| { | |||
| const ScopedLock sl (runningThreadsLock); | |||
| runningThreads.add (thread); | |||
| } | |||
| JUCE_TRY | |||
| { | |||
| @@ -14705,10 +14755,12 @@ void Thread::threadEntryPoint (Thread* const thread) throw() | |||
| } | |||
| JUCE_CATCH_ALL_ASSERT | |||
| runningThreadsLock.enter(); | |||
| jassert (runningThreads.contains (thread)); | |||
| runningThreads.removeValue (thread); | |||
| runningThreadsLock.exit(); | |||
| { | |||
| const ScopedLock sl (runningThreadsLock); | |||
| jassert (runningThreads.contains (thread)); | |||
| runningThreads.removeValue (thread); | |||
| } | |||
| #if JUCE_WIN32 | |||
| juce_CloseThreadHandle (thread->threadHandle_); | |||
| @@ -14872,34 +14924,28 @@ int Thread::getNumRunningThreads() throw() | |||
| Thread* Thread::getCurrentThread() throw() | |||
| { | |||
| const ThreadID thisId = getCurrentThreadId(); | |||
| Thread* result = 0; | |||
| runningThreadsLock.enter(); | |||
| const ScopedLock sl (runningThreadsLock); | |||
| for (int i = runningThreads.size(); --i >= 0;) | |||
| { | |||
| Thread* const t = (Thread*) (runningThreads.getUnchecked(i)); | |||
| if (t->threadId_ == thisId) | |||
| { | |||
| result = t; | |||
| break; | |||
| } | |||
| return t; | |||
| } | |||
| runningThreadsLock.exit(); | |||
| return result; | |||
| return 0; | |||
| } | |||
| void Thread::stopAllThreads (const int timeOutMilliseconds) throw() | |||
| { | |||
| runningThreadsLock.enter(); | |||
| for (int i = runningThreads.size(); --i >= 0;) | |||
| ((Thread*) runningThreads.getUnchecked(i))->signalThreadShouldExit(); | |||
| { | |||
| const ScopedLock sl (runningThreadsLock); | |||
| runningThreadsLock.exit(); | |||
| for (int i = runningThreads.size(); --i >= 0;) | |||
| ((Thread*) runningThreads.getUnchecked(i))->signalThreadShouldExit(); | |||
| } | |||
| for (;;) | |||
| { | |||
| @@ -27173,7 +27219,7 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||
| String thisSubMenuName (sortMethod == sortByCategory ? pd->category | |||
| : pd->manufacturerName); | |||
| if (thisSubMenuName.trim().isEmpty()) | |||
| if (! thisSubMenuName.containsNonWhitespaceChars()) | |||
| thisSubMenuName = T("Other"); | |||
| if (thisSubMenuName != lastSubMenuName) | |||
| @@ -41632,9 +41678,14 @@ bool Button::keyStateChanged (Component*) | |||
| updateState (0); | |||
| if (isEnabled() && wasDown && ! isKeyDown) | |||
| { | |||
| internalClickCallback (ModifierKeys::getCurrentModifiers()); | |||
| return isKeyDown || wasDown; | |||
| // (return immediately - this button may now have been deleted) | |||
| return true; | |||
| } | |||
| return wasDown || isKeyDown; | |||
| } | |||
| bool Button::keyPressed (const KeyPress&, Component*) | |||
| @@ -51504,6 +51555,13 @@ void TreeView::setRootItem (TreeViewItem* const newRootItem) | |||
| } | |||
| } | |||
| void TreeView::deleteRootItem() | |||
| { | |||
| TreeViewItem* const oldItem = rootItem; | |||
| setRootItem (0); | |||
| delete oldItem; | |||
| } | |||
| void TreeView::setRootItemVisible (const bool shouldBeVisible) | |||
| { | |||
| rootItemVisible = shouldBeVisible; | |||
| @@ -53286,7 +53344,7 @@ FileChooser::FileChooser (const String& chooserBoxTitle, | |||
| useNativeDialogBox = false; | |||
| #endif | |||
| if (fileFilters.trim().isEmpty()) | |||
| if (! fileFilters.containsNonWhitespaceChars()) | |||
| filters = T("*"); | |||
| } | |||
| @@ -239911,7 +239969,7 @@ bool juce_canWriteToFile (const String& fileName) throw() | |||
| } | |||
| bool juce_setFileReadOnly (const String& fileName, | |||
| bool isReadOnly) | |||
| bool isReadOnly) throw() | |||
| { | |||
| DWORD attr = GetFileAttributes (fileName); | |||
| @@ -245012,7 +245070,7 @@ static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPAR | |||
| return 0; | |||
| } | |||
| void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet); | |||
| void juce_setWindowStyleBit (HWND h, int styleType, int feature, bool bitIsSet) throw(); | |||
| static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) | |||
| { | |||
| @@ -246576,6 +246634,12 @@ void WebBrowserComponent::goForward() | |||
| browser->browser->GoForward(); | |||
| } | |||
| void WebBrowserComponent::refresh() | |||
| { | |||
| if (browser->browser != 0) | |||
| browser->browser->Refresh(); | |||
| } | |||
| void WebBrowserComponent::paint (Graphics& g) | |||
| { | |||
| if (browser->browser == 0) | |||
| @@ -259370,6 +259434,10 @@ void WebBrowserComponent::goForward() | |||
| } | |||
| void WebBrowserComponent::refresh() | |||
| { | |||
| } | |||
| void WebBrowserComponent::paint (Graphics& g) | |||
| { | |||
| g.fillAll (Colours::white); | |||
| @@ -265555,6 +265623,9 @@ private: | |||
| void swapRGBOrder (const int x, const int y, const int w, int h) const | |||
| { | |||
| #if JUCE_BIG_ENDIAN | |||
| jassert (pixelStride == 4); | |||
| #endif | |||
| jassert (Rectangle (0, 0, juceImage.getWidth(), juceImage.getHeight()) | |||
| .contains (Rectangle (x, y, w, h))); | |||
| @@ -265567,9 +265638,18 @@ private: | |||
| for (int i = w; --i >= 0;) | |||
| { | |||
| const uint8 temp = p[0]; | |||
| #if JUCE_BIG_ENDIAN | |||
| const uint8 oldp3 = p[3]; | |||
| const uint8 oldp1 = p[1]; | |||
| p[3] = p[0]; | |||
| p[0] = oldp1; | |||
| p[1] = p[2]; | |||
| p[2] = oldp3; | |||
| #else | |||
| const uint8 oldp0 = p[0]; | |||
| p[0] = p[2]; | |||
| p[2] = temp; | |||
| p[2] = oldp0; | |||
| #endif | |||
| p += pixelStride; | |||
| } | |||
| @@ -269370,6 +269450,11 @@ public: | |||
| [webView stopLoading: nil]; | |||
| } | |||
| void refresh() | |||
| { | |||
| [webView reload: nil]; | |||
| } | |||
| private: | |||
| WebView* webView; | |||
| DownloadClickDetector* clickListener; | |||
| @@ -269426,6 +269511,11 @@ void WebBrowserComponent::goForward() | |||
| browser->goForward(); | |||
| } | |||
| void WebBrowserComponent::refresh() | |||
| { | |||
| browser->refresh(); | |||
| } | |||
| void WebBrowserComponent::paint (Graphics& g) | |||
| { | |||
| } | |||
| @@ -1535,12 +1535,16 @@ public: | |||
| /** Returns true if the string contains no characters. | |||
| Note that there's also an isNotEmpty() method to help write readable code. | |||
| @see containsNonWhitespaceChars() | |||
| */ | |||
| inline bool isEmpty() const throw() { return text->text[0] == 0; } | |||
| /** Returns true if the string contains at least one character. | |||
| Note that there's also an isEmpty() method to help write readable code. | |||
| @see containsNonWhitespaceChars() | |||
| */ | |||
| inline bool isNotEmpty() const throw() { return text->text[0] != 0; } | |||
| @@ -1700,6 +1704,15 @@ public: | |||
| */ | |||
| bool containsOnly (const tchar* const charactersItMightContain) const throw(); | |||
| /** Returns true if this string contains any non-whitespace characters. | |||
| This will return false if the string contains only whitespace characters, or | |||
| if it's empty. | |||
| It is equivalent to calling "myString.trim().isNotEmpty()". | |||
| */ | |||
| bool containsNonWhitespaceChars() const throw(); | |||
| /** Returns true if the string matches this simple wildcard expression. | |||
| So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. | |||
| @@ -6534,6 +6547,11 @@ public: | |||
| int getNumberOfChildFiles (const int whatToLookFor, | |||
| const String& wildCardPattern = JUCE_T("*")) const throw(); | |||
| /** Returns true if this file is a directory that contains one or more subdirectories. | |||
| @see isDirectory, findChildFiles | |||
| */ | |||
| bool File::containsSubDirectories() const throw(); | |||
| /** Creates a stream to read from this file. | |||
| @returns a stream that will read from this file (initially positioned at the | |||
| @@ -11812,7 +11830,14 @@ public: | |||
| /** Creates a checksum for a block of binary data. */ | |||
| MD5 (const char* data, const int numBytes); | |||
| /** Creates a checksum for a string. */ | |||
| /** Creates a checksum for a string. | |||
| Note that this operates on the string as a block of unicode characters, so the | |||
| result you get will differ from the value you'd get if the string was treated | |||
| as a block of utf8 or ascii. Bear this in mind if you're comparing the result | |||
| of this method with a checksum created by a different framework, which may have | |||
| used a different encoding. | |||
| */ | |||
| MD5 (const String& text); | |||
| /** Creates a checksum for the input from a stream. | |||
| @@ -13751,6 +13776,15 @@ public: | |||
| */ | |||
| void setInputSource (InputSource* const newSource) throw(); | |||
| /** Sets a flag to change the treatment of empty text elements. | |||
| If this is true (the default state), then any text elements that contain only | |||
| whitespace characters will be ingored during parsing. If you need to catch | |||
| whitespace-only text, then you should set this to false before calling the | |||
| getDocumentElement() method. | |||
| */ | |||
| void setEmptyTextElementsIgnored (const bool shouldBeIgnored) throw(); | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| @@ -13761,7 +13795,7 @@ private: | |||
| bool identifierLookupTable [128]; | |||
| String lastError, dtdText; | |||
| StringArray tokenisedDTD; | |||
| bool needToLoadDTD; | |||
| bool needToLoadDTD, ignoreEmptyTextElements; | |||
| InputSource* inputSource; | |||
| void setLastError (const String& desc, const bool carryOn) throw(); | |||
| @@ -43179,7 +43213,8 @@ public: | |||
| The object passed in will not be deleted by the treeview, it's up to the caller | |||
| to delete it when no longer needed. BUT make absolutely sure that you don't delete | |||
| this item until you've removed it from the tree, either by calling setRootItem (0), | |||
| or by deleting the tree first. | |||
| or by deleting the tree first. You can also use deleteRootItem() as a quick way | |||
| to delete it. | |||
| */ | |||
| void setRootItem (TreeViewItem* const newRootItem); | |||
| @@ -43189,6 +43224,12 @@ public: | |||
| */ | |||
| TreeViewItem* getRootItem() const throw() { return rootItem; } | |||
| /** This will remove and delete the current root item. | |||
| It's a convenient way of deleting the item and calling setRootItem (0). | |||
| */ | |||
| void deleteRootItem(); | |||
| /** Changes whether the tree's root item is shown or not. | |||
| If the root item is hidden, only its sub-items will be shown in the treeview - this | |||
| @@ -52820,6 +52861,10 @@ public: | |||
| */ | |||
| void goForward(); | |||
| /** Refreshes the browser. | |||
| */ | |||
| void refresh(); | |||
| /** This callback is called when the browser is about to navigate | |||
| to a new location. | |||
| @@ -416,7 +416,7 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||
| String thisSubMenuName (sortMethod == sortByCategory ? pd->category | |||
| : pd->manufacturerName); | |||
| if (thisSubMenuName.trim().isEmpty()) | |||
| if (! thisSubMenuName.containsNonWhitespaceChars()) | |||
| thisSubMenuName = T("Other"); | |||
| if (thisSubMenuName != lastSubMenuName) | |||
| @@ -613,9 +613,14 @@ bool Button::keyStateChanged (Component*) | |||
| updateState (0); | |||
| if (isEnabled() && wasDown && ! isKeyDown) | |||
| { | |||
| internalClickCallback (ModifierKeys::getCurrentModifiers()); | |||
| return isKeyDown || wasDown; | |||
| // (return immediately - this button may now have been deleted) | |||
| return true; | |||
| } | |||
| return wasDown || isKeyDown; | |||
| } | |||
| bool Button::keyPressed (const KeyPress&, Component*) | |||
| @@ -435,6 +435,14 @@ void TreeView::setRootItem (TreeViewItem* const newRootItem) | |||
| } | |||
| } | |||
| void TreeView::deleteRootItem() | |||
| { | |||
| TreeViewItem* const oldItem = rootItem; | |||
| setRootItem (0); | |||
| delete oldItem; | |||
| } | |||
| void TreeView::setRootItemVisible (const bool shouldBeVisible) | |||
| { | |||
| rootItemVisible = shouldBeVisible; | |||
| @@ -414,7 +414,8 @@ public: | |||
| The object passed in will not be deleted by the treeview, it's up to the caller | |||
| to delete it when no longer needed. BUT make absolutely sure that you don't delete | |||
| this item until you've removed it from the tree, either by calling setRootItem (0), | |||
| or by deleting the tree first. | |||
| or by deleting the tree first. You can also use deleteRootItem() as a quick way | |||
| to delete it. | |||
| */ | |||
| void setRootItem (TreeViewItem* const newRootItem); | |||
| @@ -424,6 +425,12 @@ public: | |||
| */ | |||
| TreeViewItem* getRootItem() const throw() { return rootItem; } | |||
| /** This will remove and delete the current root item. | |||
| It's a convenient way of deleting the item and calling setRootItem (0). | |||
| */ | |||
| void deleteRootItem(); | |||
| /** Changes whether the tree's root item is shown or not. | |||
| If the root item is hidden, only its sub-items will be shown in the treeview - this | |||
| @@ -54,7 +54,7 @@ FileChooser::FileChooser (const String& chooserBoxTitle, | |||
| useNativeDialogBox = false; | |||
| #endif | |||
| if (fileFilters.trim().isEmpty()) | |||
| if (! fileFilters.containsNonWhitespaceChars()) | |||
| filters = T("*"); | |||
| } | |||
| @@ -86,6 +86,9 @@ public: | |||
| */ | |||
| void goForward(); | |||
| /** Refreshes the browser. | |||
| */ | |||
| void refresh(); | |||
| //============================================================================== | |||
| /** This callback is called when the browser is about to navigate | |||
| @@ -66,7 +66,14 @@ public: | |||
| /** Creates a checksum for a block of binary data. */ | |||
| MD5 (const char* data, const int numBytes); | |||
| /** Creates a checksum for a string. */ | |||
| /** Creates a checksum for a string. | |||
| Note that this operates on the string as a block of unicode characters, so the | |||
| result you get will differ from the value you'd get if the string was treated | |||
| as a block of utf8 or ascii. Bear this in mind if you're comparing the result | |||
| of this method with a checksum created by a different framework, which may have | |||
| used a different encoding. | |||
| */ | |||
| MD5 (const String& text); | |||
| /** Creates a checksum for the input from a stream. | |||
| @@ -96,6 +96,12 @@ bool juce_findFileNext (void* handle, String& resultFile, | |||
| void juce_findFileClose (void* handle) throw(); | |||
| //============================================================================== | |||
| static const String juce_addTrailingSeparator (const String& path) throw() | |||
| { | |||
| return path.endsWithChar (File::separator) ? path | |||
| : path + File::separator; | |||
| } | |||
| //============================================================================== | |||
| static const String parseAbsolutePath (String path) throw() | |||
| @@ -498,10 +504,7 @@ const File File::getChildFile (String relativePath) const throw() | |||
| } | |||
| } | |||
| if (! path.endsWithChar (separator)) | |||
| path += separator; | |||
| return File (path + relativePath); | |||
| return File (juce_addTrailingSeparator (path) + relativePath); | |||
| } | |||
| } | |||
| @@ -663,9 +666,7 @@ int File::findChildFiles (OwnedArray<File>& results, | |||
| // find child files or directories in this directory first.. | |||
| if (isDirectory()) | |||
| { | |||
| String path (fullPath); | |||
| if (! path.endsWithChar (separator)) | |||
| path += separator; | |||
| const String path (juce_addTrailingSeparator (fullPath)); | |||
| String filename; | |||
| bool isDirectory, isHidden; | |||
| @@ -760,6 +761,37 @@ int File::getNumberOfChildFiles (const int whatToLookFor, | |||
| return count; | |||
| } | |||
| bool File::containsSubDirectories() const throw() | |||
| { | |||
| bool result = false; | |||
| if (isDirectory()) | |||
| { | |||
| String filename; | |||
| bool isDirectory, isHidden; | |||
| void* const handle = juce_findFileStart (juce_addTrailingSeparator (fullPath), | |||
| T("*"), filename, | |||
| &isDirectory, &isHidden, 0, 0, 0, 0); | |||
| if (handle != 0) | |||
| { | |||
| do | |||
| { | |||
| if (isDirectory) | |||
| { | |||
| result = true; | |||
| break; | |||
| } | |||
| } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0)); | |||
| juce_findFileClose (handle); | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| //============================================================================== | |||
| const File File::getNonexistentChildFile (const String& prefix_, | |||
| const String& suffix, | |||
| @@ -1040,11 +1072,8 @@ const String File::getRelativePathFrom (const File& dir) const throw() | |||
| thisPath [len] = 0; | |||
| } | |||
| String dirPath ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName() | |||
| : dir.fullPath); | |||
| if (! dirPath.endsWithChar (separator)) | |||
| dirPath += separator; | |||
| String dirPath (juce_addTrailingSeparator ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName() | |||
| : dir.fullPath)); | |||
| const int len = jmin (thisPath.length(), dirPath.length()); | |||
| int commonBitLength = 0; | |||
| @@ -548,6 +548,11 @@ public: | |||
| int getNumberOfChildFiles (const int whatToLookFor, | |||
| const String& wildCardPattern = JUCE_T("*")) const throw(); | |||
| /** Returns true if this file is a directory that contains one or more subdirectories. | |||
| @see isDirectory, findChildFiles | |||
| */ | |||
| bool File::containsSubDirectories() const throw(); | |||
| //============================================================================== | |||
| /** Creates a stream to read from this file. | |||
| @@ -1483,11 +1483,15 @@ bool String::startsWithIgnoreCase (const tchar* const other) const throw() | |||
| bool String::startsWithChar (const tchar character) const throw() | |||
| { | |||
| jassert (character != 0); // strings can't contain a null character! | |||
| return text->text[0] == character; | |||
| } | |||
| bool String::endsWithChar (const tchar character) const throw() | |||
| { | |||
| jassert (character != 0); // strings can't contain a null character! | |||
| return text->text[0] != 0 | |||
| && text->text [length() - 1] == character; | |||
| } | |||
| @@ -1838,6 +1842,18 @@ bool String::containsAnyOf (const tchar* const chars) const throw() | |||
| return false; | |||
| } | |||
| bool String::containsNonWhitespaceChars() const throw() | |||
| { | |||
| const tchar* t = text->text; | |||
| while (*t != 0) | |||
| if (! CharacterFunctions::isWhitespace (*t++)) | |||
| return true; | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| int String::getIntValue() const throw() | |||
| { | |||
| @@ -176,12 +176,16 @@ public: | |||
| /** Returns true if the string contains no characters. | |||
| Note that there's also an isNotEmpty() method to help write readable code. | |||
| @see containsNonWhitespaceChars() | |||
| */ | |||
| inline bool isEmpty() const throw() { return text->text[0] == 0; } | |||
| /** Returns true if the string contains at least one character. | |||
| Note that there's also an isEmpty() method to help write readable code. | |||
| @see containsNonWhitespaceChars() | |||
| */ | |||
| inline bool isNotEmpty() const throw() { return text->text[0] != 0; } | |||
| @@ -341,6 +345,15 @@ public: | |||
| */ | |||
| bool containsOnly (const tchar* const charactersItMightContain) const throw(); | |||
| /** Returns true if this string contains any non-whitespace characters. | |||
| This will return false if the string contains only whitespace characters, or | |||
| if it's empty. | |||
| It is equivalent to calling "myString.trim().isNotEmpty()". | |||
| */ | |||
| bool containsNonWhitespaceChars() const throw(); | |||
| /** Returns true if the string matches this simple wildcard expression. | |||
| So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. | |||
| @@ -271,7 +271,7 @@ void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw( | |||
| if (removeWhitespaceStrings) | |||
| { | |||
| for (int i = size(); --i >= 0;) | |||
| if (((const String*) strings.getUnchecked(i))->trim().isEmpty()) | |||
| if (! ((const String*) strings.getUnchecked(i))->containsNonWhitespaceChars()) | |||
| remove (i); | |||
| } | |||
| else | |||
| @@ -54,6 +54,7 @@ static bool isXmlIdentifierChar_Slow (const tchar c) throw() | |||
| //============================================================================== | |||
| XmlDocument::XmlDocument (const String& documentText) throw() | |||
| : originalText (documentText), | |||
| ignoreEmptyTextElements (true), | |||
| inputSource (0) | |||
| { | |||
| } | |||
| @@ -77,6 +78,11 @@ void XmlDocument::setInputSource (InputSource* const newSource) throw() | |||
| } | |||
| } | |||
| void XmlDocument::setEmptyTextElementsIgnored (const bool shouldBeIgnored) throw() | |||
| { | |||
| ignoreEmptyTextElements = shouldBeIgnored; | |||
| } | |||
| XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) | |||
| { | |||
| String textToParse (originalText); | |||
| @@ -626,9 +632,8 @@ void XmlDocument::readChildElements (XmlElement* parent) throw() | |||
| } | |||
| } | |||
| textElementContent = textElementContent.trim(); | |||
| if (textElementContent.isNotEmpty()) | |||
| if (ignoreEmptyTextElements ? textElementContent.containsNonWhitespaceChars() | |||
| : textElementContent.isNotEmpty()) | |||
| e->setText (textElementContent); | |||
| } | |||
| } | |||
| @@ -122,6 +122,14 @@ public: | |||
| */ | |||
| void setInputSource (InputSource* const newSource) throw(); | |||
| /** Sets a flag to change the treatment of empty text elements. | |||
| If this is true (the default state), then any text elements that contain only | |||
| whitespace characters will be ingored during parsing. If you need to catch | |||
| whitespace-only text, then you should set this to false before calling the | |||
| getDocumentElement() method. | |||
| */ | |||
| void setEmptyTextElementsIgnored (const bool shouldBeIgnored) throw(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| @@ -134,7 +142,7 @@ private: | |||
| bool identifierLookupTable [128]; | |||
| String lastError, dtdText; | |||
| StringArray tokenisedDTD; | |||
| bool needToLoadDTD; | |||
| bool needToLoadDTD, ignoreEmptyTextElements; | |||
| InputSource* inputSource; | |||
| void setLastError (const String& desc, const bool carryOn) throw(); | |||
| @@ -63,7 +63,7 @@ XmlElement::XmlElement (const String& tagName_) throw() | |||
| attributes (0) | |||
| { | |||
| // the tag name mustn't be empty, or it'll look like a text element! | |||
| jassert (tagName_.trim().isNotEmpty()) | |||
| jassert (tagName_.containsNonWhitespaceChars()) | |||
| } | |||
| XmlElement::XmlElement (int /*dummy*/) throw() | |||
| @@ -55,9 +55,10 @@ static CriticalSection runningThreadsLock; | |||
| //============================================================================== | |||
| void Thread::threadEntryPoint (Thread* const thread) throw() | |||
| { | |||
| runningThreadsLock.enter(); | |||
| runningThreads.add (thread); | |||
| runningThreadsLock.exit(); | |||
| { | |||
| const ScopedLock sl (runningThreadsLock); | |||
| runningThreads.add (thread); | |||
| } | |||
| JUCE_TRY | |||
| { | |||
| @@ -76,10 +77,12 @@ void Thread::threadEntryPoint (Thread* const thread) throw() | |||
| } | |||
| JUCE_CATCH_ALL_ASSERT | |||
| runningThreadsLock.enter(); | |||
| jassert (runningThreads.contains (thread)); | |||
| runningThreads.removeValue (thread); | |||
| runningThreadsLock.exit(); | |||
| { | |||
| const ScopedLock sl (runningThreadsLock); | |||
| jassert (runningThreads.contains (thread)); | |||
| runningThreads.removeValue (thread); | |||
| } | |||
| #if JUCE_WIN32 | |||
| juce_CloseThreadHandle (thread->threadHandle_); | |||
| @@ -250,34 +253,28 @@ int Thread::getNumRunningThreads() throw() | |||
| Thread* Thread::getCurrentThread() throw() | |||
| { | |||
| const ThreadID thisId = getCurrentThreadId(); | |||
| Thread* result = 0; | |||
| runningThreadsLock.enter(); | |||
| const ScopedLock sl (runningThreadsLock); | |||
| for (int i = runningThreads.size(); --i >= 0;) | |||
| { | |||
| Thread* const t = (Thread*) (runningThreads.getUnchecked(i)); | |||
| if (t->threadId_ == thisId) | |||
| { | |||
| result = t; | |||
| break; | |||
| } | |||
| return t; | |||
| } | |||
| runningThreadsLock.exit(); | |||
| return result; | |||
| return 0; | |||
| } | |||
| void Thread::stopAllThreads (const int timeOutMilliseconds) throw() | |||
| { | |||
| runningThreadsLock.enter(); | |||
| for (int i = runningThreads.size(); --i >= 0;) | |||
| ((Thread*) runningThreads.getUnchecked(i))->signalThreadShouldExit(); | |||
| { | |||
| const ScopedLock sl (runningThreadsLock); | |||
| runningThreadsLock.exit(); | |||
| for (int i = runningThreads.size(); --i >= 0;) | |||
| ((Thread*) runningThreads.getUnchecked(i))->signalThreadShouldExit(); | |||
| } | |||
| for (;;) | |||
| { | |||