From 3b2a39b01b88a35ef4726636e99525839727baf3 Mon Sep 17 00:00:00 2001 From: hogliux Date: Fri, 10 Jul 2015 10:11:35 +0100 Subject: [PATCH 01/11] Fix typo --- modules/juce_gui_basics/components/juce_Component.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/juce_gui_basics/components/juce_Component.h b/modules/juce_gui_basics/components/juce_Component.h index a8136a6397..3d15f58601 100644 --- a/modules/juce_gui_basics/components/juce_Component.h +++ b/modules/juce_gui_basics/components/juce_Component.h @@ -443,7 +443,7 @@ public: /** Changes the component's position and size. The coordinates are relative to the top-left of the component's parent, or relative - to the origin of the screen is the component is on the desktop. + to the origin of the screen if the component is on the desktop. If this method changes the component's top-left position, it will make a synchronous call to moved(). If it changes the size, it will also make a call to resized(). @@ -459,7 +459,7 @@ public: /** Changes the component's position and size. The coordinates are relative to the top-left of the component's parent, or relative - to the origin of the screen is the component is on the desktop. + to the origin of the screen if the component is on the desktop. If this method changes the component's top-left position, it will make a synchronous call to moved(). If it changes the size, it will also make a call to resized(). From c4f5e8ebe61758b97c1c7efd0f70c671e16e51c5 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 15 Jul 2015 10:29:05 +0100 Subject: [PATCH 02/11] Fix to the WavefrontObjParser class's sort method. --- examples/Demo/Source/Demos/WavefrontObjParser.h | 15 ++++++++++++++- .../Source/Resources/WavefrontObjParser.h | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/examples/Demo/Source/Demos/WavefrontObjParser.h b/examples/Demo/Source/Demos/WavefrontObjParser.h index ca8a3b3ff0..3997305087 100644 --- a/examples/Demo/Source/Demos/WavefrontObjParser.h +++ b/examples/Demo/Source/Demos/WavefrontObjParser.h @@ -101,7 +101,20 @@ private: struct TripleIndex { TripleIndex() noexcept : vertexIndex (-1), textureIndex (-1), normalIndex (-1) {} - bool operator< (const TripleIndex& other) const noexcept { return vertexIndex < other.vertexIndex; } + + bool operator< (const TripleIndex& other) const noexcept + { + if (this == &other) + return false; + + if (vertexIndex != other.vertexIndex) + return vertexIndex < other.vertexIndex; + + if (textureIndex != other.textureIndex) + return textureIndex < other.textureIndex; + + return normalIndex < other.normalIndex; + } int vertexIndex, textureIndex, normalIndex; }; diff --git a/examples/OpenGLAppExample/Source/Resources/WavefrontObjParser.h b/examples/OpenGLAppExample/Source/Resources/WavefrontObjParser.h index ca8a3b3ff0..3997305087 100644 --- a/examples/OpenGLAppExample/Source/Resources/WavefrontObjParser.h +++ b/examples/OpenGLAppExample/Source/Resources/WavefrontObjParser.h @@ -101,7 +101,20 @@ private: struct TripleIndex { TripleIndex() noexcept : vertexIndex (-1), textureIndex (-1), normalIndex (-1) {} - bool operator< (const TripleIndex& other) const noexcept { return vertexIndex < other.vertexIndex; } + + bool operator< (const TripleIndex& other) const noexcept + { + if (this == &other) + return false; + + if (vertexIndex != other.vertexIndex) + return vertexIndex < other.vertexIndex; + + if (textureIndex != other.textureIndex) + return textureIndex < other.textureIndex; + + return normalIndex < other.normalIndex; + } int vertexIndex, textureIndex, normalIndex; }; From c2c0795aea710c013b6f0ab8b4d3652319c6d3f8 Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 15 Jul 2015 10:58:08 +0100 Subject: [PATCH 03/11] Add https/SSL support for Linux (via libcurl) --- extras/Introjucer/JuceLibraryCode/AppConfig.h | 4 + .../Project Saving/jucer_ProjectExport_Make.h | 3 + modules/juce_core/juce_core.cpp | 7 + modules/juce_core/juce_core.h | 11 + .../juce_core/native/juce_curl_Network.cpp | 472 ++++++++++++++++++ .../juce_core/native/juce_linux_Network.cpp | 3 +- 6 files changed, 499 insertions(+), 1 deletion(-) create mode 100644 modules/juce_core/native/juce_curl_Network.cpp diff --git a/extras/Introjucer/JuceLibraryCode/AppConfig.h b/extras/Introjucer/JuceLibraryCode/AppConfig.h index f39b0f4dc3..38f7f449f1 100644 --- a/extras/Introjucer/JuceLibraryCode/AppConfig.h +++ b/extras/Introjucer/JuceLibraryCode/AppConfig.h @@ -58,6 +58,10 @@ //#define JUCE_INCLUDE_ZLIB_CODE #endif +#ifndef JUCE_USE_CURL + //#define JUCE_USE_CURL +#endif + //============================================================================== // juce_graphics flags: diff --git a/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_Make.h b/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_Make.h index 975ae3ceb4..84a3507fc6 100644 --- a/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_Make.h +++ b/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_Make.h @@ -184,6 +184,9 @@ private: for (int i = 0; i < linuxLibs.size(); ++i) out << " -l" << linuxLibs[i]; + if (getProject().isConfigFlagEnabled ("JUCE_USE_CURL")) + out << " -lcurl"; + StringArray libraries; libraries.addTokens (getExternalLibrariesString(), ";", "\"'"); libraries.removeEmptyStrings(); diff --git a/modules/juce_core/juce_core.cpp b/modules/juce_core/juce_core.cpp index 4c7266ac5f..1c16a99693 100644 --- a/modules/juce_core/juce_core.cpp +++ b/modules/juce_core/juce_core.cpp @@ -82,6 +82,10 @@ #if JUCE_LINUX #include #include + + #if JUCE_USE_CURL + #include + #endif #endif #include @@ -210,6 +214,9 @@ namespace juce #include "native/juce_linux_CommonFile.cpp" #include "native/juce_linux_Files.cpp" #include "native/juce_linux_Network.cpp" +#if JUCE_USE_CURL + #include "native/juce_curl_Network.cpp" +#endif #include "native/juce_linux_SystemStats.cpp" #include "native/juce_linux_Threads.cpp" diff --git a/modules/juce_core/juce_core.h b/modules/juce_core/juce_core.h index 120773c9a4..39538b7082 100644 --- a/modules/juce_core/juce_core.h +++ b/modules/juce_core/juce_core.h @@ -126,6 +126,17 @@ #define JUCE_ZLIB_INCLUDE_PATH #endif +/** Config: JUCE_USE_CURL + Enables http/https support via libcurl (Linux only). Enabling this will add an additional + run-time dynmic dependency to libcurl. + + If you disable this then https/ssl support will not be available on linux. +*/ +#ifndef JUCE_USE_CURL + #define JUCE_USE_CURL 0 +#endif + + /* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS If enabled, this will add some exception-catching code to forward unhandled exceptions to your JUCEApplicationBase::unhandledException() callback. diff --git a/modules/juce_core/native/juce_curl_Network.cpp b/modules/juce_core/native/juce_curl_Network.cpp new file mode 100644 index 0000000000..769965673f --- /dev/null +++ b/modules/juce_core/native/juce_curl_Network.cpp @@ -0,0 +1,472 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + + +class WebInputStream : public InputStream +{ +public: + WebInputStream (const String& address, bool isPost, const MemoryBlock& postData, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, int timeOutMs, StringPairArray* responseHeaders, + const int maxRedirects) + : multi (nullptr), curl (nullptr), headerList (nullptr), lastError (CURLE_OK), + contentLength (-1), streamPos (0), + finished (false), skipBytes (0), + postBuffer (nullptr), postPosition (0) + { + statusCode = -1; + + if (init() && setOptions (address, timeOutMs, (responseHeaders != nullptr), + maxRedirects, headers, isPost, postData.getSize())) + { + connect (responseHeaders, isPost, postData, progressCallback, progressCallbackContext); + } + else + { + cleanup(); + } + } + + ~WebInputStream() + { + cleanup(); + } + + //============================================================================== + // Input Stream overrides + bool isError() const { return curl == nullptr || lastError != CURLE_OK; } + bool isExhausted() override { return (isError() || finished) && curlBuffer.getSize() == 0; } + int64 getPosition() override { return streamPos; } + int64 getTotalLength() override { return contentLength; } + + int read (void* buffer, int bytesToRead) override + { + return readOrSkip (buffer, bytesToRead, false); + } + + bool setPosition (int64 wantedPos) override + { + const int amountToSkip = static_cast (wantedPos - getPosition()); + + if (amountToSkip < 0) + return false; + + if (amountToSkip == 0) + return true; + + const int actuallySkipped = readOrSkip (nullptr, amountToSkip, true); + + return actuallySkipped == amountToSkip; + } + + //============================================================================== + int statusCode; + +private: + //============================================================================== + bool init() + { + multi = curl_multi_init(); + + if (multi != nullptr) + { + curl = curl_easy_init(); + + if (curl != nullptr) + if (curl_multi_add_handle (multi, curl) == CURLM_OK) + return true; + } + + cleanup(); + return false; + } + + void cleanup() + { + if (curl != nullptr) + { + curl_multi_remove_handle (multi, curl); + + if (headerList != nullptr) + { + curl_slist_free_all (headerList); + headerList = nullptr; + } + + curl_easy_cleanup (curl); + curl = nullptr; + } + + if (multi != nullptr) + { + curl_multi_cleanup (multi); + multi = nullptr; + } + } + + //============================================================================== + bool setOptions (const String& address, int timeOutMs, bool wantsHeaders, + const int maxRedirects, const String& headers, + bool isPost, size_t postSize) + { + if (curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK + && curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK + && curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK + && curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast (maxRedirects)) == CURLE_OK) + { + if (isPost) + { + if (curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK + || curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK) + return false; + + if (curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK + || curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast (postSize)) != CURLE_OK) + return false; + } + + // do we want to parse the headers + if (wantsHeaders) + { + if (curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK + || curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK) + return false; + } + + if (headers.isNotEmpty()) + { + const StringArray headerLines = StringArray::fromLines (headers); + + // fromLines will always return at least one line if the string is not empty + jassert (headerLines.size() > 0); + headerList = curl_slist_append (headerList, headerLines [0].toRawUTF8()); + + for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i) + headerList = curl_slist_append (headerList, headerLines [i].toRawUTF8()); + + if (headerList == nullptr) + return false; + + if (curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK) + return false; + } + + if (timeOutMs > 0) + { + long timeOutSecs = static_cast (ceil (static_cast (timeOutMs) / 1000.0)); + + if (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK) + return false; + } + + return true; + } + + return false; + } + + void connect (StringPairArray* responseHeaders, bool isPost, const MemoryBlock& postData, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + { + if (isPost) + postBuffer = &postData; + + size_t lastPos = static_cast (-1); + + // step until either: 1) there is an error 2) the transaction is complete + // or 3) data is in the in buffer + while ((! finished) && curlBuffer.getSize() == 0 && curl != nullptr) + { + singleStep(); + + // call callbacks if this is a post request + if (isPost && progressCallback != nullptr && lastPos != postPosition) + { + lastPos = postPosition; + + if (! progressCallback (progressCallbackContext, + static_cast (lastPos), + static_cast (postData.getSize()))) + { + // user has decided to abort the transaction + cleanup(); + return; + } + } + } + + long responseCode; + if (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK) + statusCode = static_cast (responseCode); + + // parse headers + if (responseHeaders != nullptr) + parseHttpHeaders (*responseHeaders); + + // get content length size + double curlLength; + if (curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK) + contentLength = static_cast (curlLength); + } + + void finish() + { + if (curl == nullptr) + return; + + for (;;) + { + int cnt = 0; + + if (CURLMsg* msg = curl_multi_info_read (multi, &cnt)) + { + if (msg->msg == CURLMSG_DONE && msg->easy_handle == curl) + { + lastError = msg->data.result; // this is the error that stopped our process from continuing + break; + } + } + else + { + break; + } + } + + finished = true; + } + + //============================================================================== + void singleStep() + { + if (curl == nullptr || lastError != CURLE_OK) + return; + + fd_set fdread, fdwrite, fdexcep; + int maxfd = -1; + long curl_timeo; + + if ((lastError = (int) curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK) + return; + + // why 980? see http://curl.haxx.se/libcurl/c/curl_multi_timeout.html + if (curl_timeo < 0) + curl_timeo = 980; + + struct timeval tv; + tv.tv_sec = curl_timeo / 1000; + tv.tv_usec = (curl_timeo % 1000) * 1000; + + FD_ZERO (&fdread); + FD_ZERO (&fdwrite); + FD_ZERO (&fdexcep); + + + if ((lastError = (int) curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK) + return; + + if (maxfd != -1) + { + if (select (maxfd + 1, &fdread, &fdwrite, &fdexcep, &tv) < 0) + { + lastError = -1; + return; + } + } + else + { + // if curl does not return any sockets for to wait on, then the doc says to wait 100 ms + Thread::sleep (100); + } + + int still_running = 0; + int curlRet; + + while ((curlRet = (int) curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM) + {} + + if ((lastError = curlRet) != CURLM_OK) + return; + + if (still_running <= 0) + finish(); + } + + int readOrSkip (void* buffer, int bytesToRead, bool skip) + { + if (bytesToRead <= 0) + return 0; + + size_t pos = 0; + size_t len = static_cast (bytesToRead); + + while (len > 0) + { + size_t bufferBytes = curlBuffer.getSize(); + bool removeSection = true; + + if (bufferBytes == 0) + { + // do not call curl again if we are finished + if (finished || curl == nullptr) + return static_cast (pos); + + skipBytes = skip ? len : 0; + singleStep(); + + // update the amount that was read/skipped from curl + bufferBytes = skip ? len - skipBytes : curlBuffer.getSize(); + removeSection = ! skip; + } + + // can we copy data from the internal buffer? + if (bufferBytes > 0) + { + size_t max = jmin (len, bufferBytes); + + if (! skip) + memcpy (addBytesToPointer (buffer, pos), curlBuffer.getData(), max); + + pos += max; + streamPos += static_cast (max); + len -= max; + + if (removeSection) + curlBuffer.removeSection (0, max); + } + } + + return static_cast (pos); + } + + + //============================================================================== + void parseHttpHeaders (StringPairArray& responseHeaders) + { + StringArray headerLines = StringArray::fromLines (curlHeaders); + + // ignore the first line as this is the status line + for (int i = 1; i < headerLines.size(); ++i) + { + const String& headersEntry = headerLines[i]; + + if (headersEntry.isNotEmpty()) + { + const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); + const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue (responseHeaders [key]); + responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + } + } + + + //============================================================================== + // CURL callbacks + size_t curlWriteCallback (char* ptr, size_t size, size_t nmemb) + { + if (curl == nullptr || lastError != CURLE_OK) + return 0; + + const size_t len = size * nmemb; + + // skip bytes if necessary + size_t max = jmin (skipBytes, len); + skipBytes -= max; + + if (len > max) + curlBuffer.append (ptr + max, len - max); + + return len; + } + + size_t curlReadCallback (char* ptr, size_t size, size_t nmemb) + { + if (curl == nullptr || postBuffer == nullptr || lastError != CURLE_OK) + return 0; + + const size_t len = size * nmemb; + + size_t max = jmin (postBuffer->getSize() - postPosition, len); + memcpy (ptr, (char*)postBuffer->getData() + postPosition, max); + postPosition += max; + + return max; + } + + size_t curlHeaderCallback (char* ptr, size_t size, size_t nmemb) + { + if (curl == nullptr || lastError != CURLE_OK) + return 0; + + size_t len = size * nmemb; + + curlHeaders += String (ptr, len); + return len; + } + + //============================================================================== + // Static method wrappers + static size_t StaticCurlWrite (char* ptr, size_t size, size_t nmemb, void* userdata) + { + WebInputStream* wi = reinterpret_cast (userdata); + return wi->curlWriteCallback (ptr, size, nmemb); + } + + static size_t StaticCurlRead (char* ptr, size_t size, size_t nmemb, void* userdata) + { + WebInputStream* wi = reinterpret_cast (userdata); + return wi->curlReadCallback (ptr, size, nmemb); + } + + static size_t StaticCurlHeader (char* ptr, size_t size, size_t nmemb, void* userdata) + { + WebInputStream* wi = reinterpret_cast (userdata); + return wi->curlHeaderCallback (ptr, size, nmemb); + } + +private: + CURLM* multi; + CURL* curl; + struct curl_slist* headerList; + int lastError; + + //============================================================================== + // internal buffers and buffer positions + int64 contentLength, streamPos; + MemoryBlock curlBuffer; + String curlHeaders; + bool finished; + size_t skipBytes; + + //============================================================================== + // Http POST variables + const MemoryBlock* postBuffer; + size_t postPosition; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) +}; diff --git a/modules/juce_core/native/juce_linux_Network.cpp b/modules/juce_core/native/juce_linux_Network.cpp index 61553bdd9d..662c06d666 100644 --- a/modules/juce_core/native/juce_linux_Network.cpp +++ b/modules/juce_core/native/juce_linux_Network.cpp @@ -67,8 +67,8 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEma return false; } - //============================================================================== +#if ! JUCE_USE_CURL class WebInputStream : public InputStream { public: @@ -455,3 +455,4 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; +#endif From 0e6c3ab68fb4e155a607e6a0301598e6dff2c923 Mon Sep 17 00:00:00 2001 From: hogliux Date: Thu, 16 Jul 2015 12:33:10 +0100 Subject: [PATCH 04/11] Add all possible orientations to the UISupportedInterfaceOrientations plist element on iOS --- .../Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h | 1 + 1 file changed, 1 insertion(+) diff --git a/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h b/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h index 23fa9dfc8d..f1b64bc3d7 100644 --- a/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h +++ b/extras/Introjucer/Source/Project Saving/jucer_ProjectExport_XCode.h @@ -654,6 +654,7 @@ private: static const char* kDefaultiOSOrientationStrings[] = { "UIInterfaceOrientationPortrait", + "UIInterfaceOrientationPortraitUpsideDown", "UIInterfaceOrientationLandscapeLeft", "UIInterfaceOrientationLandscapeRight", nullptr From d46ea64aa5843f5729c2eaeb63a8c009135d91b7 Mon Sep 17 00:00:00 2001 From: Timur Doumler Date: Fri, 17 Jul 2015 09:47:10 +0100 Subject: [PATCH 05/11] LookAndFeel: added method to specify a custom font for Alert Window title. --- modules/juce_graphics/fonts/juce_Font.h | 6 +++++- .../lookandfeel/juce_LookAndFeel_V2.cpp | 6 ++++++ .../lookandfeel/juce_LookAndFeel_V2.h | 15 ++++++++++++++ .../windows/juce_AlertWindow.cpp | 20 ++++++++++--------- .../windows/juce_AlertWindow.h | 1 + 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/modules/juce_graphics/fonts/juce_Font.h b/modules/juce_graphics/fonts/juce_Font.h index 3da239d731..429e35e435 100644 --- a/modules/juce_graphics/fonts/juce_Font.h +++ b/modules/juce_graphics/fonts/juce_Font.h @@ -279,8 +279,12 @@ public: //============================================================================== /** Makes the font bold or non-bold. */ void setBold (bool shouldBeBold); - /** Returns a copy of this font with the bold attribute set. */ + + /** Returns a copy of this font with the bold attribute set. + If the font does not have a bold version, this will return the default font. + */ Font boldened() const; + /** Returns true if the font is bold. */ bool isBold() const noexcept; diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 2d894e567b..973955d767 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -490,6 +490,12 @@ int LookAndFeel_V2::getAlertWindowButtonHeight() return 28; } +Font LookAndFeel_V2::getAlertWindowTitleFont() +{ + Font messageFont = getAlertWindowMessageFont(); + return messageFont.withHeight (messageFont.getHeight() * 1.1f).boldened(); +} + Font LookAndFeel_V2::getAlertWindowMessageFont() { return Font (15.0f); diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h index 40ccad202b..163176941f 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -67,7 +67,22 @@ public: void drawAlertBox (Graphics&, AlertWindow&, const Rectangle& textArea, TextLayout&) override; int getAlertBoxWindowFlags() override; int getAlertWindowButtonHeight() override; + + /** Override this function to supply a custom font for the alert window title. + This default implementation will use a boldened and slightly larger version + of the alert window message font. + + @see getAlertWindowMessageFont. + */ + Font getAlertWindowTitleFont() override; + + /** Override this function to supply a custom font for the alert window message. + This default implementation will use the default font with height set to 15.0f. + + @see getAlertWindowTitleFont + */ Font getAlertWindowMessageFont() override; + Font getAlertWindowFont() override; //============================================================================== diff --git a/modules/juce_gui_basics/windows/juce_AlertWindow.cpp b/modules/juce_gui_basics/windows/juce_AlertWindow.cpp index 1ce0ae6482..c3aebbddbe 100644 --- a/modules/juce_gui_basics/windows/juce_AlertWindow.cpp +++ b/modules/juce_gui_basics/windows/juce_AlertWindow.cpp @@ -343,22 +343,24 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) const int titleH = 24; const int iconWidth = 80; - const Font font (getLookAndFeel().getAlertWindowMessageFont()); + LookAndFeel& lookAndFeel = getLookAndFeel(); - const int wid = jmax (font.getStringWidth (text), - font.getStringWidth (getName())); + const Font messageFont (lookAndFeel.getAlertWindowMessageFont()); - const int sw = (int) std::sqrt (font.getHeight() * wid); + const int wid = jmax (messageFont.getStringWidth (text), + messageFont.getStringWidth (getName())); + + const int sw = (int) std::sqrt (messageFont.getHeight() * wid); int w = jmin (300 + sw * 2, (int) (getParentWidth() * 0.7f)); const int edgeGap = 10; const int labelHeight = 18; int iconSpace = 0; AttributedString attributedText; - attributedText.append (getName(), font.withHeight (font.getHeight() * 1.1f).boldened()); + attributedText.append (getName(), lookAndFeel.getAlertWindowTitleFont()); if (text.isNotEmpty()) - attributedText.append ("\n\n" + text, font); + attributedText.append ("\n\n" + text, messageFont); attributedText.setColour (findColour (textColourId)); @@ -383,18 +385,18 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) int buttonW = 40; for (int i = 0; i < buttons.size(); ++i) - buttonW += 16 + buttons.getUnchecked(i)->getWidth(); + buttonW += 16 + buttons.getUnchecked (i)->getWidth(); w = jmax (buttonW, w); h += (textBoxes.size() + comboBoxes.size() + progressBars.size()) * 50; if (buttons.size() > 0) - h += 20 + buttons.getUnchecked(0)->getHeight(); + h += 20 + buttons.getUnchecked (0)->getHeight(); for (int i = customComps.size(); --i >= 0;) { - Component* c = customComps.getUnchecked(i); + Component* c = customComps.getUnchecked (i); w = jmax (w, (c->getWidth() * 100) / 80); h += 10 + c->getHeight(); diff --git a/modules/juce_gui_basics/windows/juce_AlertWindow.h b/modules/juce_gui_basics/windows/juce_AlertWindow.h index 733da87394..26aae69c5f 100644 --- a/modules/juce_gui_basics/windows/juce_AlertWindow.h +++ b/modules/juce_gui_basics/windows/juce_AlertWindow.h @@ -437,6 +437,7 @@ public: virtual int getAlertWindowButtonHeight() = 0; + virtual Font getAlertWindowTitleFont() = 0; virtual Font getAlertWindowMessageFont() = 0; virtual Font getAlertWindowFont() = 0; }; From 301dfaf5018589f4f63ad99b9ca5068282fbae1d Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 17 Jul 2015 11:26:07 +0100 Subject: [PATCH 06/11] Fix for shift-delete shortcut in text editors --- .../keyboard/juce_TextEditorKeyMapper.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h b/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h index f09dc9b9bd..59b006b00e 100644 --- a/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h +++ b/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h @@ -85,12 +85,6 @@ struct TextEditorKeyMapper if (key.isKeyCode (KeyPress::pageDownKey)) return target.pageDown (isShiftDown); } - if (numCtrlAltCommandKeys < 2) - { - if (key.isKeyCode (KeyPress::backspaceKey)) return target.deleteBackwards (ctrlOrAltDown); - if (key.isKeyCode (KeyPress::deleteKey)) return target.deleteForwards (ctrlOrAltDown); - } - if (key == KeyPress ('c', ModifierKeys::commandModifier, 0) || key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0)) return target.copyToClipboard(); @@ -103,6 +97,13 @@ struct TextEditorKeyMapper || key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0)) return target.pasteFromClipboard(); + // NB: checking for delete must happen after the earlier check for shift + delete + if (numCtrlAltCommandKeys < 2) + { + if (key.isKeyCode (KeyPress::backspaceKey)) return target.deleteBackwards (ctrlOrAltDown); + if (key.isKeyCode (KeyPress::deleteKey)) return target.deleteForwards (ctrlOrAltDown); + } + if (key == KeyPress ('a', ModifierKeys::commandModifier, 0)) return target.selectAll(); From e65bd0769ac82697d9b6b1f9f9fe7da5d3352f67 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 17 Jul 2015 12:06:06 +0100 Subject: [PATCH 07/11] Added support for reading chunked HTTP streams on linux. --- .../juce_core/native/juce_linux_Network.cpp | 79 ++++++++++++++++--- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/modules/juce_core/native/juce_linux_Network.cpp b/modules/juce_core/native/juce_linux_Network.cpp index 662c06d666..a2339c5de0 100644 --- a/modules/juce_core/native/juce_linux_Network.cpp +++ b/modules/juce_core/native/juce_linux_Network.cpp @@ -77,8 +77,9 @@ public: const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, const int maxRedirects) : statusCode (0), socketHandle (-1), levelsOfRedirection (0), - address (address_), headers (headers_), postData (postData_), position (0), - finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (maxRedirects) + address (address_), headers (headers_), postData (postData_), contentLength (-1), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (maxRedirects), + chunkEnd (0), isChunked (false), readingChunk (false) { statusCode = createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); @@ -104,18 +105,63 @@ public: bool isError() const { return socketHandle < 0; } bool isExhausted() override { return finished; } int64 getPosition() override { return position; } - - int64 getTotalLength() override - { - //xxx to do - return -1; - } + int64 getTotalLength() override { return contentLength; } int read (void* buffer, int bytesToRead) override { if (finished || isError()) return 0; + if (isChunked && ! readingChunk) + { + if (position >= chunkEnd) + { + const ScopedValueSetter setter (readingChunk, true, false); + MemoryOutputStream chunkLengthBuffer; + char c = 0; + + if (chunkEnd > 0) + { + if (read (&c, 1) != 1 || c != '\r' + || read (&c, 1) != 1 || c != '\n') + { + finished = true; + return 0; + } + } + + while (chunkLengthBuffer.getDataSize() < 512 && ! (finished || isError())) + { + if (read (&c, 1) != 1) + { + finished = true; + return 0; + } + + if (c == '\r') + continue; + + if (c == '\n') + break; + + chunkLengthBuffer.writeByte (c); + } + + const int64 chunkSize = chunkLengthBuffer.toString().trimStart().getHexValue64(); + + if (chunkSize == 0) + { + finished = true; + return 0; + } + + chunkEnd += chunkSize; + } + + if (bytesToRead > chunkEnd - position) + bytesToRead = chunkEnd - position; + } + fd_set readbits; FD_ZERO (&readbits); FD_SET (socketHandle, &readbits); @@ -131,7 +177,9 @@ public: if (bytesRead == 0) finished = true; - position += bytesRead; + if (! readingChunk) + position += bytesRead; + return bytesRead; } @@ -165,11 +213,13 @@ private: StringArray headerLines; String address, headers; MemoryBlock postData; - int64 position; + int64 contentLength, position; bool finished; const bool isPost; const int timeOutMs; const int numRedirectsToFollow; + int64 chunkEnd; + bool isChunked, readingChunk; void closeSocket (bool resetLevelsOfRedirection = true) { @@ -298,6 +348,13 @@ private: return createConnection (progressCallback, progressCallbackContext, numRedirects); } + String contentLengthString (findHeaderItem (headerLines, "Content-Length:")); + + if (contentLengthString.isNotEmpty()) + contentLength = contentLengthString.getLargeIntValue(); + + isChunked = (findHeaderItem (headerLines, "Transfer-Encoding:") == "chunked"); + return status; } @@ -345,7 +402,7 @@ private: static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, int port) { - dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host; + dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.1\r\nHost: " << host; /* HTTP spec 14.23 says that the port number must be included in the header if it is not 80 */ if (port != 80) From d635ced7003d648461cc1de2002ff97500353c8c Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 17 Jul 2015 12:25:40 +0100 Subject: [PATCH 08/11] Minor comment addition --- modules/juce_gui_basics/widgets/juce_TableListBox.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/juce_gui_basics/widgets/juce_TableListBox.h b/modules/juce_gui_basics/widgets/juce_TableListBox.h index d631125509..d308e00ac0 100644 --- a/modules/juce_gui_basics/widgets/juce_TableListBox.h +++ b/modules/juce_gui_basics/widgets/juce_TableListBox.h @@ -56,6 +56,9 @@ public: The graphics context has its origin at the row's top-left, and your method should fill the area specified by the width and height parameters. + + Note that the rowNumber value may be greater than the number of rows in your + list, so be careful that you don't assume it's less than getNumRows(). */ virtual void paintRowBackground (Graphics&, int rowNumber, From 6ea3b5ba49eaaac658adda90a0546dcaa678539d Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 17 Jul 2015 12:34:14 +0100 Subject: [PATCH 09/11] Added Final Cut to the PluginHostType list of recognised hosts. --- .../utility/juce_PluginHostType.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/juce_audio_plugin_client/utility/juce_PluginHostType.h b/modules/juce_audio_plugin_client/utility/juce_PluginHostType.h index 9b0a07aa2b..c707efe061 100644 --- a/modules/juce_audio_plugin_client/utility/juce_PluginHostType.h +++ b/modules/juce_audio_plugin_client/utility/juce_PluginHostType.h @@ -47,6 +47,7 @@ public: CakewalkSonarGeneric, DigidesignProTools, DigitalPerformer, + FinalCut, FruityLoops, MagixSamplitude, MergingPyramix, @@ -72,8 +73,8 @@ public: StudioOne, Tracktion3, TracktionGeneric, - WaveBurner, - VBVSTScanner + VBVSTScanner, + WaveBurner }; HostType type; @@ -87,6 +88,7 @@ public: bool isCubase7orLater() const noexcept { return isCubase() && ! (type == SteinbergCubase4 || type == SteinbergCubase5 || type == SteinbergCubase6); } bool isCubaseBridged() const noexcept { return type == SteinbergCubase5Bridged; } bool isLogic() const noexcept { return type == AppleLogic; } + bool isFinalCut() const noexcept { return type == FinalCut; } bool isFruityLoops() const noexcept { return type == FruityLoops; } bool isNuendo() const noexcept { return type == SteinbergNuendo3 || type == SteinbergNuendo4 || type == SteinbergNuendo5 || type == SteinbergNuendoGeneric; } bool isPremiere() const noexcept { return type == AdobePremierePro; } @@ -120,6 +122,7 @@ public: case CakewalkSonarGeneric: return "Cakewalk Sonar"; case DigidesignProTools: return "ProTools"; case DigitalPerformer: return "DigitalPerformer"; + case FinalCut: return "Final Cut"; case FruityLoops: return "FruityLoops"; case MagixSamplitude: return "Magix Samplitude"; case MergingPyramix: return "Pyramix"; @@ -167,6 +170,8 @@ private: const String hostFilename (File (hostPath).getFileName()); #if JUCE_MAC + if (hostPath.containsIgnoreCase ("Final Cut Pro.app")) return FinalCut; + if (hostPath.containsIgnoreCase ("Final Cut Pro Trial.app")) return FinalCut; if (hostPath.containsIgnoreCase ("Live 6.")) return AbletonLive6; if (hostPath.containsIgnoreCase ("Live 7.")) return AbletonLive7; if (hostPath.containsIgnoreCase ("Live 8.")) return AbletonLive8; From 2246cd50fba807a2494824379c65bafe84c3c321 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 17 Jul 2015 13:07:11 +0100 Subject: [PATCH 10/11] Avoided a race condition when removing i/o nodes from an AudioProcessorGraph --- .../processors/juce_AudioProcessorGraph.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index a442fe3034..7e6832a441 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -977,7 +977,6 @@ bool AudioProcessorGraph::removeNode (const uint32 nodeId) { if (nodes.getUnchecked(i)->nodeId == nodeId) { - nodes.getUnchecked(i)->setParentGraph (nullptr); nodes.remove (i); triggerAsyncUpdate(); From 52825b180b03fe45dfd0df5621e4edab6574417f Mon Sep 17 00:00:00 2001 From: hogliux Date: Fri, 17 Jul 2015 18:13:17 +0100 Subject: [PATCH 11/11] Fix build error with newest AAX SDK --- modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp index 6b21ddf662..72bdae210c 100644 --- a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp @@ -918,9 +918,11 @@ struct AAXClasses for (int parameterIndex = 0; parameterIndex < numParameters; ++parameterIndex) { + AAX_CString paramName (audioProcessor.getParameterName (parameterIndex, 31).toRawUTF8()); + AAX_IParameter* parameter = new AAX_CParameter (IndexAsParamID (parameterIndex), - audioProcessor.getParameterName (parameterIndex, 31).toRawUTF8(), + paramName, audioProcessor.getParameterDefaultValue (parameterIndex), AAX_CLinearTaperDelegate(), AAX_CNumberDisplayDelegate(),