diff --git a/build/linux/JUCE.make b/build/linux/JUCE.make index 701b2bc20f..73e31908ba 100644 --- a/build/linux/JUCE.make +++ b/build/linux/JUCE.make @@ -411,6 +411,7 @@ OBJECTS := \ $(OBJDIR)/juce_linux_Network.o \ $(OBJDIR)/juce_linux_SystemStats.o \ $(OBJDIR)/juce_linux_Threads.o \ + $(OBJDIR)/juce_linux_WebBrowserComponent.o \ $(OBJDIR)/juce_linux_Windowing.o \ MKDIR_TYPE := msdos @@ -2327,6 +2328,11 @@ $(OBJDIR)/juce_linux_Threads.o: platform_specific_code/juce_linux_Threads.cpp @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o $@ -c $< +$(OBJDIR)/juce_linux_WebBrowserComponent.o: platform_specific_code/juce_linux_WebBrowserComponent.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o $@ -c $< + $(OBJDIR)/juce_linux_Windowing.o: platform_specific_code/juce_linux_Windowing.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) diff --git a/build/linux/platform_specific_code/juce_linux_WebBrowserComponent.cpp b/build/linux/platform_specific_code/juce_linux_WebBrowserComponent.cpp new file mode 100644 index 0000000000..0fa0590c9e --- /dev/null +++ b/build/linux/platform_specific_code/juce_linux_WebBrowserComponent.cpp @@ -0,0 +1,135 @@ +/* + ============================================================================== + + 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" + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_appframework/gui/components/special/juce_WebBrowserComponent.h" + + +/* + Sorry.. This class isn't implemented on Linux! +*/ + +//============================================================================== +WebBrowserComponent::WebBrowserComponent() + : browser (0), + blankPageShown (false) +{ + setOpaque (true); +} + +WebBrowserComponent::~WebBrowserComponent() +{ +} + +//============================================================================== +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + + +} + +void WebBrowserComponent::stop() +{ +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + +} + +//============================================================================== +void WebBrowserComponent::paint (Graphics& g) +{ + g.fillAll (Colours::white); +} + +void WebBrowserComponent::checkWindowAssociation() +{ +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::moved() +{ +} + +void WebBrowserComponent::resized() +{ +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String& url) +{ + return true; +} + + +END_JUCE_NAMESPACE diff --git a/build/macosx/Juce.xcodeproj/project.pbxproj b/build/macosx/Juce.xcodeproj/project.pbxproj index 7451503491..20b7d1399f 100644 --- a/build/macosx/Juce.xcodeproj/project.pbxproj +++ b/build/macosx/Juce.xcodeproj/project.pbxproj @@ -208,6 +208,8 @@ 847F72DC0C2702A000CBECEC /* juce_DirectoryContentsDisplayComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 847F72D80C2702A000CBECEC /* juce_DirectoryContentsDisplayComponent.h */; }; 847F72DD0C2702A000CBECEC /* juce_FileTreeComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 847F72D90C2702A000CBECEC /* juce_FileTreeComponent.cpp */; }; 847F72DE0C2702A000CBECEC /* juce_FileTreeComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 847F72DA0C2702A000CBECEC /* juce_FileTreeComponent.h */; }; + 848A560B0DDDD3E40041C706 /* juce_mac_WebBrowserComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 848A560A0DDDD3E40041C706 /* juce_mac_WebBrowserComponent.mm */; }; + 848A560E0DDDD3FB0041C706 /* juce_WebBrowserComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 848A560D0DDDD3FB0041C706 /* juce_WebBrowserComponent.h */; }; 8495BB8C0D8067B2001D9C0B /* juce_AudioThumbnail.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8495BB880D8067B2001D9C0B /* juce_AudioThumbnail.cpp */; }; 8495BB8D0D8067B2001D9C0B /* juce_AudioThumbnail.h in Headers */ = {isa = PBXBuildFile; fileRef = 8495BB890D8067B2001D9C0B /* juce_AudioThumbnail.h */; }; 8495BB8E0D8067B2001D9C0B /* juce_AudioThumbnailCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8495BB8A0D8067B2001D9C0B /* juce_AudioThumbnailCache.cpp */; }; @@ -897,6 +899,8 @@ 847F72D80C2702A000CBECEC /* juce_DirectoryContentsDisplayComponent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce_DirectoryContentsDisplayComponent.h; path = filebrowser/juce_DirectoryContentsDisplayComponent.h; sourceTree = ""; }; 847F72D90C2702A000CBECEC /* juce_FileTreeComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = juce_FileTreeComponent.cpp; path = filebrowser/juce_FileTreeComponent.cpp; sourceTree = ""; }; 847F72DA0C2702A000CBECEC /* juce_FileTreeComponent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce_FileTreeComponent.h; path = filebrowser/juce_FileTreeComponent.h; sourceTree = ""; }; + 848A560A0DDDD3E40041C706 /* juce_mac_WebBrowserComponent.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_WebBrowserComponent.mm; sourceTree = ""; }; + 848A560D0DDDD3FB0041C706 /* juce_WebBrowserComponent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_WebBrowserComponent.h; sourceTree = ""; }; 8495BB880D8067B2001D9C0B /* juce_AudioThumbnail.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = juce_AudioThumbnail.cpp; sourceTree = ""; }; 8495BB890D8067B2001D9C0B /* juce_AudioThumbnail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = juce_AudioThumbnail.h; sourceTree = ""; }; 8495BB8A0D8067B2001D9C0B /* juce_AudioThumbnailCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = juce_AudioThumbnailCache.cpp; sourceTree = ""; }; @@ -1554,6 +1558,7 @@ 847D06460DD88D1100DF6A61 /* juce_mac_Network.mm */, 84A4882508A22E2400752A2B /* juce_mac_SystemStats.cpp */, 84A4882608A22E2400752A2B /* juce_mac_Threads.cpp */, + 848A560A0DDDD3E40041C706 /* juce_mac_WebBrowserComponent.mm */, 84A4882708A22E2400752A2B /* juce_mac_Windowing.cpp */, ); name = "mac specific code"; @@ -1937,6 +1942,7 @@ 84A488EB08A22E4900752A2B /* juce_PreferencesPanel.h */, 84A488EC08A22E4900752A2B /* juce_QuickTimeMovieComponent.cpp */, 84A488ED08A22E4900752A2B /* juce_QuickTimeMovieComponent.h */, + 848A560D0DDDD3FB0041C706 /* juce_WebBrowserComponent.h */, ); name = special; path = ../../src/juce_appframework/gui/components/special; @@ -2904,6 +2910,7 @@ 8495BB960D806BDA001D9C0B /* juce_InputSource.h in Headers */, 84581EEB0D9148C500AE1A4C /* juce_QuickTimeAudioFormat.h in Headers */, 84022DFD0DAE4CB9004CF59A /* juce_mac_HTTPStream.h in Headers */, + 848A560E0DDDD3FB0041C706 /* juce_WebBrowserComponent.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3320,6 +3327,7 @@ 84581EEA0D9148C500AE1A4C /* juce_QuickTimeAudioFormat.cpp in Sources */, 84CE8A410DC5D7D600799A0E /* juce_mac_AudioCDBurner.mm in Sources */, 847D06470DD88D1100DF6A61 /* juce_mac_Network.mm in Sources */, + 848A560B0DDDD3E40041C706 /* juce_mac_WebBrowserComponent.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/build/macosx/platform_specific_code/juce_mac_WebBrowserComponent.mm b/build/macosx/platform_specific_code/juce_mac_WebBrowserComponent.mm new file mode 100644 index 0000000000..6741996df7 --- /dev/null +++ b/build/macosx/platform_specific_code/juce_mac_WebBrowserComponent.mm @@ -0,0 +1,442 @@ +/* + ============================================================================== + + 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" + +#import +#import +#import +#import + +BEGIN_JUCE_NAMESPACE +#include "../../../src/juce_appframework/events/juce_Timer.h" +#include "../../../src/juce_appframework/gui/components/special/juce_WebBrowserComponent.h" +END_JUCE_NAMESPACE + +//============================================================================== +@interface DownloadClickDetector : NSObject +{ + juce::WebBrowserComponent* ownerComponent; +} + +- (DownloadClickDetector*) init: (juce::WebBrowserComponent*) ownerComponent; + +- (void) webView: (WebView*) webView decidePolicyForNavigationAction: (NSDictionary*) actionInformation + request: (NSURLRequest*) request + frame: (WebFrame*) frame + decisionListener: (id) listener; +@end + +//============================================================================== +@implementation DownloadClickDetector + +- (DownloadClickDetector*) init: (juce::WebBrowserComponent*) ownerComponent_ +{ + [super init]; + ownerComponent = ownerComponent_; + return self; +} + +- (void) webView: (WebView*) sender decidePolicyForNavigationAction: (NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener +{ + NSURL* url = [actionInformation valueForKey: @"WebActionOriginalURLKey"]; + + if (ownerComponent->pageAboutToLoad (juce::String::fromUTF8 ((const juce::uint8*) [[url absoluteString] UTF8String]))) + [listener use]; + else + [listener ignore]; +} + +@end + +BEGIN_JUCE_NAMESPACE + +//============================================================================== +class WebBrowserComponentInternal : public Timer +{ +public: + WebBrowserComponentInternal (WebBrowserComponent* const owner_) + : owner (owner_), + view (0), + webView (0) + { + HIWebViewCreate (&view); + + ComponentPeer* const peer = owner_->getPeer(); + jassert (peer != 0); + + if (view != 0 && peer != 0) + { + WindowRef parentWindow = (WindowRef) peer->getNativeHandle(); + + WindowAttributes attributes; + GetWindowAttributes (parentWindow, &attributes); + + HIViewRef parentView = 0; + + if ((attributes & kWindowCompositingAttribute) != 0) + { + HIViewRef root = HIViewGetRoot (parentWindow); + HIViewFindByID (root, kHIViewWindowContentID, &parentView); + + if (parentView == 0) + parentView = root; + } + else + { + GetRootControl (parentWindow, (ControlRef*) &parentView); + + if (parentView == 0) + CreateRootControl (parentWindow, (ControlRef*) &parentView); + } + + HIViewAddSubview (parentView, view); + updateBounds(); + show(); + + webView = HIWebViewGetWebView (view); + + clickListener = [[DownloadClickDetector alloc] init: owner_]; + [webView setPolicyDelegate: clickListener]; + } + + startTimer (500); + } + + ~WebBrowserComponentInternal() + { + [webView setPolicyDelegate: nil]; + [clickListener release]; + + if (view != 0) + CFRelease (view); + } + + // Horrific bodge-workaround for the fact that the webview somehow hangs onto key + // focus when you pop up a new window, no matter what that window does to + // try to grab focus for itself. This catches such a situation and forces + // focus away from the webview, then back to the place it should be.. + void timerCallback() + { + WindowRef viewWindow = HIViewGetWindow (view); + WindowRef focusedWindow = GetUserFocusWindow(); + + if (focusedWindow != viewWindow) + { + if (HIViewSubtreeContainsFocus (view)) + { + HIViewAdvanceFocus (HIViewGetRoot (viewWindow), 0); + HIViewAdvanceFocus (HIViewGetRoot (focusedWindow), 0); + } + } + } + + void show() + { + HIViewSetVisible (view, true); + } + + void hide() + { + HIViewSetVisible (view, false); + } + + void goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) + { + char** headerNamesAsChars = 0; + char** headerValuesAsChars = 0; + int numHeaders = 0; + + if (headers != 0) + { + numHeaders = headers->size(); + + headerNamesAsChars = (char**) juce_malloc (sizeof (char*) * numHeaders); + headerValuesAsChars = (char**) juce_malloc (sizeof (char*) * numHeaders); + + int i; + for (i = 0; i < numHeaders; ++i) + { + const String headerName ((*headers)[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); + headerNamesAsChars[i] = (char*) juce_calloc (headerName.copyToUTF8 (0)); + headerName.copyToUTF8 ((juce::uint8*) headerNamesAsChars[i]); + + const String headerValue ((*headers)[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); + headerValuesAsChars[i] = (char*) juce_calloc (headerValue.copyToUTF8 (0)); + headerValue.copyToUTF8 ((juce::uint8*) headerValuesAsChars[i]); + } + } + + sendWebViewToURL ((const char*) url.toUTF8(), + (const char**) headerNamesAsChars, + (const char**) headerValuesAsChars, + numHeaders, + postData != 0 ? (const char*) postData->getData() : 0, + postData != 0 ? postData->getSize() : 0); + + for (int i = 0; i < numHeaders; ++i) + { + juce_free (headerNamesAsChars[i]); + juce_free (headerValuesAsChars[i]); + } + + juce_free (headerNamesAsChars); + juce_free (headerValuesAsChars); + } + + void goBack() + { + [webView goBack]; + } + + void goForward() + { + [webView goForward]; + } + + void stop() + { + [webView stopLoading]; + } + + void updateBounds() + { + HIRect r; + r.origin.x = (float) owner->getScreenX() - owner->getTopLevelComponent()->getScreenX(); + r.origin.y = (float) owner->getScreenY() - owner->getTopLevelComponent()->getScreenY(); + r.size.width = (float) owner->getWidth(); + r.size.height = (float) owner->getHeight(); + HIViewSetFrame (view, &r); + } + +private: + WebBrowserComponent* const owner; + HIViewRef view; + WebView* webView; + DownloadClickDetector* clickListener; + + void sendWebViewToURL (const char* utf8URL, + const char** headerNames, + const char** headerValues, + int numHeaders, + const char* postData, + int postDataSize) + { + NSMutableURLRequest* r = [NSMutableURLRequest + requestWithURL: [NSURL URLWithString: [NSString stringWithUTF8String: utf8URL]] + cachePolicy: NSURLRequestUseProtocolCachePolicy + timeoutInterval: 30.0]; + + if (postDataSize > 0) + { + [ r setHTTPMethod: @"POST"]; + [ r setHTTPBody: [NSData dataWithBytes: postData length: postDataSize]]; + } + + int i; + for (i = 0; i < numHeaders; ++i) + { + [ r setValue: [NSString stringWithUTF8String: headerValues[i]] + forHTTPHeaderField: [NSString stringWithUTF8String: headerNames[i]]]; + } + + [[webView mainFrame] stopLoading ]; + [[webView mainFrame] loadRequest: r]; + } + + WebBrowserComponentInternal (const WebBrowserComponentInternal&); + const WebBrowserComponentInternal& operator= (const WebBrowserComponentInternal&); +}; + +//============================================================================== +WebBrowserComponent::WebBrowserComponent() + : browser (0), + associatedWindow (0), + blankPageShown (false) +{ + setOpaque (true); +} + +WebBrowserComponent::~WebBrowserComponent() +{ + deleteBrowser(); +} + +//============================================================================== +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + + if (browser != 0) + browser->goToURL (url, headers, postData); +} + +void WebBrowserComponent::stop() +{ + if (browser != 0) + browser->stop(); +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + + if (browser != 0) + browser->goBack(); +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + + if (browser != 0) + browser->goForward(); +} + +//============================================================================== +void WebBrowserComponent::paint (Graphics& g) +{ + if (browser == 0) + g.fillAll (Colours::white); +} + +void WebBrowserComponent::checkWindowAssociation() +{ + void* const window = getWindowHandle(); + + if (window != associatedWindow + || (browser == 0 && window != 0)) + { + associatedWindow = window; + + deleteBrowser(); + createBrowser(); + } + + if (browser != 0) + { + if (associatedWindow != 0 && isShowing()) + { + browser->show(); + + if (blankPageShown) + goBack(); + } + else + { + if (! blankPageShown) + { + // when the component becomes invisible, some stuff like flash + // carries on playing audio, so we need to force it onto a blank + // page to avoid this.. + + blankPageShown = true; + browser->goToURL ("about:blank", 0, 0); + } + + browser->hide(); + } + } +} + +void WebBrowserComponent::createBrowser() +{ + deleteBrowser(); + + if (isShowing()) + { + WebInitForCarbon(); + browser = new WebBrowserComponentInternal (this); + reloadLastURL(); + } +} + +void WebBrowserComponent::deleteBrowser() +{ + deleteAndZero (browser); +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::updateBrowserPosition() +{ + if (getPeer() != 0 && browser != 0) + browser->updateBounds(); +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::moved() +{ + updateBrowserPosition(); +} + +void WebBrowserComponent::resized() +{ + updateBrowserPosition(); +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String& url) +{ + return true; +} + +END_JUCE_NAMESPACE diff --git a/build/win32/platform_specific_code/juce_win32_WebBrowserComponent.cpp b/build/win32/platform_specific_code/juce_win32_WebBrowserComponent.cpp new file mode 100644 index 0000000000..84b9863829 --- /dev/null +++ b/build/win32/platform_specific_code/juce_win32_WebBrowserComponent.cpp @@ -0,0 +1,362 @@ +/* + ============================================================================== + + 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. + + ============================================================================== +*/ + +//============================================================================== +#ifdef _MSC_VER + #pragma warning (disable: 4514) + #pragma warning (push) +#endif + +#include "win32_headers.h" +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning (pop) + #pragma warning (disable: 4312 4244) +#endif + + +#include "../../../src/juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_appframework/gui/components/special/juce_WebBrowserComponent.h" +#include "../../../src/juce_appframework/gui/components/special/juce_ActiveXControlComponent.h" + + +//============================================================================== +class WebBrowserComponentInternal : public ActiveXControlComponent +{ +public: + //============================================================================== + WebBrowserComponentInternal() + : browser (0), + connectionPoint (0), + adviseCookie (0) + { + } + + ~WebBrowserComponentInternal() + { + if (connectionPoint != 0) + connectionPoint->Unadvise (adviseCookie); + + if (browser != 0) + browser->Release(); + } + + void createBrowser() + { + createControl (&CLSID_WebBrowser); + browser = (IWebBrowser2*) queryInterface (&IID_IWebBrowser2); + + IConnectionPointContainer* connectionPointContainer = (IConnectionPointContainer*) queryInterface (&IID_IConnectionPointContainer); + + if (connectionPointContainer != 0) + { + connectionPointContainer->FindConnectionPoint (DIID_DWebBrowserEvents2, + &connectionPoint); + + if (connectionPoint != 0) + { + WebBrowserComponent* const owner = dynamic_cast (getParentComponent()); + jassert (owner != 0); + + EventHandler* handler = new EventHandler (owner); + connectionPoint->Advise (handler, &adviseCookie); + } + } + } + + void goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) + { + if (browser != 0) + { + LPSAFEARRAY sa = 0; + _variant_t flags, frame, postDataVar, headersVar; + + if (headers != 0) + headersVar = (const tchar*) headers->joinIntoString ("\r\n"); + + if (postData != 0 && postData->getSize() > 0) + { + LPSAFEARRAY sa = SafeArrayCreateVector (VT_UI1, 0, postData->getSize()); + + if (sa != 0) + { + void* data = 0; + SafeArrayAccessData (sa, &data); + jassert (data != 0); + + if (data != 0) + { + postData->copyTo (data, 0, postData->getSize()); + SafeArrayUnaccessData (sa); + + VARIANT postDataVar2; + VariantInit (&postDataVar2); + V_VT (&postDataVar2) = VT_ARRAY | VT_UI1; + V_ARRAY (&postDataVar2) = sa; + + postDataVar = postDataVar2; + } + } + } + + browser->Navigate ((BSTR) (const OLECHAR*) url, + &flags, &frame, + &postDataVar, &headersVar); + + if (sa != 0) + SafeArrayDestroy (sa); + } + } + + //============================================================================== + IWebBrowser2* browser; + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + IConnectionPoint* connectionPoint; + DWORD adviseCookie; + + //============================================================================== + class EventHandler : public IDispatch + { + public: + EventHandler (WebBrowserComponent* owner_) + : owner (owner_), + refCount (0) + { + } + + ~EventHandler() + { + } + + //============================================================================== + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown || id == IID_IDispatch || id == DIID_DWebBrowserEvents2) + { + AddRef(); + *result = this; + return S_OK; + } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall GetTypeInfoCount (UINT __RPC_FAR*) { return E_NOTIMPL; } + HRESULT __stdcall GetTypeInfo (UINT, LCID, ITypeInfo __RPC_FAR *__RPC_FAR*) { return E_NOTIMPL; } + HRESULT __stdcall GetIDsOfNames (REFIID, LPOLESTR __RPC_FAR*, UINT, LCID, DISPID __RPC_FAR*) { return E_NOTIMPL; } + + HRESULT __stdcall Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, + WORD /*wFlags*/, DISPPARAMS __RPC_FAR* pDispParams, + VARIANT __RPC_FAR* /*pVarResult*/, EXCEPINFO __RPC_FAR* /*pExcepInfo*/, + UINT __RPC_FAR* /*puArgErr*/) + { + switch (dispIdMember) + { + case DISPID_BEFORENAVIGATE2: + { + VARIANT* const vurl = pDispParams->rgvarg[5].pvarVal; + + String url; + + if ((vurl->vt & VT_BYREF) != 0) + url = *vurl->pbstrVal; + else + url = vurl->bstrVal; + + *pDispParams->rgvarg->pboolVal + = owner->pageAboutToLoad (url) ? VARIANT_FALSE + : VARIANT_TRUE; + + return S_OK; + } + + default: + break; + } + + return E_NOTIMPL; + } + + //============================================================================== + juce_UseDebuggingNewOperator + + private: + WebBrowserComponent* const owner; + int refCount; + + EventHandler (const EventHandler&); + const EventHandler& operator= (const EventHandler&); + }; +}; + + + +//============================================================================== +WebBrowserComponent::WebBrowserComponent() + : browser (0), + blankPageShown (false) +{ + setOpaque (true); + addAndMakeVisible (browser = new WebBrowserComponentInternal()); +} + +WebBrowserComponent::~WebBrowserComponent() +{ + delete browser; +} + +//============================================================================== +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + + browser->goToURL (url, headers, postData); +} + +void WebBrowserComponent::stop() +{ + if (browser->browser != 0) + browser->browser->Stop(); +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + + if (browser->browser != 0) + browser->browser->GoBack(); +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + + if (browser->browser != 0) + browser->browser->GoForward(); +} + +//============================================================================== +void WebBrowserComponent::paint (Graphics& g) +{ + if (browser->browser == 0) + g.fillAll (Colours::white); +} + +void WebBrowserComponent::checkWindowAssociation() +{ + if (isShowing()) + { + if (blankPageShown) + goBack(); + + if (browser->browser == 0 && getPeer() != 0) + { + browser->createBrowser(); + reloadLastURL(); + } + } + else + { + if (browser != 0 && ! blankPageShown) + { + // when the component becomes invisible, some stuff like flash + // carries on playing audio, so we need to force it onto a blank + // page to avoid this.. + + blankPageShown = true; + browser->goToURL ("about:blank", 0, 0); + } + } +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::moved() +{ +} + +void WebBrowserComponent::resized() +{ + browser->setSize (getWidth(), getHeight()); +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String&) +{ + return true; +} + +END_JUCE_NAMESPACE diff --git a/build/win32/vc8/JUCE.vcproj b/build/win32/vc8/JUCE.vcproj index 6e099d65a2..6bc95bf6f7 100644 --- a/build/win32/vc8/JUCE.vcproj +++ b/build/win32/vc8/JUCE.vcproj @@ -4157,6 +4157,10 @@ RelativePath="..\..\..\src\juce_appframework\gui\components\special\juce_SystemTrayIconComponent.h" > + + + + diff --git a/docs/JUCE changelist.txt b/docs/JUCE changelist.txt index 20cc7e8e1c..4ba7eaf981 100644 --- a/docs/JUCE changelist.txt +++ b/docs/JUCE changelist.txt @@ -9,6 +9,7 @@ Changelist for version 1.46 - new class: AudioProcessorGraph: This allows AudioProcessors to be efficiently wired together and run as a graph. I've converted the plugin host demo to now use this instead of its own graph rendering code. - new class: AudioProcessorPlayer: This allows an audio i/o device to stream through an AudioProcessor (or an AudioProcessorGraph). - new class QuickTimeAudioFormat, which uses QuickTime to implement an AudioFormat that can read .mov files and other formats that QT supports (e.g. mp3, aac, etc) +- new class: WebBrowserComponent, for embedding a web browser in your app - AudioProcessor now has a few more pure virtual methods that you'll need to implement: acceptsMidi(), producesMidi() and getName() - moved all the audio plugin hosting classes into the main juce tree - Mac: the project now requires at least XCode V2.5 diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h index 8a79bfb515..00f3cc6e96 100644 --- a/src/juce_app_includes.h +++ b/src/juce_app_includes.h @@ -713,6 +713,9 @@ #ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ #include "juce_appframework/gui/components/special/juce_SystemTrayIconComponent.h" #endif +#ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ + #include "juce_appframework/gui/components/special/juce_WebBrowserComponent.h" +#endif #ifndef __JUCE_LOOKANDFEEL_JUCEHEADER__ #include "juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h" #endif diff --git a/src/juce_appframework/gui/components/special/juce_WebBrowserComponent.h b/src/juce_appframework/gui/components/special/juce_WebBrowserComponent.h new file mode 100644 index 0000000000..782388175d --- /dev/null +++ b/src/juce_appframework/gui/components/special/juce_WebBrowserComponent.h @@ -0,0 +1,135 @@ +/* + ============================================================================== + + 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. + + ============================================================================== +*/ + +#ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ +#define __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ + +#include "../juce_Component.h" +class WebBrowserComponentInternal; + + +//============================================================================== +/** + A component that displays an embedded web browser. + + The browser itself will be platform-dependent. On the Mac, probably Safari, on + Windows, probably IE. + +*/ +class WebBrowserComponent : public Component +{ +public: + //============================================================================== + /** Creates a WebBrowserComponent. + + Once it's created and visible, send the browser to a URL using goToURL(). + */ + WebBrowserComponent(); + + /** Destructor. */ + ~WebBrowserComponent(); + + //============================================================================== + /** Sends the browser to a particular URL. + + @param url the URL to go to. + @param headers an optional set of parameters to put in the HTTP header. If + you supply this, it should be a set of string in the form + "HeaderKey: HeaderValue" + @param postData an optional block of data that will be attached to the HTTP + POST request + */ + void goToURL (const String& url, + const StringArray* headers = 0, + const MemoryBlock* postData = 0); + + /** Stops the current page loading. + */ + void stop(); + + /** Sends the browser back one page. + */ + void goBack(); + + /** Sends the browser forward one page. + */ + void goForward(); + + + //============================================================================== + /** This callback is called when the browser is about to navigate + to a new location. + + You can override this method to perform some action when the user + tries to go to a particular URL. To allow the operation to carry on, + return true, or return false to stop the navigation happening. + */ + virtual bool pageAboutToLoad (const String& newURL); + + //============================================================================== + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void moved(); + /** @internal */ + void resized(); + /** @internal */ + void parentHierarchyChanged(); + /** @internal */ + void visibilityChanged(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + WebBrowserComponentInternal* browser; + bool blankPageShown; + + String lastURL; + StringArray lastHeaders; + MemoryBlock lastPostData; + +#if JUCE_MAC + void* associatedWindow; + void updateBrowserPosition(); + void createBrowser(); + void deleteBrowser(); +#endif + + void reloadLastURL(); + void checkWindowAssociation(); + + WebBrowserComponent (const WebBrowserComponent&); + const WebBrowserComponent& operator= (const WebBrowserComponent&); +}; + + +#endif // __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__