mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
This commit is contained in:
parent
d47cf9e8d6
commit
567fe9b1e7
7 changed files with 806 additions and 746 deletions
|
|
@ -104,174 +104,253 @@ 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();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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();
|
||||
|
||||
if (timeOutMs == 0)
|
||||
timeOutTime += 60000;
|
||||
else if (timeOutMs < 0)
|
||||
timeOutTime = 0xffffffff;
|
||||
else
|
||||
timeOutTime += timeOutMs;
|
||||
|
||||
String hostName, hostPath;
|
||||
int hostPort;
|
||||
|
||||
if (! decomposeURL (url, hostName, hostPath, hostPort))
|
||||
return false;
|
||||
|
||||
const struct hostent* host = 0;
|
||||
int port = 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;
|
||||
|
||||
host = gethostbyname ((const char*) proxyName.toUTF8());
|
||||
port = proxyPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
host = gethostbyname ((const char*) hostName.toUTF8());
|
||||
port = hostPort;
|
||||
}
|
||||
|
||||
~JUCE_HTTPSocketStream()
|
||||
{
|
||||
closeSocket();
|
||||
}
|
||||
if (host == 0)
|
||||
return false;
|
||||
|
||||
//==============================================================================
|
||||
bool open (const String& url,
|
||||
const String& headers,
|
||||
const MemoryBlock& postData,
|
||||
const bool isPost,
|
||||
URL::OpenStreamProgressCallback* callback,
|
||||
void* callbackContext,
|
||||
int timeOutMs)
|
||||
{
|
||||
closeSocket();
|
||||
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);
|
||||
|
||||
uint32 timeOutTime = Time::getMillisecondCounter();
|
||||
socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0);
|
||||
|
||||
if (timeOutMs == 0)
|
||||
timeOutTime += 60000;
|
||||
else if (timeOutMs < 0)
|
||||
timeOutTime = 0xffffffff;
|
||||
else
|
||||
timeOutTime += timeOutMs;
|
||||
if (socketHandle == -1)
|
||||
return false;
|
||||
|
||||
String hostName, hostPath;
|
||||
int hostPort;
|
||||
|
||||
if (! decomposeURL (url, hostName, hostPath, hostPort))
|
||||
return false;
|
||||
|
||||
const struct hostent* host = 0;
|
||||
int port = 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;
|
||||
|
||||
host = gethostbyname ((const char*) proxyName.toUTF8());
|
||||
port = proxyPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
host = gethostbyname ((const char*) hostName.toUTF8());
|
||||
port = hostPort;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0);
|
||||
|
||||
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())
|
||||
{
|
||||
//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;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
fd_set readbits;
|
||||
FD_ZERO (&readbits);
|
||||
FD_SET (socketHandle, &readbits);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeoutSeconds;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
|
||||
return 0; // (timeout)
|
||||
|
||||
const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL));
|
||||
readPosition += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int readPosition;
|
||||
|
||||
//==============================================================================
|
||||
juce_UseDebuggingNewOperator
|
||||
|
||||
private:
|
||||
int socketHandle, levelsOfRedirection;
|
||||
const int timeoutSeconds;
|
||||
|
||||
//==============================================================================
|
||||
void closeSocket()
|
||||
{
|
||||
if (socketHandle >= 0)
|
||||
close (socketHandle);
|
||||
|
||||
socketHandle = -1;
|
||||
}
|
||||
|
||||
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 ");
|
||||
|
||||
if (proxyName.isEmpty())
|
||||
{
|
||||
header << hostPath << " HTTP/1.0\r\nHost: "
|
||||
<< hostName << ':' << hostPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
header << originalURL << " HTTP/1.0\r\nHost: "
|
||||
<< proxyName << ':' << proxyPort;
|
||||
}
|
||||
|
||||
header << "\r\nUser-Agent: JUCE/"
|
||||
<< JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION
|
||||
<< "\r\nConnection: Close\r\nContent-Length: "
|
||||
<< postData.getSize() << "\r\n"
|
||||
<< headers << "\r\n";
|
||||
|
||||
MemoryBlock mb;
|
||||
mb.append (header.toUTF8(), (int) strlen (header.toUTF8()));
|
||||
mb.append (postData.getData(), postData.getSize());
|
||||
|
||||
return mb;
|
||||
}
|
||||
|
||||
const String readResponse (const uint32 timeOutTime)
|
||||
{
|
||||
int bytesRead = 0, numConsecutiveLFs = 0;
|
||||
MemoryBlock buffer (1024, true);
|
||||
|
||||
while (numConsecutiveLFs < 2 && bytesRead < 32768
|
||||
&& Time::getMillisecondCounter() <= timeOutTime)
|
||||
{
|
||||
fd_set readbits;
|
||||
FD_ZERO (&readbits);
|
||||
|
|
@ -282,159 +361,80 @@ class JUCE_HTTPSocketStream
|
|||
tv.tv_usec = 0;
|
||||
|
||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
|
||||
return 0; // (timeout)
|
||||
return String::empty; // (timeout)
|
||||
|
||||
const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL));
|
||||
readPosition += bytesRead;
|
||||
return bytesRead;
|
||||
buffer.ensureSize (bytesRead + 8, true);
|
||||
char* const dest = (char*) buffer.getData() + bytesRead;
|
||||
|
||||
if (recv (socketHandle, dest, 1, 0) == -1)
|
||||
return String::empty;
|
||||
|
||||
const char lastByte = *dest;
|
||||
++bytesRead;
|
||||
|
||||
if (lastByte == '\n')
|
||||
++numConsecutiveLFs;
|
||||
else if (lastByte != '\r')
|
||||
numConsecutiveLFs = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int readPosition;
|
||||
const String header (String::fromUTF8 ((const uint8*) buffer.getData()));
|
||||
|
||||
//==============================================================================
|
||||
juce_UseDebuggingNewOperator
|
||||
if (header.startsWithIgnoreCase (T("HTTP/")))
|
||||
return header.trimEnd();
|
||||
|
||||
private:
|
||||
int socketHandle, levelsOfRedirection;
|
||||
const int timeoutSeconds;
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void closeSocket()
|
||||
//==============================================================================
|
||||
static bool decomposeURL (const String& url,
|
||||
String& host, String& path, int& port)
|
||||
{
|
||||
if (! url.startsWithIgnoreCase (T("http://")))
|
||||
return false;
|
||||
|
||||
const int nextSlash = url.indexOfChar (7, '/');
|
||||
int nextColon = url.indexOfChar (7, ':');
|
||||
if (nextColon > nextSlash && nextSlash > 0)
|
||||
nextColon = -1;
|
||||
|
||||
if (nextColon >= 0)
|
||||
{
|
||||
if (socketHandle >= 0)
|
||||
close (socketHandle);
|
||||
|
||||
socketHandle = -1;
|
||||
}
|
||||
|
||||
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 ");
|
||||
|
||||
if (proxyName.isEmpty())
|
||||
{
|
||||
header << hostPath << " HTTP/1.0\r\nHost: "
|
||||
<< hostName << ':' << hostPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
header << originalURL << " HTTP/1.0\r\nHost: "
|
||||
<< proxyName << ':' << proxyPort;
|
||||
}
|
||||
|
||||
header << "\r\nUser-Agent: JUCE/"
|
||||
<< JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION
|
||||
<< "\r\nConnection: Close\r\nContent-Length: "
|
||||
<< postData.getSize() << "\r\n"
|
||||
<< headers << "\r\n";
|
||||
|
||||
MemoryBlock mb;
|
||||
mb.append (header.toUTF8(), (int) strlen (header.toUTF8()));
|
||||
mb.append (postData.getData(), postData.getSize());
|
||||
|
||||
return mb;
|
||||
}
|
||||
|
||||
const String readResponse (const uint32 timeOutTime)
|
||||
{
|
||||
int bytesRead = 0, numConsecutiveLFs = 0;
|
||||
MemoryBlock buffer (1024, true);
|
||||
|
||||
while (numConsecutiveLFs < 2 && bytesRead < 32768
|
||||
&& Time::getMillisecondCounter() <= timeOutTime)
|
||||
{
|
||||
fd_set readbits;
|
||||
FD_ZERO (&readbits);
|
||||
FD_SET (socketHandle, &readbits);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeoutSeconds;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
|
||||
return String::empty; // (timeout)
|
||||
|
||||
buffer.ensureSize (bytesRead + 8, true);
|
||||
char* const dest = (char*) buffer.getData() + bytesRead;
|
||||
|
||||
if (recv (socketHandle, dest, 1, 0) == -1)
|
||||
return String::empty;
|
||||
|
||||
const char lastByte = *dest;
|
||||
++bytesRead;
|
||||
|
||||
if (lastByte == '\n')
|
||||
++numConsecutiveLFs;
|
||||
else if (lastByte != '\r')
|
||||
numConsecutiveLFs = 0;
|
||||
}
|
||||
|
||||
const String header (String::fromUTF8 ((const uint8*) buffer.getData()));
|
||||
|
||||
if (header.startsWithIgnoreCase (T("HTTP/")))
|
||||
return header.trimEnd();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool decomposeURL (const String& url,
|
||||
String& host, String& path, int& port)
|
||||
{
|
||||
if (! url.startsWithIgnoreCase (T("http://")))
|
||||
return false;
|
||||
|
||||
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)
|
||||
port = url.substring (nextColon + 1, nextSlash).getIntValue();
|
||||
else
|
||||
port = url.substring (nextColon + 1).getIntValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 80;
|
||||
|
||||
if (nextSlash >= 0)
|
||||
host = url.substring (7, nextSlash);
|
||||
else
|
||||
host = url.substring (7);
|
||||
}
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
close();
|
||||
// are there are devices that need to be closed before showing their control panel?
|
||||
// close();
|
||||
insideControlPanelModalLoop = true;
|
||||
|
||||
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)
|
||||
{
|
||||
if (handle != 0)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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&);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue