diff --git a/build/linux/platform_specific_code/juce_linux_Network.cpp b/build/linux/platform_specific_code/juce_linux_Network.cpp index 1fa968902b..b52f4d27c4 100644 --- a/build/linux/platform_specific_code/juce_linux_Network.cpp +++ b/build/linux/platform_specific_code/juce_linux_Network.cpp @@ -104,337 +104,337 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd /** A HTTP input stream that uses sockets. */ class JUCE_HTTPSocketStream - { - public: - //============================================================================== - JUCE_HTTPSocketStream() +{ +public: + //============================================================================== + JUCE_HTTPSocketStream() : 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 - setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); + setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); #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(); 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) - path = url.substring (nextSlash); + port = url.substring (nextColon + 1, nextSlash).getIntValue(); 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() @@ -478,6 +478,19 @@ int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) 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) { JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle; diff --git a/build/macosx/platform_specific_code/juce_mac_Network.mm b/build/macosx/platform_specific_code/juce_mac_Network.mm index 51a9dca195..cad8219d11 100644 --- a/build/macosx/platform_specific_code/juce_mac_Network.mm +++ b/build/macosx/platform_specific_code/juce_mac_Network.mm @@ -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 diff --git a/build/win32/platform_specific_code/juce_win32_ASIO.cpp b/build/win32/platform_specific_code/juce_win32_ASIO.cpp index eee14d1fac..a39886c07f 100644 --- a/build/win32/platform_specific_code/juce_win32_ASIO.cpp +++ b/build/win32/platform_specific_code/juce_win32_ASIO.cpp @@ -738,7 +738,8 @@ public: JUCE_TRY { - close(); + // are there are devices that need to be closed before showing their control panel? + // close(); insideControlPanelModalLoop = true; const uint32 started = Time::getMillisecondCounter(); diff --git a/build/win32/platform_specific_code/juce_win32_Network.cpp b/build/win32/platform_specific_code/juce_win32_Network.cpp index d6e3853d1d..0568c70724 100644 --- a/build/win32/platform_specific_code/juce_win32_Network.cpp +++ b/build/win32/platform_specific_code/juce_win32_Network.cpp @@ -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) { if (handle != 0) diff --git a/src/juce_appframework/gui/components/controls/juce_Label.cpp b/src/juce_appframework/gui/components/controls/juce_Label.cpp index 3e47cddb44..81c831ba3f 100644 --- a/src/juce_appframework/gui/components/controls/juce_Label.cpp +++ b/src/juce_appframework/gui/components/controls/juce_Label.cpp @@ -80,14 +80,15 @@ void Label::setText (const String& newText, if (text != newText) { text = newText; - - if (broadcastChangeMessage) - triggerAsyncUpdate(); - repaint(); if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) + { componentMovedOrResized (*ownerComponent, true, true); + + if (broadcastChangeMessage) + callChangeListeners(); + } } } @@ -220,8 +221,6 @@ bool Label::updateFromTextEditorContents() if (text != newText) { text = newText; - - triggerAsyncUpdate(); repaint(); if (ownerComponent != 0 && ! deletionWatcher->hasBeenDeleted()) @@ -247,6 +246,9 @@ void Label::hideEditor (const bool discardCurrentEditorContents) textWasEdited(); exitModalState (0); + + if (changed && isValidComponent()) + callChangeListeners(); } } @@ -332,6 +334,15 @@ void Label::colourChanged() 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 // 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); } -void Label::handleAsyncUpdate() +void Label::callChangeListeners() { for (int i = listeners.size(); --i >= 0;) { @@ -408,7 +419,12 @@ void Label::textEditorReturnKeyPressed (TextEditor& ed) hideEditor (true); if (changed) + { textWasEdited(); + + if (isValidComponent()) + callChangeListeners(); + } } } diff --git a/src/juce_appframework/gui/components/controls/juce_Label.h b/src/juce_appframework/gui/components/controls/juce_Label.h index b6ccd3b791..73f9596869 100644 --- a/src/juce_appframework/gui/components/controls/juce_Label.h +++ b/src/juce_appframework/gui/components/controls/juce_Label.h @@ -33,7 +33,6 @@ #define __JUCE_LABEL_JUCEHEADER__ #include "../juce_ComponentDeletionWatcher.h" -#include "../../../events/juce_AsyncUpdater.h" #include "juce_TextEditor.h" class Label; @@ -69,8 +68,7 @@ public: class JUCE_API Label : public Component, public SettableTooltipClient, protected TextEditorListener, - private ComponentListener, - private AsyncUpdater + private ComponentListener { public: //============================================================================== @@ -295,8 +293,6 @@ protected: /** @internal */ void textEditorFocusLost (TextEditor& editor); /** @internal */ - void handleAsyncUpdate(); - /** @internal */ void colourChanged(); /** 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 updateFromTextEditorContents(); + void callChangeListeners(); Label (const Label&); const Label& operator= (const Label&); diff --git a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp index d7dcf14ef9..30f0968fd2 100644 --- a/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp +++ b/src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.cpp @@ -547,7 +547,7 @@ void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, 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, x + stripeWidth * 0.5f, 0.0f, x, (float) height,