/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-7 by Raw Material Software ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License, as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with JUCE; if not, visit www.gnu.org/licenses or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ If you'd like to release a closed-source product which uses JUCE, commercial licenses are also available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ // (This file gets included by juce_win32_NativeCode.cpp, rather than being // compiled on its own). #if JUCE_INCLUDED_FILE //============================================================================== class JuceIStorage : public IStorage { int refCount; public: JuceIStorage() : refCount (1) {} virtual ~JuceIStorage() { jassert (refCount == 0); } HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) { if (id == IID_IUnknown || id == IID_IStorage) { AddRef(); *result = this; return S_OK; } *result = 0; return E_NOINTERFACE; } ULONG __stdcall AddRef() { return ++refCount; } ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } HRESULT __stdcall CreateStream (const WCHAR*, DWORD, DWORD, DWORD, IStream**) { return E_NOTIMPL; } HRESULT __stdcall OpenStream (const WCHAR*, void*, DWORD, DWORD, IStream**) { return E_NOTIMPL; } HRESULT __stdcall CreateStorage (const WCHAR*, DWORD, DWORD, DWORD, IStorage**) { return E_NOTIMPL; } HRESULT __stdcall OpenStorage (const WCHAR*, IStorage*, DWORD, SNB, DWORD, IStorage**) { return E_NOTIMPL; } HRESULT __stdcall CopyTo (DWORD, IID const*, SNB, IStorage*) { return E_NOTIMPL; } HRESULT __stdcall MoveElementTo (const OLECHAR*,IStorage*, const OLECHAR*, DWORD) { return E_NOTIMPL; } HRESULT __stdcall Commit (DWORD) { return E_NOTIMPL; } HRESULT __stdcall Revert() { return E_NOTIMPL; } HRESULT __stdcall EnumElements (DWORD, void*, DWORD, IEnumSTATSTG**) { return E_NOTIMPL; } HRESULT __stdcall DestroyElement (const OLECHAR*) { return E_NOTIMPL; } HRESULT __stdcall RenameElement (const WCHAR*, const WCHAR*) { return E_NOTIMPL; } HRESULT __stdcall SetElementTimes (const WCHAR*, FILETIME const*, FILETIME const*, FILETIME const*) { return E_NOTIMPL; } HRESULT __stdcall SetClass (REFCLSID) { return S_OK; } HRESULT __stdcall SetStateBits (DWORD, DWORD) { return E_NOTIMPL; } HRESULT __stdcall Stat (STATSTG*, DWORD) { return E_NOTIMPL; } juce_UseDebuggingNewOperator }; class JuceOleInPlaceFrame : public IOleInPlaceFrame { int refCount; HWND window; public: JuceOleInPlaceFrame (HWND window_) : refCount (1), window (window_) { } virtual ~JuceOleInPlaceFrame() { jassert (refCount == 0); } HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) { if (id == IID_IUnknown || id == IID_IOleInPlaceFrame) { AddRef(); *result = this; return S_OK; } *result = 0; return E_NOINTERFACE; } ULONG __stdcall AddRef() { return ++refCount; } ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } HRESULT __stdcall GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; } HRESULT __stdcall ContextSensitiveHelp (BOOL) { return E_NOTIMPL; } HRESULT __stdcall GetBorder (LPRECT) { return E_NOTIMPL; } HRESULT __stdcall RequestBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; } HRESULT __stdcall SetBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; } HRESULT __stdcall SetActiveObject (IOleInPlaceActiveObject*, LPCOLESTR) { return S_OK; } HRESULT __stdcall InsertMenus (HMENU, LPOLEMENUGROUPWIDTHS) { return E_NOTIMPL; } HRESULT __stdcall SetMenu (HMENU, HOLEMENU, HWND) { return S_OK; } HRESULT __stdcall RemoveMenus (HMENU) { return E_NOTIMPL; } HRESULT __stdcall SetStatusText (LPCOLESTR) { return S_OK; } HRESULT __stdcall EnableModeless (BOOL) { return S_OK; } HRESULT __stdcall TranslateAccelerator(LPMSG, WORD) { return E_NOTIMPL; } juce_UseDebuggingNewOperator }; class JuceIOleInPlaceSite : public IOleInPlaceSite { int refCount; HWND window; JuceOleInPlaceFrame* frame; public: JuceIOleInPlaceSite (HWND window_) : refCount (1), window (window_) { frame = new JuceOleInPlaceFrame (window); } virtual ~JuceIOleInPlaceSite() { jassert (refCount == 0); frame->Release(); } HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) { if (id == IID_IUnknown || id == IID_IOleInPlaceSite) { AddRef(); *result = this; return S_OK; } *result = 0; return E_NOINTERFACE; } ULONG __stdcall AddRef() { return ++refCount; } ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } HRESULT __stdcall GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; } HRESULT __stdcall ContextSensitiveHelp (BOOL) { return E_NOTIMPL; } HRESULT __stdcall CanInPlaceActivate() { return S_OK; } HRESULT __stdcall OnInPlaceActivate() { return S_OK; } HRESULT __stdcall OnUIActivate() { return S_OK; } HRESULT __stdcall GetWindowContext (LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, LPRECT, LPRECT, LPOLEINPLACEFRAMEINFO lpFrameInfo) { frame->AddRef(); *lplpFrame = frame; *lplpDoc = 0; lpFrameInfo->fMDIApp = FALSE; lpFrameInfo->hwndFrame = window; lpFrameInfo->haccel = 0; lpFrameInfo->cAccelEntries = 0; return S_OK; } HRESULT __stdcall Scroll (SIZE) { return E_NOTIMPL; } HRESULT __stdcall OnUIDeactivate (BOOL) { return S_OK; } HRESULT __stdcall OnInPlaceDeactivate() { return S_OK; } HRESULT __stdcall DiscardUndoState() { return E_NOTIMPL; } HRESULT __stdcall DeactivateAndUndo() { return E_NOTIMPL; } HRESULT __stdcall OnPosRectChange (LPCRECT) { return S_OK; } juce_UseDebuggingNewOperator }; class JuceIOleClientSite : public IOleClientSite { int refCount; JuceIOleInPlaceSite* inplaceSite; public: JuceIOleClientSite (HWND window) : refCount (1) { inplaceSite = new JuceIOleInPlaceSite (window); } virtual ~JuceIOleClientSite() { jassert (refCount == 0); inplaceSite->Release(); } HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) { if (id == IID_IUnknown || id == IID_IOleClientSite) { AddRef(); *result = this; return S_OK; } else if (id == IID_IOleInPlaceSite) { inplaceSite->AddRef(); *result = inplaceSite; return S_OK; } *result = 0; return E_NOINTERFACE; } ULONG __stdcall AddRef() { return ++refCount; } ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } HRESULT __stdcall SaveObject() { return E_NOTIMPL; } HRESULT __stdcall GetMoniker (DWORD, DWORD, IMoniker**) { return E_NOTIMPL; } HRESULT __stdcall GetContainer (LPOLECONTAINER* ppContainer) { *ppContainer = 0; return E_NOINTERFACE; } HRESULT __stdcall ShowObject() { return S_OK; } HRESULT __stdcall OnShowWindow (BOOL) { return E_NOTIMPL; } HRESULT __stdcall RequestNewObjectLayout() { return E_NOTIMPL; } juce_UseDebuggingNewOperator }; //============================================================================== class ActiveXControlData : public ComponentMovementWatcher { ActiveXControlComponent* const owner; bool wasShowing; public: HWND controlHWND; IStorage* storage; IOleClientSite* clientSite; IOleObject* control; //============================================================================== ActiveXControlData (HWND hwnd, ActiveXControlComponent* const owner_) : ComponentMovementWatcher (owner_), owner (owner_), wasShowing (owner_ != 0 && owner_->isShowing()), controlHWND (0), storage (new JuceIStorage()), clientSite (new JuceIOleClientSite (hwnd)), control (0) { } ~ActiveXControlData() { if (control != 0) { control->Close (OLECLOSE_NOSAVE); control->Release(); } clientSite->Release(); storage->Release(); } //============================================================================== void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) { Component* const topComp = owner->getTopLevelComponent(); if (topComp->getPeer() != 0) { int x = 0, y = 0; owner->relativePositionToOtherComponent (topComp, x, y); owner->setControlBounds (Rectangle (x, y, owner->getWidth(), owner->getHeight())); } } void componentPeerChanged() { const bool isShowingNow = owner->isShowing(); if (wasShowing != isShowingNow) { wasShowing = isShowingNow; owner->setControlVisible (isShowingNow); } } void componentVisibilityChanged (Component&) { componentPeerChanged(); } static bool doesWindowMatch (const ActiveXControlComponent* const ax, HWND hwnd) { return ((ActiveXControlData*) ax->control) != 0 && ((ActiveXControlData*) ax->control)->controlHWND == hwnd; } }; //============================================================================== static VoidArray activeXComps; static HWND getHWND (const ActiveXControlComponent* const component) { HWND hwnd = 0; const IID iid = IID_IOleWindow; IOleWindow* const window = (IOleWindow*) component->queryInterface (&iid); if (window != 0) { window->GetWindow (&hwnd); window->Release(); } return hwnd; } static void offerActiveXMouseEventToPeer (ComponentPeer* const peer, HWND hwnd, UINT message, LPARAM lParam) { RECT activeXRect, peerRect; GetWindowRect (hwnd, &activeXRect); GetWindowRect ((HWND) peer->getNativeHandle(), &peerRect); const int mx = GET_X_LPARAM (lParam) + activeXRect.left - peerRect.left; const int my = GET_Y_LPARAM (lParam) + activeXRect.top - peerRect.top; const int64 mouseEventTime = getMouseEventTime(); const int oldModifiers = currentModifiers; ModifierKeys::getCurrentModifiersRealtime(); // to update the mouse button flags switch (message) { case WM_MOUSEMOVE: if (ModifierKeys (currentModifiers).isAnyMouseButtonDown()) peer->handleMouseDrag (mx, my, mouseEventTime); else peer->handleMouseMove (mx, my, mouseEventTime); break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: peer->handleMouseDown (mx, my, mouseEventTime); break; case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: peer->handleMouseUp (oldModifiers, mx, my, mouseEventTime); break; default: break; } } // intercepts events going to an activeX control, so we can sneakily use the mouse events static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { for (int i = activeXComps.size(); --i >= 0;) { const ActiveXControlComponent* const ax = (const ActiveXControlComponent*) activeXComps.getUnchecked(i); if (ActiveXControlData::doesWindowMatch (ax, hwnd)) { switch (message) { case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: if (ax->isShowing()) { ComponentPeer* const peer = ax->getPeer(); if (peer != 0) { offerActiveXMouseEventToPeer (peer, hwnd, message, lParam); if (! ax->areMouseEventsAllowed()) return 0; } } break; default: break; } return CallWindowProc ((WNDPROC) (ax->originalWndProc), hwnd, message, wParam, lParam); } } return DefWindowProc (hwnd, message, wParam, lParam); } ActiveXControlComponent::ActiveXControlComponent() : originalWndProc (0), control (0), mouseEventsAllowed (true) { activeXComps.add (this); } ActiveXControlComponent::~ActiveXControlComponent() { deleteControl(); activeXComps.removeValue (this); } void ActiveXControlComponent::paint (Graphics& g) { if (control == 0) g.fillAll (Colours::lightgrey); } bool ActiveXControlComponent::createControl (const void* controlIID) { deleteControl(); ComponentPeer* const peer = getPeer(); // the component must have already been added to a real window when you call this! jassert (dynamic_cast (peer) != 0); if (dynamic_cast (peer) != 0) { int x = 0, y = 0; relativePositionToOtherComponent (getTopLevelComponent(), x, y); HWND hwnd = (HWND) peer->getNativeHandle(); ActiveXControlData* const info = new ActiveXControlData (hwnd, this); HRESULT hr; if ((hr = OleCreate (*(const IID*) controlIID, IID_IOleObject, 1 /*OLERENDER_DRAW*/, 0, info->clientSite, info->storage, (void**) &(info->control))) == S_OK) { info->control->SetHostNames (L"Juce", 0); if (OleSetContainedObject (info->control, TRUE) == S_OK) { RECT rect; rect.left = x; rect.top = y; rect.right = x + getWidth(); rect.bottom = y + getHeight(); if (info->control->DoVerb (OLEIVERB_SHOW, 0, info->clientSite, 0, hwnd, &rect) == S_OK) { control = info; setControlBounds (Rectangle (x, y, getWidth(), getHeight())); info->controlHWND = getHWND (this); if (info->controlHWND != 0) { originalWndProc = (void*) (pointer_sized_int) GetWindowLongPtr ((HWND) info->controlHWND, GWLP_WNDPROC); SetWindowLongPtr ((HWND) info->controlHWND, GWLP_WNDPROC, (LONG_PTR) activeXHookWndProc); } return true; } } } delete info; } return false; } void ActiveXControlComponent::deleteControl() { ActiveXControlData* const info = (ActiveXControlData*) control; if (info != 0) { delete info; control = 0; originalWndProc = 0; } } void* ActiveXControlComponent::queryInterface (const void* iid) const { ActiveXControlData* const info = (ActiveXControlData*) control; void* result = 0; if (info != 0 && info->control != 0 && info->control->QueryInterface (*(const IID*) iid, &result) == S_OK) return result; return 0; } void ActiveXControlComponent::setControlBounds (const Rectangle& newBounds) const { HWND hwnd = ((ActiveXControlData*) control)->controlHWND; if (hwnd != 0) MoveWindow (hwnd, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE); } void ActiveXControlComponent::setControlVisible (const bool shouldBeVisible) const { HWND hwnd = ((ActiveXControlData*) control)->controlHWND; if (hwnd != 0) ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE); } void ActiveXControlComponent::setMouseEventsAllowed (const bool eventsCanReachControl) { mouseEventsAllowed = eventsCanReachControl; } #endif