| @@ -104,337 +104,337 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd | |||||
| /** A HTTP input stream that uses sockets. | /** A HTTP input stream that uses sockets. | ||||
| */ | */ | ||||
| class JUCE_HTTPSocketStream | class JUCE_HTTPSocketStream | ||||
| { | |||||
| public: | |||||
| //============================================================================== | |||||
| JUCE_HTTPSocketStream() | |||||
| { | |||||
| public: | |||||
| //============================================================================== | |||||
| JUCE_HTTPSocketStream() | |||||
| : readPosition (0), | : readPosition (0), | ||||
| socketHandle (-1), | |||||
| levelsOfRedirection (0), | |||||
| timeoutSeconds (15) | |||||
| { | |||||
| } | |||||
| socketHandle (-1), | |||||
| levelsOfRedirection (0), | |||||
| timeoutSeconds (15) | |||||
| { | |||||
| } | |||||
| ~JUCE_HTTPSocketStream() | |||||
| { | |||||
| closeSocket(); | |||||
| } | |||||
| ~JUCE_HTTPSocketStream() | |||||
| { | |||||
| closeSocket(); | |||||
| } | |||||
| //============================================================================== | |||||
| bool open (const String& url, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext, | |||||
| int timeOutMs) | |||||
| { | |||||
| closeSocket(); | |||||
| //============================================================================== | |||||
| bool open (const String& url, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext, | |||||
| int timeOutMs) | |||||
| { | |||||
| closeSocket(); | |||||
| uint32 timeOutTime = Time::getMillisecondCounter(); | |||||
| uint32 timeOutTime = Time::getMillisecondCounter(); | |||||
| if (timeOutMs == 0) | |||||
| timeOutTime += 60000; | |||||
| else if (timeOutMs < 0) | |||||
| timeOutTime = 0xffffffff; | |||||
| else | |||||
| timeOutTime += timeOutMs; | |||||
| if (timeOutMs == 0) | |||||
| timeOutTime += 60000; | |||||
| else if (timeOutMs < 0) | |||||
| timeOutTime = 0xffffffff; | |||||
| else | |||||
| timeOutTime += timeOutMs; | |||||
| String hostName, hostPath; | |||||
| int hostPort; | |||||
| String hostName, hostPath; | |||||
| int hostPort; | |||||
| if (! decomposeURL (url, hostName, hostPath, hostPort)) | |||||
| return false; | |||||
| if (! decomposeURL (url, hostName, hostPath, hostPort)) | |||||
| return false; | |||||
| const struct hostent* host = 0; | |||||
| int port = 0; | |||||
| const struct hostent* host = 0; | |||||
| int port = 0; | |||||
| String proxyName, proxyPath; | |||||
| int proxyPort = 0; | |||||
| String proxyName, proxyPath; | |||||
| int proxyPort = 0; | |||||
| String proxyURL (getenv ("http_proxy")); | |||||
| if (proxyURL.startsWithIgnoreCase (T("http://"))) | |||||
| { | |||||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||||
| return false; | |||||
| String proxyURL (getenv ("http_proxy")); | |||||
| if (proxyURL.startsWithIgnoreCase (T("http://"))) | |||||
| { | |||||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||||
| return false; | |||||
| host = gethostbyname ((const char*) proxyName.toUTF8()); | |||||
| port = proxyPort; | |||||
| } | |||||
| else | |||||
| { | |||||
| host = gethostbyname ((const char*) hostName.toUTF8()); | |||||
| port = hostPort; | |||||
| } | |||||
| host = gethostbyname ((const char*) proxyName.toUTF8()); | |||||
| port = proxyPort; | |||||
| } | |||||
| else | |||||
| { | |||||
| host = gethostbyname ((const char*) hostName.toUTF8()); | |||||
| port = hostPort; | |||||
| } | |||||
| if (host == 0) | |||||
| return false; | |||||
| if (host == 0) | |||||
| return false; | |||||
| struct sockaddr_in address; | |||||
| zerostruct (address); | |||||
| memcpy ((void*) &address.sin_addr, (const void*) host->h_addr, host->h_length); | |||||
| address.sin_family = host->h_addrtype; | |||||
| address.sin_port = htons (port); | |||||
| struct sockaddr_in address; | |||||
| zerostruct (address); | |||||
| memcpy ((void*) &address.sin_addr, (const void*) host->h_addr, host->h_length); | |||||
| address.sin_family = host->h_addrtype; | |||||
| address.sin_port = htons (port); | |||||
| socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); | |||||
| socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); | |||||
| if (socketHandle == -1) | |||||
| return false; | |||||
| if (socketHandle == -1) | |||||
| return false; | |||||
| int receiveBufferSize = 16384; | |||||
| setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); | |||||
| setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); | |||||
| int receiveBufferSize = 16384; | |||||
| setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); | |||||
| setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); | |||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); | |||||
| setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); | |||||
| #endif | #endif | ||||
| if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) | |||||
| if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) | |||||
| { | |||||
| closeSocket(); | |||||
| return false; | |||||
| } | |||||
| const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, | |||||
| proxyName, proxyPort, | |||||
| hostPath, url, | |||||
| headers, postData, | |||||
| isPost)); | |||||
| int totalHeaderSent = 0; | |||||
| while (totalHeaderSent < requestHeader.getSize()) | |||||
| { | |||||
| if (Time::getMillisecondCounter() > timeOutTime) | |||||
| { | { | ||||
| closeSocket(); | closeSocket(); | ||||
| return false; | return false; | ||||
| } | } | ||||
| const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, | |||||
| proxyName, proxyPort, | |||||
| hostPath, url, | |||||
| headers, postData, | |||||
| isPost)); | |||||
| int totalHeaderSent = 0; | |||||
| const int numToSend = jmin (1024, requestHeader.getSize() - totalHeaderSent); | |||||
| while (totalHeaderSent < requestHeader.getSize()) | |||||
| if (send (socketHandle, | |||||
| ((const char*) requestHeader.getData()) + totalHeaderSent, | |||||
| numToSend, 0) | |||||
| != numToSend) | |||||
| { | { | ||||
| if (Time::getMillisecondCounter() > timeOutTime) | |||||
| { | |||||
| closeSocket(); | |||||
| return false; | |||||
| } | |||||
| const int numToSend = jmin (1024, requestHeader.getSize() - totalHeaderSent); | |||||
| if (send (socketHandle, | |||||
| ((const char*) requestHeader.getData()) + totalHeaderSent, | |||||
| numToSend, 0) | |||||
| != numToSend) | |||||
| { | |||||
| closeSocket(); | |||||
| return false; | |||||
| } | |||||
| totalHeaderSent += numToSend; | |||||
| if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) | |||||
| { | |||||
| closeSocket(); | |||||
| return false; | |||||
| } | |||||
| closeSocket(); | |||||
| return false; | |||||
| } | } | ||||
| const String responseHeader (readResponse (timeOutTime)); | |||||
| totalHeaderSent += numToSend; | |||||
| if (responseHeader.isNotEmpty()) | |||||
| if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) | |||||
| { | { | ||||
| //DBG (responseHeader); | |||||
| StringArray lines; | |||||
| lines.addLines (responseHeader); | |||||
| // NB - using charToString() here instead of just T(" "), because that was | |||||
| // causing a mysterious gcc internal compiler error... | |||||
| const int statusCode = responseHeader.fromFirstOccurrenceOf (String::charToString (T(' ')), false, false) | |||||
| .substring (0, 3) | |||||
| .getIntValue(); | |||||
| //int contentLength = findHeaderItem (lines, T("Content-Length:")).getIntValue(); | |||||
| //bool isChunked = findHeaderItem (lines, T("Transfer-Encoding:")).equalsIgnoreCase ("chunked"); | |||||
| String location (findHeaderItem (lines, T("Location:"))); | |||||
| if (statusCode >= 300 && statusCode < 400 | |||||
| && location.isNotEmpty()) | |||||
| { | |||||
| if (! location.startsWithIgnoreCase (T("http://"))) | |||||
| location = T("http://") + location; | |||||
| if (levelsOfRedirection++ < 3) | |||||
| return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); | |||||
| } | |||||
| else | |||||
| { | |||||
| levelsOfRedirection = 0; | |||||
| return true; | |||||
| } | |||||
| closeSocket(); | |||||
| return false; | |||||
| } | } | ||||
| closeSocket(); | |||||
| return false; | |||||
| } | } | ||||
| //============================================================================== | |||||
| int read (void* buffer, int bytesToRead) | |||||
| const String responseHeader (readResponse (timeOutTime)); | |||||
| if (responseHeader.isNotEmpty()) | |||||
| { | { | ||||
| fd_set readbits; | |||||
| FD_ZERO (&readbits); | |||||
| FD_SET (socketHandle, &readbits); | |||||
| //DBG (responseHeader); | |||||
| struct timeval tv; | |||||
| tv.tv_sec = timeoutSeconds; | |||||
| tv.tv_usec = 0; | |||||
| StringArray lines; | |||||
| lines.addLines (responseHeader); | |||||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
| return 0; // (timeout) | |||||
| // NB - using charToString() here instead of just T(" "), because that was | |||||
| // causing a mysterious gcc internal compiler error... | |||||
| const int statusCode = responseHeader.fromFirstOccurrenceOf (String::charToString (T(' ')), false, false) | |||||
| .substring (0, 3) | |||||
| .getIntValue(); | |||||
| //int contentLength = findHeaderItem (lines, T("Content-Length:")).getIntValue(); | |||||
| //bool isChunked = findHeaderItem (lines, T("Transfer-Encoding:")).equalsIgnoreCase ("chunked"); | |||||
| const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||||
| readPosition += bytesRead; | |||||
| return bytesRead; | |||||
| String location (findHeaderItem (lines, T("Location:"))); | |||||
| if (statusCode >= 300 && statusCode < 400 | |||||
| && location.isNotEmpty()) | |||||
| { | |||||
| if (! location.startsWithIgnoreCase (T("http://"))) | |||||
| location = T("http://") + location; | |||||
| if (levelsOfRedirection++ < 3) | |||||
| return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); | |||||
| } | |||||
| else | |||||
| { | |||||
| levelsOfRedirection = 0; | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| //============================================================================== | |||||
| int readPosition; | |||||
| closeSocket(); | |||||
| return false; | |||||
| } | |||||
| //============================================================================== | |||||
| int read (void* buffer, int bytesToRead) | |||||
| { | |||||
| fd_set readbits; | |||||
| FD_ZERO (&readbits); | |||||
| FD_SET (socketHandle, &readbits); | |||||
| //============================================================================== | |||||
| juce_UseDebuggingNewOperator | |||||
| struct timeval tv; | |||||
| tv.tv_sec = timeoutSeconds; | |||||
| tv.tv_usec = 0; | |||||
| private: | |||||
| int socketHandle, levelsOfRedirection; | |||||
| const int timeoutSeconds; | |||||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
| return 0; // (timeout) | |||||
| //============================================================================== | |||||
| void closeSocket() | |||||
| { | |||||
| if (socketHandle >= 0) | |||||
| close (socketHandle); | |||||
| const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||||
| readPosition += bytesRead; | |||||
| return bytesRead; | |||||
| } | |||||
| socketHandle = -1; | |||||
| } | |||||
| //============================================================================== | |||||
| int readPosition; | |||||
| const MemoryBlock createRequestHeader (const String& hostName, | |||||
| const int hostPort, | |||||
| const String& proxyName, | |||||
| const int proxyPort, | |||||
| const String& hostPath, | |||||
| const String& originalURL, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost) | |||||
| { | |||||
| String header (isPost ? "POST " : "GET "); | |||||
| //============================================================================== | |||||
| juce_UseDebuggingNewOperator | |||||
| if (proxyName.isEmpty()) | |||||
| { | |||||
| header << hostPath << " HTTP/1.0\r\nHost: " | |||||
| << hostName << ':' << hostPort; | |||||
| } | |||||
| else | |||||
| { | |||||
| header << originalURL << " HTTP/1.0\r\nHost: " | |||||
| << proxyName << ':' << proxyPort; | |||||
| } | |||||
| private: | |||||
| int socketHandle, levelsOfRedirection; | |||||
| const int timeoutSeconds; | |||||
| //============================================================================== | |||||
| void closeSocket() | |||||
| { | |||||
| if (socketHandle >= 0) | |||||
| close (socketHandle); | |||||
| header << "\r\nUser-Agent: JUCE/" | |||||
| << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | |||||
| << "\r\nConnection: Close\r\nContent-Length: " | |||||
| << postData.getSize() << "\r\n" | |||||
| << headers << "\r\n"; | |||||
| socketHandle = -1; | |||||
| } | |||||
| MemoryBlock mb; | |||||
| mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||||
| mb.append (postData.getData(), postData.getSize()); | |||||
| const MemoryBlock createRequestHeader (const String& hostName, | |||||
| const int hostPort, | |||||
| const String& proxyName, | |||||
| const int proxyPort, | |||||
| const String& hostPath, | |||||
| const String& originalURL, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost) | |||||
| { | |||||
| String header (isPost ? "POST " : "GET "); | |||||
| return mb; | |||||
| if (proxyName.isEmpty()) | |||||
| { | |||||
| header << hostPath << " HTTP/1.0\r\nHost: " | |||||
| << hostName << ':' << hostPort; | |||||
| } | } | ||||
| const String readResponse (const uint32 timeOutTime) | |||||
| else | |||||
| { | { | ||||
| int bytesRead = 0, numConsecutiveLFs = 0; | |||||
| MemoryBlock buffer (1024, true); | |||||
| header << originalURL << " HTTP/1.0\r\nHost: " | |||||
| << proxyName << ':' << proxyPort; | |||||
| } | |||||
| while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||||
| && Time::getMillisecondCounter() <= timeOutTime) | |||||
| { | |||||
| fd_set readbits; | |||||
| FD_ZERO (&readbits); | |||||
| FD_SET (socketHandle, &readbits); | |||||
| header << "\r\nUser-Agent: JUCE/" | |||||
| << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | |||||
| << "\r\nConnection: Close\r\nContent-Length: " | |||||
| << postData.getSize() << "\r\n" | |||||
| << headers << "\r\n"; | |||||
| struct timeval tv; | |||||
| tv.tv_sec = timeoutSeconds; | |||||
| tv.tv_usec = 0; | |||||
| MemoryBlock mb; | |||||
| mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||||
| mb.append (postData.getData(), postData.getSize()); | |||||
| return mb; | |||||
| } | |||||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
| return String::empty; // (timeout) | |||||
| const String readResponse (const uint32 timeOutTime) | |||||
| { | |||||
| int bytesRead = 0, numConsecutiveLFs = 0; | |||||
| MemoryBlock buffer (1024, true); | |||||
| buffer.ensureSize (bytesRead + 8, true); | |||||
| char* const dest = (char*) buffer.getData() + bytesRead; | |||||
| while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||||
| && Time::getMillisecondCounter() <= timeOutTime) | |||||
| { | |||||
| fd_set readbits; | |||||
| FD_ZERO (&readbits); | |||||
| FD_SET (socketHandle, &readbits); | |||||
| if (recv (socketHandle, dest, 1, 0) == -1) | |||||
| return String::empty; | |||||
| struct timeval tv; | |||||
| tv.tv_sec = timeoutSeconds; | |||||
| tv.tv_usec = 0; | |||||
| const char lastByte = *dest; | |||||
| ++bytesRead; | |||||
| if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
| return String::empty; // (timeout) | |||||
| if (lastByte == '\n') | |||||
| ++numConsecutiveLFs; | |||||
| else if (lastByte != '\r') | |||||
| numConsecutiveLFs = 0; | |||||
| } | |||||
| buffer.ensureSize (bytesRead + 8, true); | |||||
| char* const dest = (char*) buffer.getData() + bytesRead; | |||||
| const String header (String::fromUTF8 ((const uint8*) buffer.getData())); | |||||
| if (recv (socketHandle, dest, 1, 0) == -1) | |||||
| return String::empty; | |||||
| if (header.startsWithIgnoreCase (T("HTTP/"))) | |||||
| return header.trimEnd(); | |||||
| const char lastByte = *dest; | |||||
| ++bytesRead; | |||||
| return String::empty; | |||||
| if (lastByte == '\n') | |||||
| ++numConsecutiveLFs; | |||||
| else if (lastByte != '\r') | |||||
| numConsecutiveLFs = 0; | |||||
| } | } | ||||
| //============================================================================== | |||||
| static bool decomposeURL (const String& url, | |||||
| String& host, String& path, int& port) | |||||
| { | |||||
| if (! url.startsWithIgnoreCase (T("http://"))) | |||||
| return false; | |||||
| const String header (String::fromUTF8 ((const uint8*) buffer.getData())); | |||||
| const int nextSlash = url.indexOfChar (7, '/'); | |||||
| int nextColon = url.indexOfChar (7, ':'); | |||||
| if (nextColon > nextSlash && nextSlash > 0) | |||||
| nextColon = -1; | |||||
| if (header.startsWithIgnoreCase (T("HTTP/"))) | |||||
| return header.trimEnd(); | |||||
| if (nextColon >= 0) | |||||
| { | |||||
| host = url.substring (7, nextColon); | |||||
| return String::empty; | |||||
| } | |||||
| if (nextSlash >= 0) | |||||
| port = url.substring (nextColon + 1, nextSlash).getIntValue(); | |||||
| else | |||||
| port = url.substring (nextColon + 1).getIntValue(); | |||||
| } | |||||
| else | |||||
| { | |||||
| port = 80; | |||||
| //============================================================================== | |||||
| static bool decomposeURL (const String& url, | |||||
| String& host, String& path, int& port) | |||||
| { | |||||
| if (! url.startsWithIgnoreCase (T("http://"))) | |||||
| return false; | |||||
| if (nextSlash >= 0) | |||||
| host = url.substring (7, nextSlash); | |||||
| else | |||||
| host = url.substring (7); | |||||
| } | |||||
| const int nextSlash = url.indexOfChar (7, '/'); | |||||
| int nextColon = url.indexOfChar (7, ':'); | |||||
| if (nextColon > nextSlash && nextSlash > 0) | |||||
| nextColon = -1; | |||||
| if (nextColon >= 0) | |||||
| { | |||||
| host = url.substring (7, nextColon); | |||||
| if (nextSlash >= 0) | if (nextSlash >= 0) | ||||
| path = url.substring (nextSlash); | |||||
| port = url.substring (nextColon + 1, nextSlash).getIntValue(); | |||||
| else | else | ||||
| path = T("/"); | |||||
| return true; | |||||
| port = url.substring (nextColon + 1).getIntValue(); | |||||
| } | } | ||||
| //============================================================================== | |||||
| static const String findHeaderItem (const StringArray& lines, const String& itemName) | |||||
| else | |||||
| { | { | ||||
| for (int i = 0; i < lines.size(); ++i) | |||||
| if (lines[i].startsWithIgnoreCase (itemName)) | |||||
| return lines[i].substring (itemName.length()).trim(); | |||||
| port = 80; | |||||
| return String::empty; | |||||
| if (nextSlash >= 0) | |||||
| host = url.substring (7, nextSlash); | |||||
| else | |||||
| host = url.substring (7); | |||||
| } | } | ||||
| }; | |||||
| if (nextSlash >= 0) | |||||
| path = url.substring (nextSlash); | |||||
| else | |||||
| path = T("/"); | |||||
| return true; | |||||
| } | |||||
| //============================================================================== | |||||
| static const String findHeaderItem (const StringArray& lines, const String& itemName) | |||||
| { | |||||
| for (int i = 0; i < lines.size(); ++i) | |||||
| if (lines[i].startsWithIgnoreCase (itemName)) | |||||
| return lines[i].substring (itemName.length()).trim(); | |||||
| return String::empty; | |||||
| } | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| bool juce_isOnLine() | bool juce_isOnLine() | ||||
| @@ -478,6 +478,19 @@ int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| int juce_getInternetFileContentLength (void* handle) | |||||
| { | |||||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| //xxx todo | |||||
| jassertfalse | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int juce_seekInInternetFile (void* handle, int newPosition) | int juce_seekInInternetFile (void* handle, int newPosition) | ||||
| { | { | ||||
| JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; | ||||
| @@ -1,451 +1,464 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||||
| Copyright 2004-7 by Raw Material Software ltd. | |||||
| ------------------------------------------------------------------------------ | |||||
| JUCE can be redistributed and/or modified under the terms of the | |||||
| GNU General Public License, as published by the Free Software Foundation; | |||||
| either version 2 of the License, or (at your option) any later version. | |||||
| 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. | |||||
| You should have received a copy of the GNU General Public License | |||||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||||
| Boston, MA 02111-1307 USA | |||||
| ------------------------------------------------------------------------------ | |||||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||||
| more information. | |||||
| ============================================================================== | |||||
| */ | |||||
| // (This file gets included by juce_mac_NativeCode.mm, rather than being | |||||
| // compiled on its own). | |||||
| #if JUCE_INCLUDED_FILE | |||||
| //============================================================================== | |||||
| static bool getEthernetIterator (io_iterator_t* matchingServices) throw() | |||||
| { | |||||
| mach_port_t masterPort; | |||||
| if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) | |||||
| { | |||||
| CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); | |||||
| if (dict != 0) | |||||
| { | |||||
| CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, | |||||
| 0, | |||||
| &kCFTypeDictionaryKeyCallBacks, | |||||
| &kCFTypeDictionaryValueCallBacks); | |||||
| if (propDict != 0) | |||||
| { | |||||
| CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); | |||||
| CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); | |||||
| CFRelease (propDict); | |||||
| } | |||||
| } | |||||
| return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | |||||
| int numResults = 0; | |||||
| io_iterator_t it; | |||||
| if (getEthernetIterator (&it)) | |||||
| { | |||||
| io_object_t i; | |||||
| while ((i = IOIteratorNext (it)) != 0) | |||||
| { | |||||
| io_object_t controller; | |||||
| if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) | |||||
| { | |||||
| CFTypeRef data = IORegistryEntryCreateCFProperty (controller, | |||||
| CFSTR (kIOMACAddress), | |||||
| kCFAllocatorDefault, | |||||
| 0); | |||||
| if (data != 0) | |||||
| { | |||||
| UInt8 addr [kIOEthernetAddressSize]; | |||||
| zeromem (addr, sizeof (addr)); | |||||
| CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); | |||||
| CFRelease (data); | |||||
| int64 a = 0; | |||||
| for (int i = 6; --i >= 0;) | |||||
| a = (a << 8) | addr[i]; | |||||
| if (! littleEndian) | |||||
| a = (int64) swapByteOrder ((uint64) a); | |||||
| if (numResults < maxNum) | |||||
| { | |||||
| *addresses++ = a; | |||||
| ++numResults; | |||||
| } | |||||
| } | |||||
| IOObjectRelease (controller); | |||||
| } | |||||
| IOObjectRelease (i); | |||||
| } | |||||
| IOObjectRelease (it); | |||||
| } | |||||
| return numResults; | |||||
| } | |||||
| //============================================================================== | |||||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
| const String& emailSubject, | |||||
| const String& bodyText, | |||||
| const StringArray& filesToAttach) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| String script; | |||||
| script << "tell application \"Mail\"\r\n" | |||||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||||
| << emailSubject.replace (T("\""), T("\\\"")) | |||||
| << "\", content:\"" | |||||
| << bodyText.replace (T("\""), T("\\\"")) | |||||
| << "\" & return & return}\r\n" | |||||
| "tell newMessage\r\n" | |||||
| "set visible to true\r\n" | |||||
| "set sender to \"sdfsdfsdfewf\"\r\n" | |||||
| "make new to recipient at end of to recipients with properties {address:\"" | |||||
| << targetEmailAddress | |||||
| << "\"}\r\n"; | |||||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||||
| { | |||||
| script << "tell content\r\n" | |||||
| "make new attachment with properties {file name:\"" | |||||
| << filesToAttach[i].replace (T("\""), T("\\\"")) | |||||
| << "\"} at after the last paragraph\r\n" | |||||
| "end tell\r\n"; | |||||
| } | |||||
| script << "end tell\r\n" | |||||
| "end tell\r\n"; | |||||
| NSAppleScript* s = [[NSAppleScript alloc] | |||||
| initWithSource: juceStringToNS (script)]; | |||||
| NSDictionary* error = 0; | |||||
| const bool ok = [s executeAndReturnError: &error] != nil; | |||||
| [s release]; | |||||
| return ok; | |||||
| } | |||||
| //============================================================================== | |||||
| END_JUCE_NAMESPACE | |||||
| using namespace JUCE_NAMESPACE; | |||||
| //============================================================================== | |||||
| #define JuceURLConnection MakeObjCClassName(JuceURLConnection) | |||||
| @interface JuceURLConnection : NSObject | |||||
| { | |||||
| NSURLRequest* request; | |||||
| NSURLConnection* connection; | |||||
| NSMutableData* data; | |||||
| Thread* runLoopThread; | |||||
| bool initialised, hasFailed, hasFinished; | |||||
| int position; | |||||
| NSLock* dataLock; | |||||
| } | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req withCallback: (URL::OpenStreamProgressCallback*) callback withContext: (void*) context; | |||||
| - (void) dealloc; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response; | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data; | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection; | |||||
| - (BOOL) isOpen; | |||||
| - (int) read: (char*) dest numBytes: (int) num; | |||||
| - (int) readPosition; | |||||
| - (void) stop; | |||||
| - (void) createConnection; | |||||
| @end | |||||
| class JuceURLConnectionMessageThread : public Thread | |||||
| { | |||||
| JuceURLConnection* owner; | |||||
| public: | |||||
| JuceURLConnectionMessageThread (JuceURLConnection* owner_) | |||||
| : Thread (T("http connection")), | |||||
| owner (owner_) | |||||
| { | |||||
| startThread(); | |||||
| } | |||||
| ~JuceURLConnectionMessageThread() | |||||
| { | |||||
| stopThread (10000); | |||||
| } | |||||
| void run() | |||||
| { | |||||
| [owner createConnection]; | |||||
| while (! threadShouldExit()) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; | |||||
| } | |||||
| } | |||||
| }; | |||||
| @implementation JuceURLConnection | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req | |||||
| withCallback: (URL::OpenStreamProgressCallback*) callback | |||||
| withContext: (void*) context; | |||||
| { | |||||
| [super init]; | |||||
| request = req; | |||||
| [request retain]; | |||||
| data = [[NSMutableData data] retain]; | |||||
| dataLock = [[NSLock alloc] init]; | |||||
| connection = 0; | |||||
| initialised = false; | |||||
| hasFailed = false; | |||||
| hasFinished = false; | |||||
| runLoopThread = new JuceURLConnectionMessageThread (self); | |||||
| while (runLoopThread->isThreadRunning() && ! initialised) | |||||
| { | |||||
| if (callback != 0) | |||||
| callback (context, -1, [[request HTTPBody] length]); | |||||
| Thread::sleep (1); | |||||
| } | |||||
| return self; | |||||
| } | |||||
| - (void) dealloc | |||||
| { | |||||
| [self stop]; | |||||
| delete runLoopThread; | |||||
| [connection release]; | |||||
| [data release]; | |||||
| [dataLock release]; | |||||
| [request release]; | |||||
| [super dealloc]; | |||||
| } | |||||
| - (void) createConnection | |||||
| { | |||||
| connection = [[NSURLConnection alloc] initWithRequest: request | |||||
| delegate: self]; | |||||
| if (connection == nil) | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data setLength: 0]; | |||||
| [dataLock unlock]; | |||||
| initialised = true; | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error | |||||
| { | |||||
| NSLog ([error description]); | |||||
| hasFailed = true; | |||||
| initialised = true; | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) newData | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data appendData: newData]; | |||||
| [dataLock unlock]; | |||||
| initialised = true; | |||||
| } | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection | |||||
| { | |||||
| hasFinished = true; | |||||
| initialised = true; | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (BOOL) isOpen | |||||
| { | |||||
| return connection != 0 && ! hasFailed; | |||||
| } | |||||
| - (int) readPosition | |||||
| { | |||||
| return position; | |||||
| } | |||||
| - (int) read: (char*) dest numBytes: (int) numNeeded | |||||
| { | |||||
| int numDone = 0; | |||||
| while (numNeeded > 0) | |||||
| { | |||||
| int available = jmin (numNeeded, [data length]); | |||||
| if (available > 0) | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data getBytes: dest length: available]; | |||||
| [data replaceBytesInRange: NSMakeRange (0, available) withBytes: nil length: 0]; | |||||
| [dataLock unlock]; | |||||
| numDone += available; | |||||
| numNeeded -= available; | |||||
| dest += available; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (hasFailed || hasFinished) | |||||
| break; | |||||
| Thread::sleep (1); | |||||
| } | |||||
| } | |||||
| position += numDone; | |||||
| return numDone; | |||||
| } | |||||
| - (void) stop | |||||
| { | |||||
| [connection cancel]; | |||||
| runLoopThread->stopThread (10000); | |||||
| } | |||||
| @end | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| bool juce_isOnLine() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| void* juce_openInternetFile (const String& url, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext, | |||||
| int timeOutMs) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| NSMutableURLRequest* req = [NSMutableURLRequest | |||||
| requestWithURL: [NSURL URLWithString: juceStringToNS (url)] | |||||
| cachePolicy: NSURLRequestUseProtocolCachePolicy | |||||
| timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; | |||||
| if (req == nil) | |||||
| return 0; | |||||
| [req setHTTPMethod: isPost ? @"POST" : @"GET"]; | |||||
| //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; | |||||
| StringArray headerLines; | |||||
| headerLines.addLines (headers); | |||||
| headerLines.removeEmptyStrings (true); | |||||
| for (int i = 0; i < headerLines.size(); ++i) | |||||
| { | |||||
| const String key (headerLines[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| const String value (headerLines[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| if (key.isNotEmpty() && value.isNotEmpty()) | |||||
| [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; | |||||
| } | |||||
| if (isPost && postData.getSize() > 0) | |||||
| { | |||||
| [req setHTTPBody: [NSData dataWithBytes: postData.getData() | |||||
| length: postData.getSize()]]; | |||||
| } | |||||
| JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req | |||||
| withCallback: callback | |||||
| withContext: callbackContext]; | |||||
| if ([s isOpen]) | |||||
| return s; | |||||
| [s release]; | |||||
| return 0; | |||||
| } | |||||
| void juce_closeInternetFile (void* handle) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| [s stop]; | |||||
| [s release]; | |||||
| } | |||||
| } | |||||
| int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| return [s read: (char*) buffer numBytes: bytesToRead]; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int juce_seekInInternetFile (void* handle, int newPosition) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| return [s readPosition]; | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||||
| Copyright 2004-7 by Raw Material Software ltd. | |||||
| ------------------------------------------------------------------------------ | |||||
| JUCE can be redistributed and/or modified under the terms of the | |||||
| GNU General Public License, as published by the Free Software Foundation; | |||||
| either version 2 of the License, or (at your option) any later version. | |||||
| 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. | |||||
| You should have received a copy of the GNU General Public License | |||||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||||
| Boston, MA 02111-1307 USA | |||||
| ------------------------------------------------------------------------------ | |||||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||||
| more information. | |||||
| ============================================================================== | |||||
| */ | |||||
| // (This file gets included by juce_mac_NativeCode.mm, rather than being | |||||
| // compiled on its own). | |||||
| #if JUCE_INCLUDED_FILE | |||||
| //============================================================================== | |||||
| static bool getEthernetIterator (io_iterator_t* matchingServices) throw() | |||||
| { | |||||
| mach_port_t masterPort; | |||||
| if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) | |||||
| { | |||||
| CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); | |||||
| if (dict != 0) | |||||
| { | |||||
| CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, | |||||
| 0, | |||||
| &kCFTypeDictionaryKeyCallBacks, | |||||
| &kCFTypeDictionaryValueCallBacks); | |||||
| if (propDict != 0) | |||||
| { | |||||
| CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); | |||||
| CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); | |||||
| CFRelease (propDict); | |||||
| } | |||||
| } | |||||
| return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | |||||
| int numResults = 0; | |||||
| io_iterator_t it; | |||||
| if (getEthernetIterator (&it)) | |||||
| { | |||||
| io_object_t i; | |||||
| while ((i = IOIteratorNext (it)) != 0) | |||||
| { | |||||
| io_object_t controller; | |||||
| if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) | |||||
| { | |||||
| CFTypeRef data = IORegistryEntryCreateCFProperty (controller, | |||||
| CFSTR (kIOMACAddress), | |||||
| kCFAllocatorDefault, | |||||
| 0); | |||||
| if (data != 0) | |||||
| { | |||||
| UInt8 addr [kIOEthernetAddressSize]; | |||||
| zeromem (addr, sizeof (addr)); | |||||
| CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); | |||||
| CFRelease (data); | |||||
| int64 a = 0; | |||||
| for (int i = 6; --i >= 0;) | |||||
| a = (a << 8) | addr[i]; | |||||
| if (! littleEndian) | |||||
| a = (int64) swapByteOrder ((uint64) a); | |||||
| if (numResults < maxNum) | |||||
| { | |||||
| *addresses++ = a; | |||||
| ++numResults; | |||||
| } | |||||
| } | |||||
| IOObjectRelease (controller); | |||||
| } | |||||
| IOObjectRelease (i); | |||||
| } | |||||
| IOObjectRelease (it); | |||||
| } | |||||
| return numResults; | |||||
| } | |||||
| //============================================================================== | |||||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
| const String& emailSubject, | |||||
| const String& bodyText, | |||||
| const StringArray& filesToAttach) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| String script; | |||||
| script << "tell application \"Mail\"\r\n" | |||||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||||
| << emailSubject.replace (T("\""), T("\\\"")) | |||||
| << "\", content:\"" | |||||
| << bodyText.replace (T("\""), T("\\\"")) | |||||
| << "\" & return & return}\r\n" | |||||
| "tell newMessage\r\n" | |||||
| "set visible to true\r\n" | |||||
| "set sender to \"sdfsdfsdfewf\"\r\n" | |||||
| "make new to recipient at end of to recipients with properties {address:\"" | |||||
| << targetEmailAddress | |||||
| << "\"}\r\n"; | |||||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||||
| { | |||||
| script << "tell content\r\n" | |||||
| "make new attachment with properties {file name:\"" | |||||
| << filesToAttach[i].replace (T("\""), T("\\\"")) | |||||
| << "\"} at after the last paragraph\r\n" | |||||
| "end tell\r\n"; | |||||
| } | |||||
| script << "end tell\r\n" | |||||
| "end tell\r\n"; | |||||
| NSAppleScript* s = [[NSAppleScript alloc] | |||||
| initWithSource: juceStringToNS (script)]; | |||||
| NSDictionary* error = 0; | |||||
| const bool ok = [s executeAndReturnError: &error] != nil; | |||||
| [s release]; | |||||
| return ok; | |||||
| } | |||||
| //============================================================================== | |||||
| END_JUCE_NAMESPACE | |||||
| using namespace JUCE_NAMESPACE; | |||||
| //============================================================================== | |||||
| #define JuceURLConnection MakeObjCClassName(JuceURLConnection) | |||||
| @interface JuceURLConnection : NSObject | |||||
| { | |||||
| NSURLRequest* request; | |||||
| NSURLConnection* connection; | |||||
| NSMutableData* data; | |||||
| Thread* runLoopThread; | |||||
| bool initialised, hasFailed, hasFinished; | |||||
| int position; | |||||
| NSLock* dataLock; | |||||
| } | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req withCallback: (URL::OpenStreamProgressCallback*) callback withContext: (void*) context; | |||||
| - (void) dealloc; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response; | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data; | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection; | |||||
| - (BOOL) isOpen; | |||||
| - (int) read: (char*) dest numBytes: (int) num; | |||||
| - (int) readPosition; | |||||
| - (void) stop; | |||||
| - (void) createConnection; | |||||
| @end | |||||
| class JuceURLConnectionMessageThread : public Thread | |||||
| { | |||||
| JuceURLConnection* owner; | |||||
| public: | |||||
| JuceURLConnectionMessageThread (JuceURLConnection* owner_) | |||||
| : Thread (T("http connection")), | |||||
| owner (owner_) | |||||
| { | |||||
| startThread(); | |||||
| } | |||||
| ~JuceURLConnectionMessageThread() | |||||
| { | |||||
| stopThread (10000); | |||||
| } | |||||
| void run() | |||||
| { | |||||
| [owner createConnection]; | |||||
| while (! threadShouldExit()) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; | |||||
| } | |||||
| } | |||||
| }; | |||||
| @implementation JuceURLConnection | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req | |||||
| withCallback: (URL::OpenStreamProgressCallback*) callback | |||||
| withContext: (void*) context; | |||||
| { | |||||
| [super init]; | |||||
| request = req; | |||||
| [request retain]; | |||||
| data = [[NSMutableData data] retain]; | |||||
| dataLock = [[NSLock alloc] init]; | |||||
| connection = 0; | |||||
| initialised = false; | |||||
| hasFailed = false; | |||||
| hasFinished = false; | |||||
| runLoopThread = new JuceURLConnectionMessageThread (self); | |||||
| while (runLoopThread->isThreadRunning() && ! initialised) | |||||
| { | |||||
| if (callback != 0) | |||||
| callback (context, -1, [[request HTTPBody] length]); | |||||
| Thread::sleep (1); | |||||
| } | |||||
| return self; | |||||
| } | |||||
| - (void) dealloc | |||||
| { | |||||
| [self stop]; | |||||
| delete runLoopThread; | |||||
| [connection release]; | |||||
| [data release]; | |||||
| [dataLock release]; | |||||
| [request release]; | |||||
| [super dealloc]; | |||||
| } | |||||
| - (void) createConnection | |||||
| { | |||||
| connection = [[NSURLConnection alloc] initWithRequest: request | |||||
| delegate: self]; | |||||
| if (connection == nil) | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data setLength: 0]; | |||||
| [dataLock unlock]; | |||||
| initialised = true; | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error | |||||
| { | |||||
| NSLog ([error description]); | |||||
| hasFailed = true; | |||||
| initialised = true; | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) newData | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data appendData: newData]; | |||||
| [dataLock unlock]; | |||||
| initialised = true; | |||||
| } | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection | |||||
| { | |||||
| hasFinished = true; | |||||
| initialised = true; | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (BOOL) isOpen | |||||
| { | |||||
| return connection != 0 && ! hasFailed; | |||||
| } | |||||
| - (int) readPosition | |||||
| { | |||||
| return position; | |||||
| } | |||||
| - (int) read: (char*) dest numBytes: (int) numNeeded | |||||
| { | |||||
| int numDone = 0; | |||||
| while (numNeeded > 0) | |||||
| { | |||||
| int available = jmin (numNeeded, [data length]); | |||||
| if (available > 0) | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data getBytes: dest length: available]; | |||||
| [data replaceBytesInRange: NSMakeRange (0, available) withBytes: nil length: 0]; | |||||
| [dataLock unlock]; | |||||
| numDone += available; | |||||
| numNeeded -= available; | |||||
| dest += available; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (hasFailed || hasFinished) | |||||
| break; | |||||
| Thread::sleep (1); | |||||
| } | |||||
| } | |||||
| position += numDone; | |||||
| return numDone; | |||||
| } | |||||
| - (void) stop | |||||
| { | |||||
| [connection cancel]; | |||||
| runLoopThread->stopThread (10000); | |||||
| } | |||||
| @end | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| bool juce_isOnLine() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| void* juce_openInternetFile (const String& url, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext, | |||||
| int timeOutMs) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| NSMutableURLRequest* req = [NSMutableURLRequest | |||||
| requestWithURL: [NSURL URLWithString: juceStringToNS (url)] | |||||
| cachePolicy: NSURLRequestUseProtocolCachePolicy | |||||
| timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; | |||||
| if (req == nil) | |||||
| return 0; | |||||
| [req setHTTPMethod: isPost ? @"POST" : @"GET"]; | |||||
| //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; | |||||
| StringArray headerLines; | |||||
| headerLines.addLines (headers); | |||||
| headerLines.removeEmptyStrings (true); | |||||
| for (int i = 0; i < headerLines.size(); ++i) | |||||
| { | |||||
| const String key (headerLines[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| const String value (headerLines[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| if (key.isNotEmpty() && value.isNotEmpty()) | |||||
| [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; | |||||
| } | |||||
| if (isPost && postData.getSize() > 0) | |||||
| { | |||||
| [req setHTTPBody: [NSData dataWithBytes: postData.getData() | |||||
| length: postData.getSize()]]; | |||||
| } | |||||
| JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req | |||||
| withCallback: callback | |||||
| withContext: callbackContext]; | |||||
| if ([s isOpen]) | |||||
| return s; | |||||
| [s release]; | |||||
| return 0; | |||||
| } | |||||
| void juce_closeInternetFile (void* handle) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| [s stop]; | |||||
| [s release]; | |||||
| } | |||||
| } | |||||
| int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| const ScopedAutoReleasePool pool; | |||||
| return [s read: (char*) buffer numBytes: bytesToRead]; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int juce_getInternetFileContentLength (void* handle) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| //xxx todo | |||||
| jassertfalse | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int juce_seekInInternetFile (void* handle, int newPosition) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| return [s readPosition]; | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| @@ -738,7 +738,8 @@ public: | |||||
| JUCE_TRY | JUCE_TRY | ||||
| { | { | ||||
| close(); | |||||
| // are there are devices that need to be closed before showing their control panel? | |||||
| // close(); | |||||
| insideControlPanelModalLoop = true; | insideControlPanelModalLoop = true; | ||||
| const uint32 started = Time::getMillisecondCounter(); | const uint32 started = Time::getMillisecondCounter(); | ||||
| @@ -217,6 +217,26 @@ int juce_seekInInternetFile (void* handle, int newPosition) | |||||
| } | } | ||||
| } | } | ||||
| int juce_getInternetFileContentLength (void* handle) | |||||
| { | |||||
| DWORD result = 0; | |||||
| const ConnectionAndRequestStruct* const crs = (const ConnectionAndRequestStruct*) handle; | |||||
| if (crs != 0) | |||||
| { | |||||
| DWORD index = 0; | |||||
| DWORD size = sizeof (result); | |||||
| HttpQueryInfo (crs->request, | |||||
| HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, | |||||
| &result, | |||||
| &size, | |||||
| &index); | |||||
| } | |||||
| return (int) result; | |||||
| } | |||||
| void juce_closeInternetFile (void* handle) | void juce_closeInternetFile (void* handle) | ||||
| { | { | ||||
| if (handle != 0) | if (handle != 0) | ||||
| @@ -80,14 +80,15 @@ void Label::setText (const String& newText, | |||||
| if (text != newText) | if (text != newText) | ||||
| { | { | ||||
| text = newText; | text = newText; | ||||
| if (broadcastChangeMessage) | |||||
| triggerAsyncUpdate(); | |||||
| repaint(); | repaint(); | ||||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | ||||
| { | |||||
| componentMovedOrResized (*ownerComponent, true, true); | componentMovedOrResized (*ownerComponent, true, true); | ||||
| if (broadcastChangeMessage) | |||||
| callChangeListeners(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -220,8 +221,6 @@ bool Label::updateFromTextEditorContents() | |||||
| if (text != newText) | if (text != newText) | ||||
| { | { | ||||
| text = newText; | text = newText; | ||||
| triggerAsyncUpdate(); | |||||
| repaint(); | repaint(); | ||||
| if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) | ||||
| @@ -247,6 +246,9 @@ void Label::hideEditor (const bool discardCurrentEditorContents) | |||||
| textWasEdited(); | textWasEdited(); | ||||
| exitModalState (0); | exitModalState (0); | ||||
| if (changed && isValidComponent()) | |||||
| callChangeListeners(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -332,6 +334,15 @@ void Label::colourChanged() | |||||
| repaint(); | repaint(); | ||||
| } | } | ||||
| void Label::setMinimumHorizontalScale (const float newScale) | |||||
| { | |||||
| if (minimumHorizontalScale != newScale) | |||||
| { | |||||
| minimumHorizontalScale = newScale; | |||||
| repaint(); | |||||
| } | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| // We'll use a custom focus traverser here to make sure focus goes from the | // We'll use a custom focus traverser here to make sure focus goes from the | ||||
| // text editor to another component rather than back to the label itself. | // text editor to another component rather than back to the label itself. | ||||
| @@ -371,7 +382,7 @@ void Label::removeListener (LabelListener* const listener) throw() | |||||
| listeners.removeValue (listener); | listeners.removeValue (listener); | ||||
| } | } | ||||
| void Label::handleAsyncUpdate() | |||||
| void Label::callChangeListeners() | |||||
| { | { | ||||
| for (int i = listeners.size(); --i >= 0;) | for (int i = listeners.size(); --i >= 0;) | ||||
| { | { | ||||
| @@ -408,7 +419,12 @@ void Label::textEditorReturnKeyPressed (TextEditor& ed) | |||||
| hideEditor (true); | hideEditor (true); | ||||
| if (changed) | if (changed) | ||||
| { | |||||
| textWasEdited(); | textWasEdited(); | ||||
| if (isValidComponent()) | |||||
| callChangeListeners(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -33,7 +33,6 @@ | |||||
| #define __JUCE_LABEL_JUCEHEADER__ | #define __JUCE_LABEL_JUCEHEADER__ | ||||
| #include "../juce_ComponentDeletionWatcher.h" | #include "../juce_ComponentDeletionWatcher.h" | ||||
| #include "../../../events/juce_AsyncUpdater.h" | |||||
| #include "juce_TextEditor.h" | #include "juce_TextEditor.h" | ||||
| class Label; | class Label; | ||||
| @@ -69,8 +68,7 @@ public: | |||||
| class JUCE_API Label : public Component, | class JUCE_API Label : public Component, | ||||
| public SettableTooltipClient, | public SettableTooltipClient, | ||||
| protected TextEditorListener, | protected TextEditorListener, | ||||
| private ComponentListener, | |||||
| private AsyncUpdater | |||||
| private ComponentListener | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -295,8 +293,6 @@ protected: | |||||
| /** @internal */ | /** @internal */ | ||||
| void textEditorFocusLost (TextEditor& editor); | void textEditorFocusLost (TextEditor& editor); | ||||
| /** @internal */ | /** @internal */ | ||||
| void handleAsyncUpdate(); | |||||
| /** @internal */ | |||||
| void colourChanged(); | void colourChanged(); | ||||
| /** Creates the TextEditor component that will be used when the user has clicked on the label. | /** Creates the TextEditor component that will be used when the user has clicked on the label. | ||||
| @@ -325,6 +321,7 @@ private: | |||||
| bool leftOfOwnerComp : 1; | bool leftOfOwnerComp : 1; | ||||
| bool updateFromTextEditorContents(); | bool updateFromTextEditorContents(); | ||||
| void callChangeListeners(); | |||||
| Label (const Label&); | Label (const Label&); | ||||
| const Label& operator= (const Label&); | const Label& operator= (const Label&); | ||||
| @@ -547,7 +547,7 @@ void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, | |||||
| Path p; | Path p; | ||||
| for (float x = (float) (-stripeWidth - position); x < width + stripeWidth; x += stripeWidth) | |||||
| for (float x = (float) (- position); x < width + stripeWidth; x += stripeWidth) | |||||
| p.addQuadrilateral (x, 0.0f, | p.addQuadrilateral (x, 0.0f, | ||||
| x + stripeWidth * 0.5f, 0.0f, | x + stripeWidth * 0.5f, 0.0f, | ||||
| x, (float) height, | x, (float) height, | ||||