| @@ -90,6 +90,10 @@ void WebBrowserComponent::goForward() | |||||
| } | } | ||||
| void WebBrowserComponent::refresh() | |||||
| { | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void WebBrowserComponent::paint (Graphics& g) | void WebBrowserComponent::paint (Graphics& g) | ||||
| { | { | ||||
| @@ -543,6 +543,9 @@ private: | |||||
| void swapRGBOrder (const int x, const int y, const int w, int h) const | 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()) | jassert (Rectangle (0, 0, juceImage.getWidth(), juceImage.getHeight()) | ||||
| .contains (Rectangle (x, y, w, h))); | .contains (Rectangle (x, y, w, h))); | ||||
| @@ -555,9 +558,18 @@ private: | |||||
| for (int i = w; --i >= 0;) | 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[0] = p[2]; | ||||
| p[2] = temp; | |||||
| p[2] = oldp0; | |||||
| #endif | |||||
| p += pixelStride; | p += pixelStride; | ||||
| } | } | ||||
| @@ -147,6 +147,11 @@ public: | |||||
| { | { | ||||
| [webView stopLoading: nil]; | [webView stopLoading: nil]; | ||||
| } | } | ||||
| void refresh() | |||||
| { | |||||
| [webView reload: nil]; | |||||
| } | |||||
| private: | private: | ||||
| WebView* webView; | WebView* webView; | ||||
| @@ -206,6 +211,11 @@ void WebBrowserComponent::goForward() | |||||
| browser->goForward(); | browser->goForward(); | ||||
| } | } | ||||
| void WebBrowserComponent::refresh() | |||||
| { | |||||
| browser->refresh(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void WebBrowserComponent::paint (Graphics& g) | void WebBrowserComponent::paint (Graphics& g) | ||||
| { | { | ||||
| @@ -70,7 +70,7 @@ static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPAR | |||||
| return 0; | 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) | 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 juce_setFileReadOnly (const String& fileName, | ||||
| bool isReadOnly) | |||||
| bool isReadOnly) throw() | |||||
| { | { | ||||
| DWORD attr = GetFileAttributes (fileName); | DWORD attr = GetFileAttributes (fileName); | ||||
| @@ -272,6 +272,12 @@ void WebBrowserComponent::goForward() | |||||
| browser->browser->GoForward(); | browser->browser->GoForward(); | ||||
| } | } | ||||
| void WebBrowserComponent::refresh() | |||||
| { | |||||
| if (browser->browser != 0) | |||||
| browser->browser->Refresh(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void WebBrowserComponent::paint (Graphics& g) | void WebBrowserComponent::paint (Graphics& g) | ||||
| { | { | ||||
| @@ -5351,6 +5351,12 @@ bool juce_findFileNext (void* handle, String& resultFile, | |||||
| void juce_findFileClose (void* handle) throw(); | 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() | static const String parseAbsolutePath (String path) throw() | ||||
| { | { | ||||
| if (path.isEmpty()) | 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.. | // find child files or directories in this directory first.. | ||||
| if (isDirectory()) | if (isDirectory()) | ||||
| { | { | ||||
| String path (fullPath); | |||||
| if (! path.endsWithChar (separator)) | |||||
| path += separator; | |||||
| const String path (juce_addTrailingSeparator (fullPath)); | |||||
| String filename; | String filename; | ||||
| bool isDirectory, isHidden; | bool isDirectory, isHidden; | ||||
| @@ -5999,6 +6000,37 @@ int File::getNumberOfChildFiles (const int whatToLookFor, | |||||
| return count; | 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 File File::getNonexistentChildFile (const String& prefix_, | ||||
| const String& suffix, | const String& suffix, | ||||
| bool putNumbersInBrackets) const throw() | bool putNumbersInBrackets) const throw() | ||||
| @@ -6272,11 +6304,8 @@ const String File::getRelativePathFrom (const File& dir) const throw() | |||||
| thisPath [len] = 0; | 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()); | const int len = jmin (thisPath.length(), dirPath.length()); | ||||
| int commonBitLength = 0; | int commonBitLength = 0; | ||||
| @@ -11152,11 +11181,15 @@ bool String::startsWithIgnoreCase (const tchar* const other) const throw() | |||||
| bool String::startsWithChar (const tchar character) 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; | return text->text[0] == character; | ||||
| } | } | ||||
| bool String::endsWithChar (const tchar character) const throw() | bool String::endsWithChar (const tchar character) const throw() | ||||
| { | { | ||||
| jassert (character != 0); // strings can't contain a null character! | |||||
| return text->text[0] != 0 | return text->text[0] != 0 | ||||
| && text->text [length() - 1] == character; | && text->text [length() - 1] == character; | ||||
| } | } | ||||
| @@ -11502,6 +11535,17 @@ bool String::containsAnyOf (const tchar* const chars) const throw() | |||||
| return false; | 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() | int String::getIntValue() const throw() | ||||
| { | { | ||||
| return CharacterFunctions::getIntValue (text->text); | return CharacterFunctions::getIntValue (text->text); | ||||
| @@ -12096,7 +12140,7 @@ void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw( | |||||
| if (removeWhitespaceStrings) | if (removeWhitespaceStrings) | ||||
| { | { | ||||
| for (int i = size(); --i >= 0;) | for (int i = size(); --i >= 0;) | ||||
| if (((const String*) strings.getUnchecked(i))->trim().isEmpty()) | |||||
| if (! ((const String*) strings.getUnchecked(i))->containsNonWhitespaceChars()) | |||||
| remove (i); | remove (i); | ||||
| } | } | ||||
| else | else | ||||
| @@ -12525,6 +12569,7 @@ static bool isXmlIdentifierChar_Slow (const tchar c) throw() | |||||
| XmlDocument::XmlDocument (const String& documentText) throw() | XmlDocument::XmlDocument (const String& documentText) throw() | ||||
| : originalText (documentText), | : originalText (documentText), | ||||
| ignoreEmptyTextElements (true), | |||||
| inputSource (0) | 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) | XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) | ||||
| { | { | ||||
| String textToParse (originalText); | 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); | e->setText (textElementContent); | ||||
| } | } | ||||
| } | } | ||||
| @@ -13412,7 +13461,7 @@ XmlElement::XmlElement (const String& tagName_) throw() | |||||
| attributes (0) | attributes (0) | ||||
| { | { | ||||
| // the tag name mustn't be empty, or it'll look like a text element! | // 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() | XmlElement::XmlElement (int /*dummy*/) throw() | ||||
| @@ -14684,9 +14733,10 @@ static CriticalSection runningThreadsLock; | |||||
| void Thread::threadEntryPoint (Thread* const thread) throw() | void Thread::threadEntryPoint (Thread* const thread) throw() | ||||
| { | { | ||||
| runningThreadsLock.enter(); | |||||
| runningThreads.add (thread); | |||||
| runningThreadsLock.exit(); | |||||
| { | |||||
| const ScopedLock sl (runningThreadsLock); | |||||
| runningThreads.add (thread); | |||||
| } | |||||
| JUCE_TRY | JUCE_TRY | ||||
| { | { | ||||
| @@ -14705,10 +14755,12 @@ void Thread::threadEntryPoint (Thread* const thread) throw() | |||||
| } | } | ||||
| JUCE_CATCH_ALL_ASSERT | 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 | #if JUCE_WIN32 | ||||
| juce_CloseThreadHandle (thread->threadHandle_); | juce_CloseThreadHandle (thread->threadHandle_); | ||||
| @@ -14872,34 +14924,28 @@ int Thread::getNumRunningThreads() throw() | |||||
| Thread* Thread::getCurrentThread() throw() | Thread* Thread::getCurrentThread() throw() | ||||
| { | { | ||||
| const ThreadID thisId = getCurrentThreadId(); | const ThreadID thisId = getCurrentThreadId(); | ||||
| Thread* result = 0; | |||||
| runningThreadsLock.enter(); | |||||
| const ScopedLock sl (runningThreadsLock); | |||||
| for (int i = runningThreads.size(); --i >= 0;) | for (int i = runningThreads.size(); --i >= 0;) | ||||
| { | { | ||||
| Thread* const t = (Thread*) (runningThreads.getUnchecked(i)); | Thread* const t = (Thread*) (runningThreads.getUnchecked(i)); | ||||
| if (t->threadId_ == thisId) | if (t->threadId_ == thisId) | ||||
| { | |||||
| result = t; | |||||
| break; | |||||
| } | |||||
| return t; | |||||
| } | } | ||||
| runningThreadsLock.exit(); | |||||
| return result; | |||||
| return 0; | |||||
| } | } | ||||
| void Thread::stopAllThreads (const int timeOutMilliseconds) throw() | 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 (;;) | for (;;) | ||||
| { | { | ||||
| @@ -27173,7 +27219,7 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||||
| String thisSubMenuName (sortMethod == sortByCategory ? pd->category | String thisSubMenuName (sortMethod == sortByCategory ? pd->category | ||||
| : pd->manufacturerName); | : pd->manufacturerName); | ||||
| if (thisSubMenuName.trim().isEmpty()) | |||||
| if (! thisSubMenuName.containsNonWhitespaceChars()) | |||||
| thisSubMenuName = T("Other"); | thisSubMenuName = T("Other"); | ||||
| if (thisSubMenuName != lastSubMenuName) | if (thisSubMenuName != lastSubMenuName) | ||||
| @@ -41632,9 +41678,14 @@ bool Button::keyStateChanged (Component*) | |||||
| updateState (0); | updateState (0); | ||||
| if (isEnabled() && wasDown && ! isKeyDown) | if (isEnabled() && wasDown && ! isKeyDown) | ||||
| { | |||||
| internalClickCallback (ModifierKeys::getCurrentModifiers()); | 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*) | 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) | void TreeView::setRootItemVisible (const bool shouldBeVisible) | ||||
| { | { | ||||
| rootItemVisible = shouldBeVisible; | rootItemVisible = shouldBeVisible; | ||||
| @@ -53286,7 +53344,7 @@ FileChooser::FileChooser (const String& chooserBoxTitle, | |||||
| useNativeDialogBox = false; | useNativeDialogBox = false; | ||||
| #endif | #endif | ||||
| if (fileFilters.trim().isEmpty()) | |||||
| if (! fileFilters.containsNonWhitespaceChars()) | |||||
| filters = T("*"); | filters = T("*"); | ||||
| } | } | ||||
| @@ -239911,7 +239969,7 @@ bool juce_canWriteToFile (const String& fileName) throw() | |||||
| } | } | ||||
| bool juce_setFileReadOnly (const String& fileName, | bool juce_setFileReadOnly (const String& fileName, | ||||
| bool isReadOnly) | |||||
| bool isReadOnly) throw() | |||||
| { | { | ||||
| DWORD attr = GetFileAttributes (fileName); | DWORD attr = GetFileAttributes (fileName); | ||||
| @@ -245012,7 +245070,7 @@ static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPAR | |||||
| return 0; | 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) | static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) | ||||
| { | { | ||||
| @@ -246576,6 +246634,12 @@ void WebBrowserComponent::goForward() | |||||
| browser->browser->GoForward(); | browser->browser->GoForward(); | ||||
| } | } | ||||
| void WebBrowserComponent::refresh() | |||||
| { | |||||
| if (browser->browser != 0) | |||||
| browser->browser->Refresh(); | |||||
| } | |||||
| void WebBrowserComponent::paint (Graphics& g) | void WebBrowserComponent::paint (Graphics& g) | ||||
| { | { | ||||
| if (browser->browser == 0) | if (browser->browser == 0) | ||||
| @@ -259370,6 +259434,10 @@ void WebBrowserComponent::goForward() | |||||
| } | } | ||||
| void WebBrowserComponent::refresh() | |||||
| { | |||||
| } | |||||
| void WebBrowserComponent::paint (Graphics& g) | void WebBrowserComponent::paint (Graphics& g) | ||||
| { | { | ||||
| g.fillAll (Colours::white); | g.fillAll (Colours::white); | ||||
| @@ -265555,6 +265623,9 @@ private: | |||||
| void swapRGBOrder (const int x, const int y, const int w, int h) const | 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()) | jassert (Rectangle (0, 0, juceImage.getWidth(), juceImage.getHeight()) | ||||
| .contains (Rectangle (x, y, w, h))); | .contains (Rectangle (x, y, w, h))); | ||||
| @@ -265567,9 +265638,18 @@ private: | |||||
| for (int i = w; --i >= 0;) | 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[0] = p[2]; | ||||
| p[2] = temp; | |||||
| p[2] = oldp0; | |||||
| #endif | |||||
| p += pixelStride; | p += pixelStride; | ||||
| } | } | ||||
| @@ -269370,6 +269450,11 @@ public: | |||||
| [webView stopLoading: nil]; | [webView stopLoading: nil]; | ||||
| } | } | ||||
| void refresh() | |||||
| { | |||||
| [webView reload: nil]; | |||||
| } | |||||
| private: | private: | ||||
| WebView* webView; | WebView* webView; | ||||
| DownloadClickDetector* clickListener; | DownloadClickDetector* clickListener; | ||||
| @@ -269426,6 +269511,11 @@ void WebBrowserComponent::goForward() | |||||
| browser->goForward(); | browser->goForward(); | ||||
| } | } | ||||
| void WebBrowserComponent::refresh() | |||||
| { | |||||
| browser->refresh(); | |||||
| } | |||||
| void WebBrowserComponent::paint (Graphics& g) | void WebBrowserComponent::paint (Graphics& g) | ||||
| { | { | ||||
| } | } | ||||
| @@ -1535,12 +1535,16 @@ public: | |||||
| /** Returns true if the string contains no characters. | /** Returns true if the string contains no characters. | ||||
| Note that there's also an isNotEmpty() method to help write readable code. | 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; } | inline bool isEmpty() const throw() { return text->text[0] == 0; } | ||||
| /** Returns true if the string contains at least one character. | /** Returns true if the string contains at least one character. | ||||
| Note that there's also an isEmpty() method to help write readable code. | 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; } | inline bool isNotEmpty() const throw() { return text->text[0] != 0; } | ||||
| @@ -1700,6 +1704,15 @@ public: | |||||
| */ | */ | ||||
| bool containsOnly (const tchar* const charactersItMightContain) const throw(); | 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. | /** Returns true if the string matches this simple wildcard expression. | ||||
| So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. | So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. | ||||
| @@ -6534,6 +6547,11 @@ public: | |||||
| int getNumberOfChildFiles (const int whatToLookFor, | int getNumberOfChildFiles (const int whatToLookFor, | ||||
| const String& wildCardPattern = JUCE_T("*")) const throw(); | 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. | /** Creates a stream to read from this file. | ||||
| @returns a stream that will read from this file (initially positioned at the | @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. */ | /** Creates a checksum for a block of binary data. */ | ||||
| MD5 (const char* data, const int numBytes); | 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); | MD5 (const String& text); | ||||
| /** Creates a checksum for the input from a stream. | /** Creates a checksum for the input from a stream. | ||||
| @@ -13751,6 +13776,15 @@ public: | |||||
| */ | */ | ||||
| void setInputSource (InputSource* const newSource) throw(); | 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 | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| @@ -13761,7 +13795,7 @@ private: | |||||
| bool identifierLookupTable [128]; | bool identifierLookupTable [128]; | ||||
| String lastError, dtdText; | String lastError, dtdText; | ||||
| StringArray tokenisedDTD; | StringArray tokenisedDTD; | ||||
| bool needToLoadDTD; | |||||
| bool needToLoadDTD, ignoreEmptyTextElements; | |||||
| InputSource* inputSource; | InputSource* inputSource; | ||||
| void setLastError (const String& desc, const bool carryOn) throw(); | 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 | 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 | 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), | 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); | void setRootItem (TreeViewItem* const newRootItem); | ||||
| @@ -43189,6 +43224,12 @@ public: | |||||
| */ | */ | ||||
| TreeViewItem* getRootItem() const throw() { return rootItem; } | 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. | /** 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 | If the root item is hidden, only its sub-items will be shown in the treeview - this | ||||
| @@ -52820,6 +52861,10 @@ public: | |||||
| */ | */ | ||||
| void goForward(); | void goForward(); | ||||
| /** Refreshes the browser. | |||||
| */ | |||||
| void refresh(); | |||||
| /** This callback is called when the browser is about to navigate | /** This callback is called when the browser is about to navigate | ||||
| to a new location. | to a new location. | ||||
| @@ -416,7 +416,7 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||||
| String thisSubMenuName (sortMethod == sortByCategory ? pd->category | String thisSubMenuName (sortMethod == sortByCategory ? pd->category | ||||
| : pd->manufacturerName); | : pd->manufacturerName); | ||||
| if (thisSubMenuName.trim().isEmpty()) | |||||
| if (! thisSubMenuName.containsNonWhitespaceChars()) | |||||
| thisSubMenuName = T("Other"); | thisSubMenuName = T("Other"); | ||||
| if (thisSubMenuName != lastSubMenuName) | if (thisSubMenuName != lastSubMenuName) | ||||
| @@ -613,9 +613,14 @@ bool Button::keyStateChanged (Component*) | |||||
| updateState (0); | updateState (0); | ||||
| if (isEnabled() && wasDown && ! isKeyDown) | if (isEnabled() && wasDown && ! isKeyDown) | ||||
| { | |||||
| internalClickCallback (ModifierKeys::getCurrentModifiers()); | 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*) | 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) | void TreeView::setRootItemVisible (const bool shouldBeVisible) | ||||
| { | { | ||||
| rootItemVisible = 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 | 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 | 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), | 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); | void setRootItem (TreeViewItem* const newRootItem); | ||||
| @@ -424,6 +425,12 @@ public: | |||||
| */ | */ | ||||
| TreeViewItem* getRootItem() const throw() { return rootItem; } | 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. | /** 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 | 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; | useNativeDialogBox = false; | ||||
| #endif | #endif | ||||
| if (fileFilters.trim().isEmpty()) | |||||
| if (! fileFilters.containsNonWhitespaceChars()) | |||||
| filters = T("*"); | filters = T("*"); | ||||
| } | } | ||||
| @@ -86,6 +86,9 @@ public: | |||||
| */ | */ | ||||
| void goForward(); | void goForward(); | ||||
| /** Refreshes the browser. | |||||
| */ | |||||
| void refresh(); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** This callback is called when the browser is about to navigate | /** 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. */ | /** Creates a checksum for a block of binary data. */ | ||||
| MD5 (const char* data, const int numBytes); | 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); | MD5 (const String& text); | ||||
| /** Creates a checksum for the input from a stream. | /** 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(); | 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() | 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.. | // find child files or directories in this directory first.. | ||||
| if (isDirectory()) | if (isDirectory()) | ||||
| { | { | ||||
| String path (fullPath); | |||||
| if (! path.endsWithChar (separator)) | |||||
| path += separator; | |||||
| const String path (juce_addTrailingSeparator (fullPath)); | |||||
| String filename; | String filename; | ||||
| bool isDirectory, isHidden; | bool isDirectory, isHidden; | ||||
| @@ -760,6 +761,37 @@ int File::getNumberOfChildFiles (const int whatToLookFor, | |||||
| return count; | 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 File File::getNonexistentChildFile (const String& prefix_, | ||||
| const String& suffix, | const String& suffix, | ||||
| @@ -1040,11 +1072,8 @@ const String File::getRelativePathFrom (const File& dir) const throw() | |||||
| thisPath [len] = 0; | 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()); | const int len = jmin (thisPath.length(), dirPath.length()); | ||||
| int commonBitLength = 0; | int commonBitLength = 0; | ||||
| @@ -548,6 +548,11 @@ public: | |||||
| int getNumberOfChildFiles (const int whatToLookFor, | int getNumberOfChildFiles (const int whatToLookFor, | ||||
| const String& wildCardPattern = JUCE_T("*")) const throw(); | 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. | /** 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() | bool String::startsWithChar (const tchar character) const throw() | ||||
| { | { | ||||
| jassert (character != 0); // strings can't contain a null character! | |||||
| return text->text[0] == character; | return text->text[0] == character; | ||||
| } | } | ||||
| bool String::endsWithChar (const tchar character) const throw() | bool String::endsWithChar (const tchar character) const throw() | ||||
| { | { | ||||
| jassert (character != 0); // strings can't contain a null character! | |||||
| return text->text[0] != 0 | return text->text[0] != 0 | ||||
| && text->text [length() - 1] == character; | && text->text [length() - 1] == character; | ||||
| } | } | ||||
| @@ -1838,6 +1842,18 @@ bool String::containsAnyOf (const tchar* const chars) const throw() | |||||
| return false; | 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() | int String::getIntValue() const throw() | ||||
| { | { | ||||
| @@ -176,12 +176,16 @@ public: | |||||
| /** Returns true if the string contains no characters. | /** Returns true if the string contains no characters. | ||||
| Note that there's also an isNotEmpty() method to help write readable code. | 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; } | inline bool isEmpty() const throw() { return text->text[0] == 0; } | ||||
| /** Returns true if the string contains at least one character. | /** Returns true if the string contains at least one character. | ||||
| Note that there's also an isEmpty() method to help write readable code. | 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; } | inline bool isNotEmpty() const throw() { return text->text[0] != 0; } | ||||
| @@ -341,6 +345,15 @@ public: | |||||
| */ | */ | ||||
| bool containsOnly (const tchar* const charactersItMightContain) const throw(); | 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. | /** Returns true if the string matches this simple wildcard expression. | ||||
| So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. | So for example String ("abcdef").matchesWildcard ("*DEF", true) would return true. | ||||
| @@ -271,7 +271,7 @@ void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw( | |||||
| if (removeWhitespaceStrings) | if (removeWhitespaceStrings) | ||||
| { | { | ||||
| for (int i = size(); --i >= 0;) | for (int i = size(); --i >= 0;) | ||||
| if (((const String*) strings.getUnchecked(i))->trim().isEmpty()) | |||||
| if (! ((const String*) strings.getUnchecked(i))->containsNonWhitespaceChars()) | |||||
| remove (i); | remove (i); | ||||
| } | } | ||||
| else | else | ||||
| @@ -54,6 +54,7 @@ static bool isXmlIdentifierChar_Slow (const tchar c) throw() | |||||
| //============================================================================== | //============================================================================== | ||||
| XmlDocument::XmlDocument (const String& documentText) throw() | XmlDocument::XmlDocument (const String& documentText) throw() | ||||
| : originalText (documentText), | : originalText (documentText), | ||||
| ignoreEmptyTextElements (true), | |||||
| inputSource (0) | 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) | XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) | ||||
| { | { | ||||
| String textToParse (originalText); | 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); | e->setText (textElementContent); | ||||
| } | } | ||||
| } | } | ||||
| @@ -122,6 +122,14 @@ public: | |||||
| */ | */ | ||||
| void setInputSource (InputSource* const newSource) throw(); | 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 | juce_UseDebuggingNewOperator | ||||
| @@ -134,7 +142,7 @@ private: | |||||
| bool identifierLookupTable [128]; | bool identifierLookupTable [128]; | ||||
| String lastError, dtdText; | String lastError, dtdText; | ||||
| StringArray tokenisedDTD; | StringArray tokenisedDTD; | ||||
| bool needToLoadDTD; | |||||
| bool needToLoadDTD, ignoreEmptyTextElements; | |||||
| InputSource* inputSource; | InputSource* inputSource; | ||||
| void setLastError (const String& desc, const bool carryOn) throw(); | void setLastError (const String& desc, const bool carryOn) throw(); | ||||
| @@ -63,7 +63,7 @@ XmlElement::XmlElement (const String& tagName_) throw() | |||||
| attributes (0) | attributes (0) | ||||
| { | { | ||||
| // the tag name mustn't be empty, or it'll look like a text element! | // 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() | XmlElement::XmlElement (int /*dummy*/) throw() | ||||
| @@ -55,9 +55,10 @@ static CriticalSection runningThreadsLock; | |||||
| //============================================================================== | //============================================================================== | ||||
| void Thread::threadEntryPoint (Thread* const thread) throw() | void Thread::threadEntryPoint (Thread* const thread) throw() | ||||
| { | { | ||||
| runningThreadsLock.enter(); | |||||
| runningThreads.add (thread); | |||||
| runningThreadsLock.exit(); | |||||
| { | |||||
| const ScopedLock sl (runningThreadsLock); | |||||
| runningThreads.add (thread); | |||||
| } | |||||
| JUCE_TRY | JUCE_TRY | ||||
| { | { | ||||
| @@ -76,10 +77,12 @@ void Thread::threadEntryPoint (Thread* const thread) throw() | |||||
| } | } | ||||
| JUCE_CATCH_ALL_ASSERT | 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 | #if JUCE_WIN32 | ||||
| juce_CloseThreadHandle (thread->threadHandle_); | juce_CloseThreadHandle (thread->threadHandle_); | ||||
| @@ -250,34 +253,28 @@ int Thread::getNumRunningThreads() throw() | |||||
| Thread* Thread::getCurrentThread() throw() | Thread* Thread::getCurrentThread() throw() | ||||
| { | { | ||||
| const ThreadID thisId = getCurrentThreadId(); | const ThreadID thisId = getCurrentThreadId(); | ||||
| Thread* result = 0; | |||||
| runningThreadsLock.enter(); | |||||
| const ScopedLock sl (runningThreadsLock); | |||||
| for (int i = runningThreads.size(); --i >= 0;) | for (int i = runningThreads.size(); --i >= 0;) | ||||
| { | { | ||||
| Thread* const t = (Thread*) (runningThreads.getUnchecked(i)); | Thread* const t = (Thread*) (runningThreads.getUnchecked(i)); | ||||
| if (t->threadId_ == thisId) | if (t->threadId_ == thisId) | ||||
| { | |||||
| result = t; | |||||
| break; | |||||
| } | |||||
| return t; | |||||
| } | } | ||||
| runningThreadsLock.exit(); | |||||
| return result; | |||||
| return 0; | |||||
| } | } | ||||
| void Thread::stopAllThreads (const int timeOutMilliseconds) throw() | 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 (;;) | for (;;) | ||||
| { | { | ||||