| @@ -21,7 +21,7 @@ package.config["Debug"].defines = { "LINUX=1", "DEBUG=1", "_DEBUG=1" }; | |||||
| package.config["Debug"].buildoptions = { "-D_DEBUG -ggdb -Wall" } | package.config["Debug"].buildoptions = { "-D_DEBUG -ggdb -Wall" } | ||||
| package.config["Release"].defines = { "LINUX=1", "NDEBUG=1" }; | package.config["Release"].defines = { "LINUX=1", "NDEBUG=1" }; | ||||
| package.config["Release"].buildoptions = { "-Wall" } | |||||
| package.config["Release"].buildoptions = { "-O2 -Wall -fvisibility=hidden" } | |||||
| package.includepaths = { | package.includepaths = { | ||||
| @@ -44,16 +44,30 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "../../../src/juce_core/text/juce_StringArray.h" | #include "../../../src/juce_core/text/juce_StringArray.h" | ||||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | #include "../../../src/juce_core/basics/juce_SystemStats.h" | ||||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | ||||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||||
| // we'll borrow the mac's socket-based http streaming code.. | // we'll borrow the mac's socket-based http streaming code.. | ||||
| #include "../../macosx/platform_specific_code/juce_mac_HTTPStream.h" | #include "../../macosx/platform_specific_code/juce_mac_HTTPStream.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum) throw() | |||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | { | ||||
| // xxx todo | // xxx todo | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
| const String& emailSubject, | |||||
| const String& bodyText, | |||||
| const StringArray& filesToAttach) | |||||
| { | |||||
| jassertfalse // xxx todo | |||||
| return false; | |||||
| } | |||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -471,38 +471,38 @@ void juce_createDirectory (const String& fileName) throw() | |||||
| void* juce_fileOpen (const String& fileName, bool forWriting) throw() | void* juce_fileOpen (const String& fileName, bool forWriting) throw() | ||||
| { | { | ||||
| const char* const fileNameUTF8 = fileName.toUTF8(); | const char* const fileNameUTF8 = fileName.toUTF8(); | ||||
| const char* mode = "rb"; | |||||
| int flags = O_RDONLY; | |||||
| if (forWriting) | if (forWriting) | ||||
| { | { | ||||
| if (juce_fileExists (fileName, false)) | if (juce_fileExists (fileName, false)) | ||||
| { | { | ||||
| FILE* const f = fopen (fileNameUTF8, "r+b"); | |||||
| const int f = open (fileNameUTF8, O_RDWR, 00644); | |||||
| if (f != 0) | |||||
| fseek (f, 0, SEEK_END); | |||||
| if (f != -1) | |||||
| lseek (f, 0, SEEK_END); | |||||
| return (void*) f; | return (void*) f; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| mode = "w+b"; | |||||
| flags = O_RDWR + O_CREAT; | |||||
| } | } | ||||
| } | } | ||||
| return (void*) fopen (fileNameUTF8, mode); | |||||
| return (void*) open (fileNameUTF8, flags, 00644); | |||||
| } | } | ||||
| void juce_fileClose (void* handle) throw() | void juce_fileClose (void* handle) throw() | ||||
| { | { | ||||
| if (handle != 0) | if (handle != 0) | ||||
| fclose ((FILE*) handle); | |||||
| close ((int) handle); | |||||
| } | } | ||||
| int juce_fileRead (void* handle, void* buffer, int size) throw() | int juce_fileRead (void* handle, void* buffer, int size) throw() | ||||
| { | { | ||||
| if (handle != 0) | if (handle != 0) | ||||
| return fread (buffer, 1, size, (FILE*) handle); | |||||
| return read ((int) handle, buffer, size); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -510,14 +510,14 @@ int juce_fileRead (void* handle, void* buffer, int size) throw() | |||||
| int juce_fileWrite (void* handle, const void* buffer, int size) throw() | int juce_fileWrite (void* handle, const void* buffer, int size) throw() | ||||
| { | { | ||||
| if (handle != 0) | if (handle != 0) | ||||
| return fwrite (buffer, 1, size, (FILE*) handle); | |||||
| return write ((int) handle, buffer, size); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| int64 juce_fileSetPosition (void* handle, int64 pos) throw() | int64 juce_fileSetPosition (void* handle, int64 pos) throw() | ||||
| { | { | ||||
| if (handle != 0 && fseek ((FILE*) handle, pos, SEEK_SET) == 0) | |||||
| if (handle != 0 && lseek ((int) handle, pos, SEEK_SET) == pos) | |||||
| return pos; | return pos; | ||||
| return -1; | return -1; | ||||
| @@ -526,7 +526,7 @@ int64 juce_fileSetPosition (void* handle, int64 pos) throw() | |||||
| int64 juce_fileGetPosition (void* handle) throw() | int64 juce_fileGetPosition (void* handle) throw() | ||||
| { | { | ||||
| if (handle != 0) | if (handle != 0) | ||||
| return ftell ((FILE*) handle); | |||||
| return lseek ((int) handle, 0, SEEK_CUR); | |||||
| else | else | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| @@ -534,7 +534,7 @@ int64 juce_fileGetPosition (void* handle) throw() | |||||
| void juce_fileFlush (void* handle) throw() | void juce_fileFlush (void* handle) throw() | ||||
| { | { | ||||
| if (handle != 0) | if (handle != 0) | ||||
| fflush ((FILE*) handle); | |||||
| fsync ((int) handle); | |||||
| } | } | ||||
| const StringArray juce_getFileSystemRoots() throw() | const StringArray juce_getFileSystemRoots() throw() | ||||
| @@ -58,7 +58,8 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| bool open (const String& url, | bool open (const String& url, | ||||
| const String& optionalPostText, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost) | const bool isPost) | ||||
| { | { | ||||
| closeSocket(); | closeSocket(); | ||||
| @@ -101,15 +102,14 @@ public: | |||||
| if (! proxyURL.startsWithIgnoreCase (T("http://"))) | if (! proxyURL.startsWithIgnoreCase (T("http://"))) | ||||
| proxyURL = String::empty; | proxyURL = String::empty; | ||||
| const String requestHeader (createRequestHeader (hostName, hostPath, | |||||
| proxyURL, url, | |||||
| hostPort, optionalPostText, | |||||
| isPost)); | |||||
| const MemoryBlock requestHeader (createRequestHeader (hostName, hostPath, | |||||
| proxyURL, url, | |||||
| hostPort, | |||||
| headers, postData, | |||||
| isPost)); | |||||
| const char* const utf8Header = (const char*) requestHeader.toUTF8(); | |||||
| const int headerLen = strlen (utf8Header); | |||||
| if (! send (socketHandle, utf8Header, headerLen, 0) == headerLen) | |||||
| if (send (socketHandle, requestHeader.getData(), requestHeader.getSize(), 0) | |||||
| != requestHeader.getSize()) | |||||
| { | { | ||||
| closeSocket(); | closeSocket(); | ||||
| return false; | return false; | ||||
| @@ -140,7 +140,7 @@ public: | |||||
| location = T("http://") + location; | location = T("http://") + location; | ||||
| if (levelsOfRedirection++ < 3) | if (levelsOfRedirection++ < 3) | ||||
| return open (location, optionalPostText, isPost); | |||||
| return open (location, headers, postData, isPost); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -191,13 +191,14 @@ private: | |||||
| socketHandle = -1; | socketHandle = -1; | ||||
| } | } | ||||
| const String createRequestHeader (const String& hostName, | |||||
| const String& hostPath, | |||||
| const String& proxyURL, | |||||
| const String& originalURL, | |||||
| const int hostPort, | |||||
| const String& optionalPostText, | |||||
| const bool isPost) | |||||
| const MemoryBlock createRequestHeader (const String& hostName, | |||||
| const String& hostPath, | |||||
| const String& proxyURL, | |||||
| const String& originalURL, | |||||
| const int hostPort, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost) | |||||
| { | { | ||||
| String header (isPost ? "POST " : "GET "); | String header (isPost ? "POST " : "GET "); | ||||
| @@ -212,7 +213,7 @@ private: | |||||
| int proxyPort; | int proxyPort; | ||||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | ||||
| return String::empty; | |||||
| return MemoryBlock(); | |||||
| header << originalURL << " HTTP/1.1\r\nHost: " | header << originalURL << " HTTP/1.1\r\nHost: " | ||||
| << proxyName << ':' << proxyPort; | << proxyName << ':' << proxyPort; | ||||
| @@ -226,20 +227,14 @@ private: | |||||
| header << "\r\nUser-Agent: JUCE/" | header << "\r\nUser-Agent: JUCE/" | ||||
| << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | ||||
| << "\r\nConnection: Close\r\n"; | |||||
| << "\r\nConnection: Close\r\n" | |||||
| << headers << "\r\n"; | |||||
| if (isPost && optionalPostText.isNotEmpty()) | |||||
| { | |||||
| const char* const postTextUTF8 = (const char*) optionalPostText.toUTF8(); | |||||
| header << "Content-type: application/x-www-form-urlencoded\r\nContent-length: " | |||||
| << (int) strlen (postTextUTF8) << "\r\n\r\n" | |||||
| << optionalPostText; | |||||
| } | |||||
| MemoryBlock mb; | |||||
| mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||||
| mb.append (postData.getData(), postData.getSize()); | |||||
| header << "\r\n"; | |||||
| //DBG (header); | |||||
| return header; | |||||
| return mb; | |||||
| } | } | ||||
| const String readResponse() | const String readResponse() | ||||
| @@ -341,12 +336,15 @@ bool juce_isOnLine() | |||||
| } | } | ||||
| void* juce_openInternetFile (const String& url, | void* juce_openInternetFile (const String& url, | ||||
| const String& optionalPostText, | |||||
| const bool isPost) | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext) | |||||
| { | { | ||||
| JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream(); | JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream(); | ||||
| if (s->open (url, optionalPostText, isPost)) | |||||
| if (s->open (url, headers, postData, isPost)) | |||||
| return s; | return s; | ||||
| delete s; | delete s; | ||||
| @@ -1,137 +1,191 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| 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. | |||||
| ============================================================================== | |||||
| */ | |||||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||||
| #include <IOKit/IOKitLib.h> | |||||
| #include <IOKit/network/IOEthernetInterface.h> | |||||
| #include <IOKit/network/IONetworkInterface.h> | |||||
| #include <IOKit/network/IOEthernetController.h> | |||||
| #include <Carbon/Carbon.h> | |||||
| #include <netdb.h> | |||||
| #include <arpa/inet.h> | |||||
| #include <netinet/in.h> | |||||
| #include <sys/types.h> | |||||
| #include <sys/socket.h> | |||||
| #include <sys/wait.h> | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| #include "../../../src/juce_core/text/juce_String.h" | |||||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||||
| #include "../../../src/juce_core/text/juce_StringArray.h" | |||||
| #include "juce_mac_HTTPStream.h" | |||||
| //============================================================================== | |||||
| 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) 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 (numResults < maxNum) | |||||
| { | |||||
| *addresses++ = a; | |||||
| ++numResults; | |||||
| } | |||||
| } | |||||
| IOObjectRelease (controller); | |||||
| } | |||||
| IOObjectRelease (i); | |||||
| } | |||||
| IOObjectRelease (it); | |||||
| } | |||||
| return numResults; | |||||
| } | |||||
| END_JUCE_NAMESPACE | |||||
| /* | |||||
| ============================================================================== | |||||
| 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. | |||||
| ============================================================================== | |||||
| */ | |||||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||||
| #include <IOKit/IOKitLib.h> | |||||
| #include <IOKit/network/IOEthernetInterface.h> | |||||
| #include <IOKit/network/IONetworkInterface.h> | |||||
| #include <IOKit/network/IOEthernetController.h> | |||||
| #include <Carbon/Carbon.h> | |||||
| #include <netdb.h> | |||||
| #include <arpa/inet.h> | |||||
| #include <netinet/in.h> | |||||
| #include <sys/types.h> | |||||
| #include <sys/socket.h> | |||||
| #include <sys/wait.h> | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| #include "../../../src/juce_core/text/juce_String.h" | |||||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||||
| #include "../../../src/juce_core/text/juce_StringArray.h" | |||||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||||
| #include "juce_mac_HTTPStream.h" | |||||
| //============================================================================== | |||||
| 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) | |||||
| { | |||||
| String script; | |||||
| script << "tell application \"Mail\"\r\n" | |||||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||||
| << emailSubject | |||||
| << "\", content:\"" | |||||
| << bodyText | |||||
| << "\" & 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] | |||||
| << "\"} at after the last paragraph\r\n" | |||||
| "end tell\r\n"; | |||||
| } | |||||
| script << "end tell\r\n" | |||||
| "end tell\r\n"; | |||||
| DBG (script); | |||||
| ComponentInstance comp = OpenDefaultComponent (kOSAComponentType, kOSAGenericScriptingComponentSubtype); | |||||
| OSAID resultID; | |||||
| AEDesc source; | |||||
| AECreateDesc (typeUTF8Text, (Ptr) script.toUTF8(), strlen (script.toUTF8()), &source); | |||||
| OSAError err = OSACompileExecute (comp, &source, | |||||
| kOSANullScript, kOSAModeNull, | |||||
| &resultID); | |||||
| AEDisposeDesc (&source); | |||||
| CloseComponent (comp); | |||||
| return false; | |||||
| } | |||||
| END_JUCE_NAMESPACE | |||||
| @@ -38,6 +38,7 @@ | |||||
| #include <wininet.h> | #include <wininet.h> | ||||
| #include <nb30.h> | #include <nb30.h> | ||||
| #include <iphlpapi.h> | #include <iphlpapi.h> | ||||
| #include <mapi.h> | |||||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | #include "../../../src/juce_core/basics/juce_StandardHeader.h" | ||||
| BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
| @@ -46,6 +47,8 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "juce_win32_DynamicLibraryLoader.h" | #include "juce_win32_DynamicLibraryLoader.h" | ||||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | #include "../../../src/juce_core/basics/juce_SystemStats.h" | ||||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | ||||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||||
| #ifndef INTERNET_FLAG_NEED_FILE | #ifndef INTERNET_FLAG_NEED_FILE | ||||
| #define INTERNET_FLAG_NEED_FILE 0x00000010 | #define INTERNET_FLAG_NEED_FILE 0x00000010 | ||||
| @@ -72,11 +75,16 @@ struct ConnectionAndRequestStruct | |||||
| static HINTERNET sessionHandle = 0; | static HINTERNET sessionHandle = 0; | ||||
| void* juce_openInternetFile (const String& url, | void* juce_openInternetFile (const String& url, | ||||
| const String& postText, | |||||
| const bool isPost) | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext) | |||||
| { | { | ||||
| if (sessionHandle == 0) | if (sessionHandle == 0) | ||||
| sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0); | |||||
| sessionHandle = InternetOpen (_T("juce"), | |||||
| INTERNET_OPEN_TYPE_PRECONFIG, | |||||
| 0, 0, 0); | |||||
| if (sessionHandle != 0) | if (sessionHandle != 0) | ||||
| { | { | ||||
| @@ -92,9 +100,7 @@ void* juce_openInternetFile (const String& url, | |||||
| uc.lpszUrlPath = file; | uc.lpszUrlPath = file; | ||||
| uc.lpszHostName = server; | uc.lpszHostName = server; | ||||
| if (InternetCrackUrl (url, 0, | |||||
| ICU_ESCAPE | ICU_DECODE, | |||||
| &uc)) | |||||
| if (InternetCrackUrl (url, 0, ICU_ESCAPE | ICU_DECODE, &uc)) | |||||
| { | { | ||||
| const bool isFtp = url.startsWithIgnoreCase (T("ftp:")); | const bool isFtp = url.startsWithIgnoreCase (T("ftp:")); | ||||
| @@ -102,8 +108,8 @@ void* juce_openInternetFile (const String& url, | |||||
| uc.lpszHostName, | uc.lpszHostName, | ||||
| uc.nPort, | uc.nPort, | ||||
| _T(""), _T(""), | _T(""), _T(""), | ||||
| (isFtp) ? INTERNET_SERVICE_FTP | |||||
| : INTERNET_SERVICE_HTTP, | |||||
| isFtp ? INTERNET_SERVICE_FTP | |||||
| : INTERNET_SERVICE_HTTP, | |||||
| 0, 0); | 0, 0); | ||||
| if (connection != 0) | if (connection != 0) | ||||
| @@ -129,24 +135,52 @@ void* juce_openInternetFile (const String& url, | |||||
| isPost ? _T("POST") | isPost ? _T("POST") | ||||
| : _T("GET"), | : _T("GET"), | ||||
| uc.lpszUrlPath, | uc.lpszUrlPath, | ||||
| 0, 0, | |||||
| mimeTypes, | |||||
| INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0); | |||||
| 0, 0, mimeTypes, | |||||
| INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, | |||||
| 0); | |||||
| if (request != 0) | if (request != 0) | ||||
| { | { | ||||
| // (this header is needed to make webservers process a POST request correctly) | |||||
| const String hdr ("Content-Type: application/x-www-form-urlencoded"); | |||||
| if (HttpSendRequest (request, | |||||
| hdr, hdr.length(), | |||||
| (void*) (const char*) postText, | |||||
| postText.length())) | |||||
| INTERNET_BUFFERS buffers; | |||||
| zerostruct (buffers); | |||||
| buffers.dwStructSize = sizeof (INTERNET_BUFFERS); | |||||
| buffers.lpcszHeader = (const char*) headers; | |||||
| buffers.dwHeadersLength = headers.length(); | |||||
| buffers.dwBufferTotal = (DWORD) postData.getSize(); | |||||
| ConnectionAndRequestStruct* result = 0; | |||||
| if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) | |||||
| { | { | ||||
| ConnectionAndRequestStruct* const result = new ConnectionAndRequestStruct(); | |||||
| result->connection = connection; | |||||
| result->request = request; | |||||
| return result; | |||||
| int bytesSent = 0; | |||||
| for (;;) | |||||
| { | |||||
| const int bytesToDo = jmin (1024, postData.getSize() - bytesSent); | |||||
| DWORD bytesDone = 0; | |||||
| if (bytesToDo > 0 | |||||
| && ! InternetWriteFile (request, | |||||
| ((const char*) postData.getData()) + bytesSent, | |||||
| bytesToDo, &bytesDone)) | |||||
| { | |||||
| break; | |||||
| } | |||||
| if (bytesToDo == 0 || (int) bytesDone < bytesToDo) | |||||
| { | |||||
| result = new ConnectionAndRequestStruct(); | |||||
| result->connection = connection; | |||||
| result->request = request; | |||||
| HttpEndRequest (request, 0, 0, 0); | |||||
| return result; | |||||
| } | |||||
| bytesSent += bytesDone; | |||||
| if (callback != 0 && ! callback (callbackContext, bytesSent, postData.getSize())) | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| InternetCloseHandle (request); | InternetCloseHandle (request); | ||||
| @@ -222,7 +256,8 @@ void juce_closeInternetFile (void* handle) | |||||
| } | } | ||||
| } | } | ||||
| static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum) throw() | |||||
| //============================================================================== | |||||
| static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | { | ||||
| int numFound = 0; | int numFound = 0; | ||||
| @@ -251,6 +286,9 @@ static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum) throw( | |||||
| for (unsigned int i = 0; i < adapter->AddressLength; ++i) | for (unsigned int i = 0; i < adapter->AddressLength; ++i) | ||||
| mac = (mac << 8) | adapter->Address[i]; | mac = (mac << 8) | adapter->Address[i]; | ||||
| if (littleEndian) | |||||
| mac = (int64) swapByteOrder ((uint64) mac); | |||||
| if (numFound < maxNum && mac != 0) | if (numFound < maxNum && mac != 0) | ||||
| addresses [numFound++] = mac; | addresses [numFound++] = mac; | ||||
| @@ -262,7 +300,7 @@ static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum) throw( | |||||
| return numFound; | return numFound; | ||||
| } | } | ||||
| static int getMACAddressesViaNetBios (int64* addresses, int maxNum) throw() | |||||
| static int getMACAddressesViaNetBios (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | { | ||||
| int numFound = 0; | int numFound = 0; | ||||
| @@ -315,6 +353,9 @@ static int getMACAddressesViaNetBios (int64* addresses, int maxNum) throw() | |||||
| for (unsigned int i = 0; i < 6; ++i) | for (unsigned int i = 0; i < 6; ++i) | ||||
| mac = (mac << 8) | astat.adapt.adapter_address[i]; | mac = (mac << 8) | astat.adapt.adapter_address[i]; | ||||
| if (littleEndian) | |||||
| mac = (int64) swapByteOrder ((uint64) mac); | |||||
| if (numFound < maxNum && mac != 0) | if (numFound < maxNum && mac != 0) | ||||
| addresses [numFound++] = mac; | addresses [numFound++] = mac; | ||||
| } | } | ||||
| @@ -326,14 +367,62 @@ static int getMACAddressesViaNetBios (int64* addresses, int maxNum) throw() | |||||
| return numFound; | return numFound; | ||||
| } | } | ||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum) throw() | |||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | { | ||||
| int numFound = getMACAddressViaGetAdaptersInfo (addresses, maxNum); | |||||
| int numFound = getMACAddressViaGetAdaptersInfo (addresses, maxNum, littleEndian); | |||||
| if (numFound == 0) | if (numFound == 0) | ||||
| numFound = getMACAddressesViaNetBios (addresses, maxNum); | |||||
| numFound = getMACAddressesViaNetBios (addresses, maxNum, littleEndian); | |||||
| return numFound; | return numFound; | ||||
| } | } | ||||
| //============================================================================== | |||||
| typedef ULONG (WINAPI *MAPISendMailType) (LHANDLE, ULONG, lpMapiMessage, FLAGS, ULONG); | |||||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
| const String& emailSubject, | |||||
| const String& bodyText, | |||||
| const StringArray& filesToAttach) | |||||
| { | |||||
| HMODULE h = LoadLibraryA ("MAPI32.dll"); | |||||
| MAPISendMailType mapiSendMail = (MAPISendMailType) GetProcAddress (h, "MAPISendMail"); | |||||
| bool ok = false; | |||||
| if (mapiSendMail != 0) | |||||
| { | |||||
| MapiMessage message; | |||||
| zerostruct (message); | |||||
| message.lpszSubject = (LPTSTR) (LPCTSTR) emailSubject; | |||||
| message.lpszNoteText = (LPTSTR) (LPCTSTR) bodyText; | |||||
| MapiRecipDesc recip; | |||||
| zerostruct (recip); | |||||
| recip.ulRecipClass = MAPI_TO; | |||||
| recip.lpszName = (LPTSTR) (LPCTSTR) targetEmailAddress; | |||||
| message.nRecipCount = 1; | |||||
| message.lpRecips = &recip; | |||||
| MemoryBlock mb (sizeof (MapiFileDesc) * filesToAttach.size()); | |||||
| mb.fillWith (0); | |||||
| MapiFileDesc* files = (MapiFileDesc*) mb.getData(); | |||||
| message.nFileCount = filesToAttach.size(); | |||||
| message.lpFiles = files; | |||||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||||
| { | |||||
| files[i].nPosition = (ULONG) -1; | |||||
| files[i].lpszPathName = (LPTSTR) (LPCTSTR) filesToAttach [i]; | |||||
| } | |||||
| ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG, 0) == SUCCESS_SUCCESS); | |||||
| } | |||||
| FreeLibrary (h); | |||||
| return ok; | |||||
| } | |||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -141,7 +141,7 @@ | |||||
| /** Enabling this builds support for VST audio plugins. | /** Enabling this builds support for VST audio plugins. | ||||
| @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU | @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU | ||||
| */ | */ | ||||
| #define JUCE_PLUGINHOST_VST 1 | |||||
| //#define JUCE_PLUGINHOST_VST 1 | |||||
| /** Enabling this builds support for AudioUnit audio plugins. | /** Enabling this builds support for AudioUnit audio plugins. | ||||
| @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST | @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST | ||||
| @@ -666,11 +666,16 @@ int ListBox::getRowNumberOfComponent (Component* const rowComponent) const throw | |||||
| return viewport->getRowNumberOfComponent (rowComponent); | return viewport->getRowNumberOfComponent (rowComponent); | ||||
| } | } | ||||
| const Rectangle ListBox::getRowPosition (const int rowNumber) const throw() | |||||
| const Rectangle ListBox::getRowPosition (const int rowNumber, | |||||
| const bool relativeToComponentTopLeft) const throw() | |||||
| { | { | ||||
| const int rowHeight = getRowHeight(); | const int rowHeight = getRowHeight(); | ||||
| int y = viewport->getY() + rowHeight * rowNumber; | |||||
| return Rectangle (0, rowHeight * rowNumber, | |||||
| if (relativeToComponentTopLeft) | |||||
| y -= viewport->getViewPositionY(); | |||||
| return Rectangle (viewport->getX(), y, | |||||
| viewport->getViewedComponent()->getWidth(), rowHeight); | viewport->getViewedComponent()->getWidth(), rowHeight); | ||||
| } | } | ||||
| @@ -890,7 +895,7 @@ void ListBox::setHeaderComponent (Component* const newHeaderComponent) | |||||
| void ListBox::repaintRow (const int rowNumber) throw() | void ListBox::repaintRow (const int rowNumber) throw() | ||||
| { | { | ||||
| const Rectangle r (getRowPosition (rowNumber)); | |||||
| const Rectangle r (getRowPosition (rowNumber, true)); | |||||
| repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); | repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); | ||||
| } | } | ||||
| @@ -402,7 +402,8 @@ public: | |||||
| This may be off-screen, and the range of the row number that is passed-in is | This may be off-screen, and the range of the row number that is passed-in is | ||||
| not checked to see if it's a valid row. | not checked to see if it's a valid row. | ||||
| */ | */ | ||||
| const Rectangle getRowPosition (const int rowNumber) const throw(); | |||||
| const Rectangle getRowPosition (const int rowNumber, | |||||
| const bool relativeToComponentTopLeft) const throw(); | |||||
| /** Finds the row component for a given row in the list. | /** Finds the row component for a given row in the list. | ||||
| @@ -598,7 +598,7 @@ double Slider::getValueFromText (const String& text) | |||||
| while (t.startsWithChar (T('+'))) | while (t.startsWithChar (T('+'))) | ||||
| t = t.substring (1).trimStart(); | t = t.substring (1).trimStart(); | ||||
| return t.initialSectionContainingOnly (T("0123456789.-")) | |||||
| return t.initialSectionContainingOnly (T("0123456789.,-")) | |||||
| .getDoubleValue(); | .getDoubleValue(); | ||||
| } | } | ||||
| @@ -847,7 +847,7 @@ void Slider::resized() | |||||
| } | } | ||||
| } | } | ||||
| const int indent = getLookAndFeel().getSliderThumbRadius (*this) + 2; | |||||
| const int indent = getLookAndFeel().getSliderThumbRadius (*this); | |||||
| if (style == LinearBar) | if (style == LinearBar) | ||||
| { | { | ||||
| @@ -388,10 +388,16 @@ bool TableListBox::isAutoSizeMenuOptionShown() const throw() | |||||
| return autoSizeOptionsShown; | return autoSizeOptionsShown; | ||||
| } | } | ||||
| const Rectangle TableListBox::getCellPosition (const int columnId, const int rowNumber) const | |||||
| const Rectangle TableListBox::getCellPosition (const int columnId, | |||||
| const int rowNumber, | |||||
| const bool relativeToComponentTopLeft) const | |||||
| { | { | ||||
| const Rectangle headerCell (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); | |||||
| const Rectangle row (getRowPosition (rowNumber)); | |||||
| Rectangle headerCell (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); | |||||
| if (relativeToComponentTopLeft) | |||||
| headerCell.translate (header->getX(), 0); | |||||
| const Rectangle row (getRowPosition (rowNumber, relativeToComponentTopLeft)); | |||||
| return Rectangle (headerCell.getX(), row.getY(), | return Rectangle (headerCell.getX(), row.getY(), | ||||
| headerCell.getWidth(), row.getHeight()); | headerCell.getWidth(), row.getHeight()); | ||||
| @@ -406,7 +412,7 @@ void TableListBox::scrollToEnsureColumnIsOnscreen (const int columnId) | |||||
| const Rectangle pos (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); | const Rectangle pos (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); | ||||
| double x = scrollbar->getCurrentRangeStart(); | double x = scrollbar->getCurrentRangeStart(); | ||||
| double w = scrollbar->getCurrentRangeSize(); | |||||
| const double w = scrollbar->getCurrentRangeSize(); | |||||
| if (pos.getX() < x) | if (pos.getX() < x) | ||||
| x = pos.getX(); | x = pos.getX(); | ||||
| @@ -264,11 +264,16 @@ public: | |||||
| /** Returns the position of one of the cells in the table. | /** Returns the position of one of the cells in the table. | ||||
| Co-ordinates are relative to the table component's top-left. The row number isn't | |||||
| checked to see if it's in-range, but the column ID must exist or this will | |||||
| return an empty rectangle. | |||||
| If relativeToComponentTopLeft is true, the co-ordinates are relative to | |||||
| the table component's top-left. The row number isn't checked to see if it's | |||||
| in-range, but the column ID must exist or this will return an empty rectangle. | |||||
| If relativeToComponentTopLeft is false, the co-ords are relative to the | |||||
| top-left of the table's top-left cell. | |||||
| */ | */ | ||||
| const Rectangle getCellPosition (const int columnId, const int rowNumber) const; | |||||
| const Rectangle getCellPosition (const int columnId, | |||||
| const int rowNumber, | |||||
| const bool relativeToComponentTopLeft) const; | |||||
| /** Scrolls horizontally if necessary to make sure that a particular column is visible. | /** Scrolls horizontally if necessary to make sure that a particular column is visible. | ||||
| @@ -57,7 +57,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "../filebrowser/juce_FilenameComponent.h" | #include "../filebrowser/juce_FilenameComponent.h" | ||||
| #include "../filebrowser/juce_DirectoryContentsDisplayComponent.h" | #include "../filebrowser/juce_DirectoryContentsDisplayComponent.h" | ||||
| #include "../filebrowser/juce_FileSearchPathListComponent.h" | #include "../filebrowser/juce_FileSearchPathListComponent.h" | ||||
| #include "../filebrowser/juce_FileBrowserComponent.h" | |||||
| #include "../filebrowser/juce_FileBrowserComponent.h" | |||||
| #include "../layout/juce_GroupComponent.h" | #include "../layout/juce_GroupComponent.h" | ||||
| #include "../properties/juce_PropertyComponent.h" | #include "../properties/juce_PropertyComponent.h" | ||||
| #include "../juce_Desktop.h" | #include "../juce_Desktop.h" | ||||
| @@ -814,6 +814,9 @@ void LookAndFeel::getIdealPopupMenuItemSize (const String& text, | |||||
| { | { | ||||
| Font font (getPopupMenuFont()); | Font font (getPopupMenuFont()); | ||||
| if (standardMenuItemHeight > 0 && font.getHeight() > standardMenuItemHeight / 1.3f) | |||||
| font.setHeight (standardMenuItemHeight / 1.3f); | |||||
| idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundFloatToInt (font.getHeight() * 1.3f); | idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundFloatToInt (font.getHeight() * 1.3f); | ||||
| idealWidth = font.getStringWidth (text) + idealHeight * 2; | idealWidth = font.getStringWidth (text) + idealHeight * 2; | ||||
| } | } | ||||
| @@ -1137,7 +1140,7 @@ void LookAndFeel::drawLinearSliderBackground (Graphics& g, | |||||
| const Slider::SliderStyle /*style*/, | const Slider::SliderStyle /*style*/, | ||||
| Slider& slider) | Slider& slider) | ||||
| { | { | ||||
| const float sliderRadius = (float) getSliderThumbRadius (slider); | |||||
| const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); | |||||
| const Colour trackColour (slider.findColour (Slider::trackColourId)); | const Colour trackColour (slider.findColour (Slider::trackColourId)); | ||||
| const Colour gradCol1 (trackColour.overlaidWith (Colours::black.withAlpha (slider.isEnabled() ? 0.25f : 0.13f))); | const Colour gradCol1 (trackColour.overlaidWith (Colours::black.withAlpha (slider.isEnabled() ? 0.25f : 0.13f))); | ||||
| @@ -1186,7 +1189,7 @@ void LookAndFeel::drawLinearSliderThumb (Graphics& g, | |||||
| const Slider::SliderStyle style, | const Slider::SliderStyle style, | ||||
| Slider& slider) | Slider& slider) | ||||
| { | { | ||||
| const float sliderRadius = (float) getSliderThumbRadius (slider); | |||||
| const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); | |||||
| Colour knobColour (createBaseColour (slider.findColour (Slider::thumbColourId), | Colour knobColour (createBaseColour (slider.findColour (Slider::thumbColourId), | ||||
| slider.hasKeyboardFocus (false) && slider.isEnabled(), | slider.hasKeyboardFocus (false) && slider.isEnabled(), | ||||
| @@ -1277,7 +1280,7 @@ void LookAndFeel::drawLinearSlider (Graphics& g, | |||||
| Colour baseColour (createBaseColour (slider.findColour (Slider::thumbColourId) | Colour baseColour (createBaseColour (slider.findColour (Slider::thumbColourId) | ||||
| .withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f), | .withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f), | ||||
| false, | false, | ||||
| isMouseOver, | |||||
| isMouseOver, | |||||
| isMouseOver || slider.isMouseButtonDown())); | isMouseOver || slider.isMouseButtonDown())); | ||||
| drawShinyButtonShape (g, | drawShinyButtonShape (g, | ||||
| @@ -1297,7 +1300,7 @@ int LookAndFeel::getSliderThumbRadius (Slider& slider) | |||||
| { | { | ||||
| return jmin (7, | return jmin (7, | ||||
| slider.getHeight() / 2, | slider.getHeight() / 2, | ||||
| slider.getWidth() / 2); | |||||
| slider.getWidth() / 2) + 2; | |||||
| } | } | ||||
| void LookAndFeel::drawRotarySlider (Graphics& g, | void LookAndFeel::drawRotarySlider (Graphics& g, | ||||
| @@ -2358,7 +2361,7 @@ void LookAndFeel::drawFileBrowserRow (Graphics& g, int width, int height, | |||||
| Button* LookAndFeel::createFileBrowserGoUpButton() | Button* LookAndFeel::createFileBrowserGoUpButton() | ||||
| { | { | ||||
| DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground); | DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground); | ||||
| Path arrowPath; | Path arrowPath; | ||||
| arrowPath.addArrow (50.0f, 100.0f, 50.0f, 0.0, 40.0f, 100.0f, 50.0f); | arrowPath.addArrow (50.0f, 100.0f, 50.0f, 0.0, 40.0f, 100.0f, 50.0f); | ||||
| @@ -40,6 +40,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "juce_Atomic.h" | #include "juce_Atomic.h" | ||||
| #include "../threads/juce_Thread.h" | #include "../threads/juce_Thread.h" | ||||
| #include "../text/juce_LocalisedStrings.h" | #include "../text/juce_LocalisedStrings.h" | ||||
| void juce_initialiseStrings(); | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -78,6 +79,7 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() | |||||
| juceInitialisedNonGUI = true; | juceInitialisedNonGUI = true; | ||||
| DBG (SystemStats::getJUCEVersion()); | DBG (SystemStats::getJUCEVersion()); | ||||
| juce_initialiseStrings(); | |||||
| SystemStats::initialiseStats(); | SystemStats::initialiseStats(); | ||||
| Random::getSystemRandom().setSeed (Time::currentTimeMillis()); | Random::getSystemRandom().setSeed (Time::currentTimeMillis()); | ||||
| } | } | ||||
| @@ -89,6 +91,10 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() | |||||
| extern juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib; | extern juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib; | ||||
| #endif | #endif | ||||
| #if JUCE_DEBUG | |||||
| extern void juce_CheckForDanglingStreams(); | |||||
| #endif | |||||
| void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() | void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() | ||||
| { | { | ||||
| if (juceInitialisedNonGUI) | if (juceInitialisedNonGUI) | ||||
| @@ -102,6 +108,10 @@ void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() | |||||
| LocalisedStrings::setCurrentMappings (0); | LocalisedStrings::setCurrentMappings (0); | ||||
| Thread::stopAllThreads (3000); | Thread::stopAllThreads (3000); | ||||
| #if JUCE_DEBUG | |||||
| juce_CheckForDanglingStreams(); | |||||
| #endif | |||||
| juceInitialisedNonGUI = false; | juceInitialisedNonGUI = false; | ||||
| } | } | ||||
| } | } | ||||
| @@ -152,9 +152,21 @@ public: | |||||
| @param addresses an array into which the MAC addresses should be copied | @param addresses an array into which the MAC addresses should be copied | ||||
| @param maxNum the number of elements in this array | @param maxNum the number of elements in this array | ||||
| @param littleEndian the endianness of the numbers to return. Note that | |||||
| the default values of this parameter are different on | |||||
| Mac/PC to avoid breaking old software that was written | |||||
| before this parameter was added (when the two systems | |||||
| defaulted to using different endiannesses). In newer | |||||
| software you probably want to specify an explicit value | |||||
| for this. | |||||
| @returns the number of MAC addresses that were found | @returns the number of MAC addresses that were found | ||||
| */ | */ | ||||
| static int getMACAddresses (int64* addresses, int maxNum) throw(); | |||||
| static int getMACAddresses (int64* addresses, int maxNum, | |||||
| #if JUCE_MAC | |||||
| const bool littleEndian = true) throw(); | |||||
| #else | |||||
| const bool littleEndian = false) throw(); | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -35,6 +35,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "juce_URL.h" | #include "juce_URL.h" | ||||
| #include "../../basics/juce_Random.h" | |||||
| #include "../../text/juce_XmlDocument.h" | #include "../../text/juce_XmlDocument.h" | ||||
| @@ -79,7 +80,9 @@ URL::URL (const String& url_) | |||||
| URL::URL (const URL& other) | URL::URL (const URL& other) | ||||
| : url (other.url), | : url (other.url), | ||||
| parameters (other.parameters) | |||||
| parameters (other.parameters), | |||||
| filesToUpload (other.filesToUpload), | |||||
| mimeTypes (other.mimeTypes) | |||||
| { | { | ||||
| } | } | ||||
| @@ -87,6 +90,8 @@ const URL& URL::operator= (const URL& other) | |||||
| { | { | ||||
| url = other.url; | url = other.url; | ||||
| parameters = other.parameters; | parameters = other.parameters; | ||||
| filesToUpload = other.filesToUpload; | |||||
| mimeTypes = other.mimeTypes; | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| @@ -95,7 +100,7 @@ URL::~URL() throw() | |||||
| { | { | ||||
| } | } | ||||
| const String URL::getMangledParameters() const | |||||
| static const String getMangledParameters (const StringPairArray& parameters) | |||||
| { | { | ||||
| String p; | String p; | ||||
| @@ -104,9 +109,9 @@ const String URL::getMangledParameters() const | |||||
| if (i > 0) | if (i > 0) | ||||
| p += T("&"); | p += T("&"); | ||||
| p << addEscapeChars (parameters.getAllKeys() [i]) | |||||
| p << URL::addEscapeChars (parameters.getAllKeys() [i]) | |||||
| << T("=") | << T("=") | ||||
| << addEscapeChars (parameters.getAllValues() [i]); | |||||
| << URL::addEscapeChars (parameters.getAllValues() [i]); | |||||
| } | } | ||||
| return p; | return p; | ||||
| @@ -115,7 +120,7 @@ const String URL::getMangledParameters() const | |||||
| const String URL::toString (const bool includeGetParameters) const | const String URL::toString (const bool includeGetParameters) const | ||||
| { | { | ||||
| if (includeGetParameters && parameters.size() > 0) | if (includeGetParameters && parameters.size() > 0) | ||||
| return url + T("?") + getMangledParameters(); | |||||
| return url + T("?") + getMangledParameters (parameters); | |||||
| else | else | ||||
| return url; | return url; | ||||
| } | } | ||||
| @@ -153,8 +158,12 @@ bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) | |||||
| //============================================================================== | //============================================================================== | ||||
| void* juce_openInternetFile (const String& url, | void* juce_openInternetFile (const String& url, | ||||
| const String& optionalPostText, | |||||
| const bool isPost); | |||||
| const String& headers, | |||||
| const MemoryBlock& optionalPostData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext); | |||||
| void juce_closeInternetFile (void* handle); | void juce_closeInternetFile (void* handle); | ||||
| int juce_readFromInternetFile (void* handle, void* dest, int bytesToRead); | int juce_readFromInternetFile (void* handle, void* dest, int bytesToRead); | ||||
| int juce_seekInInternetFile (void* handle, int newPosition); | int juce_seekInInternetFile (void* handle, int newPosition); | ||||
| @@ -164,23 +173,25 @@ int juce_getStatusCodeFor (void* handle); | |||||
| //============================================================================== | //============================================================================== | ||||
| class WebInputStream : public InputStream | class WebInputStream : public InputStream | ||||
| { | { | ||||
| String url, postText; | |||||
| int64 position; | |||||
| bool finished, isPost; | |||||
| void* handle; | |||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| WebInputStream (const String& url_, | |||||
| const String& postText_, | |||||
| const bool isPost_) | |||||
| : url (url_), | |||||
| postText (postText_), | |||||
| position (0), | |||||
| WebInputStream (const URL& url, | |||||
| const bool isPost_, | |||||
| URL::OpenStreamProgressCallback* const progressCallback_, | |||||
| void* const progressCallbackContext_) | |||||
| : position (0), | |||||
| finished (false), | finished (false), | ||||
| isPost (isPost_) | |||||
| isPost (isPost_), | |||||
| progressCallback (progressCallback_), | |||||
| progressCallbackContext (progressCallbackContext_) | |||||
| { | { | ||||
| handle = juce_openInternetFile (url, postText, isPost); | |||||
| server = url.toString (! isPost); | |||||
| if (isPost_) | |||||
| createHeadersAndPostData (url); | |||||
| handle = juce_openInternetFile (server, headers, postData, isPost, | |||||
| progressCallback_, progressCallbackContext_); | |||||
| } | } | ||||
| ~WebInputStream() | ~WebInputStream() | ||||
| @@ -188,7 +199,7 @@ public: | |||||
| juce_closeInternetFile (handle); | juce_closeInternetFile (handle); | ||||
| } | } | ||||
| bool isError() const | |||||
| bool isError() const throw() | |||||
| { | { | ||||
| return handle == 0; | return handle == 0; | ||||
| } | } | ||||
| @@ -253,7 +264,8 @@ public: | |||||
| position = 0; | position = 0; | ||||
| finished = false; | finished = false; | ||||
| handle = juce_openInternetFile (url, postText, isPost); | |||||
| handle = juce_openInternetFile (server, headers, postData, isPost, | |||||
| progressCallback, progressCallbackContext); | |||||
| } | } | ||||
| skipNextBytes (wantedPos - position); | skipNextBytes (wantedPos - position); | ||||
| @@ -262,26 +274,105 @@ public: | |||||
| return true; | return true; | ||||
| } | } | ||||
| }; | |||||
| InputStream* URL::createInputStream (const bool usePostCommand) const | |||||
| { | |||||
| WebInputStream* wi | |||||
| = (usePostCommand) ? new WebInputStream (url, getMangledParameters(), true) | |||||
| : new WebInputStream (toString (true), String::empty, false); | |||||
| //============================================================================== | |||||
| juce_UseDebuggingNewOperator | |||||
| if (wi->isError()) | |||||
| private: | |||||
| String server, headers; | |||||
| MemoryBlock postData; | |||||
| int64 position; | |||||
| bool finished; | |||||
| const bool isPost; | |||||
| void* handle; | |||||
| URL::OpenStreamProgressCallback* const progressCallback; | |||||
| void* const progressCallbackContext; | |||||
| void createHeadersAndPostData (const URL& url) | |||||
| { | { | ||||
| delete wi; | |||||
| wi = 0; | |||||
| if (url.getFilesToUpload().size() > 0) | |||||
| { | |||||
| // need to upload some files, so do it as multi-part... | |||||
| String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); | |||||
| headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; | |||||
| appendUTF8ToPostData ("--" + boundary); | |||||
| int i; | |||||
| for (i = 0; i < url.getParameters().size(); ++i) | |||||
| { | |||||
| String s; | |||||
| s << "\r\nContent-Disposition: form-data; name=\"" | |||||
| << url.getParameters().getAllKeys() [i] | |||||
| << "\"\r\n\r\n" | |||||
| << url.getParameters().getAllValues() [i] | |||||
| << "\r\n--" | |||||
| << boundary; | |||||
| appendUTF8ToPostData (s); | |||||
| } | |||||
| for (i = 0; i < url.getFilesToUpload().size(); ++i) | |||||
| { | |||||
| const File f (url.getFilesToUpload().getAllValues() [i]); | |||||
| const String paramName (url.getFilesToUpload().getAllKeys() [i]); | |||||
| String s; | |||||
| s << "\r\nContent-Disposition: form-data; name=\"" | |||||
| << paramName | |||||
| << "\"; filename=\"" | |||||
| << f.getFileName() | |||||
| << "\"\r\n"; | |||||
| const String mimeType (url.getMimeTypesOfUploadFiles() | |||||
| .getValue (paramName, String::empty)); | |||||
| if (mimeType.isNotEmpty()) | |||||
| s << "Content-Type: " << mimeType << "\r\n"; | |||||
| s << "Content-Transfer-Encoding: binary\r\n\r\n"; | |||||
| appendUTF8ToPostData (s); | |||||
| f.loadFileAsData (postData); | |||||
| s = "\r\n--" + boundary; | |||||
| appendUTF8ToPostData (s); | |||||
| } | |||||
| appendUTF8ToPostData ("--\r\n"); | |||||
| } | |||||
| else | |||||
| { | |||||
| // just a short text attachment, so use simple url encoding.. | |||||
| const String params (getMangledParameters (url.getParameters())); | |||||
| headers = "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " | |||||
| + String ((int) strlen (params.toUTF8())) | |||||
| + "\r\n"; | |||||
| appendUTF8ToPostData (params); | |||||
| } | |||||
| } | } | ||||
| return wi; | |||||
| } | |||||
| void appendUTF8ToPostData (const String& text) throw() | |||||
| { | |||||
| postData.append (text.toUTF8(), | |||||
| (int) strlen (text.toUTF8())); | |||||
| } | |||||
| WebInputStream (const WebInputStream&); | |||||
| const WebInputStream& operator= (const WebInputStream&); | |||||
| }; | |||||
| InputStream* URL::createPostInputStream (const String& postText) const | |||||
| InputStream* URL::createInputStream (const bool usePostCommand, | |||||
| OpenStreamProgressCallback* const progressCallback, | |||||
| void* const progressCallbackContext) const | |||||
| { | { | ||||
| WebInputStream* wi = new WebInputStream (url, postText, true); | |||||
| WebInputStream* wi = new WebInputStream (*this, usePostCommand, | |||||
| progressCallback, progressCallbackContext); | |||||
| if (wi->isError()) | if (wi->isError()) | ||||
| { | { | ||||
| @@ -338,11 +429,31 @@ const URL URL::withParameter (const String& parameterName, | |||||
| return u; | return u; | ||||
| } | } | ||||
| const StringPairArray& URL::getParameters() const | |||||
| const URL URL::withFileToUpload (const String& parameterName, | |||||
| const File& fileToUpload, | |||||
| const String& mimeType) const | |||||
| { | |||||
| URL u (*this); | |||||
| u.filesToUpload.set (parameterName, fileToUpload.getFullPathName()); | |||||
| u.mimeTypes.set (parameterName, mimeType); | |||||
| return u; | |||||
| } | |||||
| const StringPairArray& URL::getParameters() const throw() | |||||
| { | { | ||||
| return parameters; | return parameters; | ||||
| } | } | ||||
| const StringPairArray& URL::getFilesToUpload() const throw() | |||||
| { | |||||
| return filesToUpload; | |||||
| } | |||||
| const StringPairArray& URL::getMimeTypesOfUploadFiles() const throw() | |||||
| { | |||||
| return mimeTypes; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| const String URL::removeEscapeChars (const String& s) | const String URL::removeEscapeChars (const String& s) | ||||
| { | { | ||||
| @@ -87,6 +87,18 @@ public: | |||||
| const URL withParameter (const String& parameterName, | const URL withParameter (const String& parameterName, | ||||
| const String& parameterValue) const; | const String& parameterValue) const; | ||||
| /** Returns a copy of this URl, with a file-upload type parameter added to it. | |||||
| When performing a POST where one of your parameters is a binary file, this | |||||
| lets you specify the file. | |||||
| Note that the filename is stored, but the file itself won't actually be read | |||||
| until this URL is later used to create a network input stream. | |||||
| */ | |||||
| const URL withFileToUpload (const String& parameterName, | |||||
| const File& fileToUpload, | |||||
| const String& mimeType) const; | |||||
| /** Returns a set of all the parameters encoded into the url. | /** Returns a set of all the parameters encoded into the url. | ||||
| E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would | E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would | ||||
| @@ -96,7 +108,18 @@ public: | |||||
| @see getNamedParameter, withParameter | @see getNamedParameter, withParameter | ||||
| */ | */ | ||||
| const StringPairArray& getParameters() const; | |||||
| const StringPairArray& getParameters() const throw(); | |||||
| /** Returns the set of files that should be uploaded as part of a POST operation. | |||||
| This is the set of files that were added to the URL with the withFileToUpload() | |||||
| method. | |||||
| */ | |||||
| const StringPairArray& getFilesToUpload() const throw(); | |||||
| /** Returns the set of mime types associated with each of the upload files. | |||||
| */ | |||||
| const StringPairArray& getMimeTypesOfUploadFiles() const throw(); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Tries to launch the system's default browser to open the URL. | /** Tries to launch the system's default browser to open the URL. | ||||
| @@ -119,21 +142,28 @@ public: | |||||
| static bool isProbablyAnEmailAddress (const String& possibleEmailAddress); | static bool isProbablyAnEmailAddress (const String& possibleEmailAddress); | ||||
| //============================================================================== | //============================================================================== | ||||
| /** This callback function can be used by the createInputStream() method. | |||||
| It allows your app to receive progress updates during a lengthy POST operation. If you | |||||
| want to continue the operation, this should return true, or false to abort. | |||||
| */ | |||||
| typedef bool (OpenStreamProgressCallback) (void* context, int bytesSent, int totalBytes); | |||||
| /** Attempts to open a stream that can read from this URL. | /** Attempts to open a stream that can read from this URL. | ||||
| @param usePostCommand if true, it will try to do use a http 'POST' to pass | @param usePostCommand if true, it will try to do use a http 'POST' to pass | ||||
| the paramters, otherwise it'll encode them into the | the paramters, otherwise it'll encode them into the | ||||
| URL and do a 'GET'. | URL and do a 'GET'. | ||||
| @param progressCallback if this is non-zero, it lets you supply a callback function | |||||
| to keep track of the operation's progress. This can be useful | |||||
| for lengthy POST operations, so that you can provide user feedback. | |||||
| @param progressCallbackContext if a callback is specified, this value will be passed to | |||||
| the function | |||||
| */ | */ | ||||
| InputStream* createInputStream (const bool usePostCommand) const; | |||||
| InputStream* createInputStream (const bool usePostCommand, | |||||
| OpenStreamProgressCallback* const progressCallback = 0, | |||||
| void* const progressCallbackContext = 0) const; | |||||
| /** Attempts to open a stream to read from this URL using a http POST command. | |||||
| Normally you'd use the createInputStream (true) method instead of this, as | |||||
| this will pass the given block of text instead of any parameters | |||||
| that were added to the this URL with the withParameter() method. | |||||
| */ | |||||
| InputStream* createPostInputStream (const String& postText) const; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Tries to download the entire contents of this URL into a binary data block. | /** Tries to download the entire contents of this URL into a binary data block. | ||||
| @@ -206,9 +236,7 @@ public: | |||||
| private: | private: | ||||
| String url; | String url; | ||||
| StringPairArray parameters; | |||||
| const String getMangledParameters() const; | |||||
| StringPairArray parameters, filesToUpload, mimeTypes; | |||||
| }; | }; | ||||
| @@ -47,6 +47,11 @@ public: | |||||
| /** Plays the operating system's default alert 'beep' sound. */ | /** Plays the operating system's default alert 'beep' sound. */ | ||||
| static void beep(); | static void beep(); | ||||
| static bool launchEmailWithAttachments (const String& targetEmailAddress, | |||||
| const String& emailSubject, | |||||
| const String& bodyText, | |||||
| const StringArray& filesToAttach); | |||||
| #if JUCE_MAC || DOXYGEN | #if JUCE_MAC || DOXYGEN | ||||
| //============================================================================== | //============================================================================== | ||||
| /** MAC ONLY - Turns a String into a pascal string. */ | /** MAC ONLY - Turns a String into a pascal string. */ | ||||