| @@ -0,0 +1,244 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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. | |||
| ============================================================================== | |||
| */ | |||
| /** Creates a floating carbon window that can be used to hold a carbon UI. | |||
| This is a handy class that's designed to be inlined where needed, e.g. | |||
| in the audio plugin hosting code. | |||
| */ | |||
| class CarbonViewWrapperComponent : public Component, | |||
| public ComponentMovementWatcher, | |||
| public Timer | |||
| { | |||
| public: | |||
| CarbonViewWrapperComponent() | |||
| : ComponentMovementWatcher (this), | |||
| wrapperWindow (0), | |||
| embeddedView (0), | |||
| recursiveResize (false) | |||
| { | |||
| } | |||
| virtual ~CarbonViewWrapperComponent() | |||
| { | |||
| jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor! | |||
| } | |||
| virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0; | |||
| virtual void removeView (HIViewRef embeddedView) = 0; | |||
| virtual void mouseDown (int x, int y) {} | |||
| virtual void paint() {} | |||
| virtual bool getEmbeddedViewSize (int& w, int& h) | |||
| { | |||
| if (embeddedView == 0) | |||
| return false; | |||
| HIRect bounds; | |||
| HIViewGetBounds (embeddedView, &bounds); | |||
| w = jmax (1, roundFloatToInt (bounds.size.width)); | |||
| h = jmax (1, roundFloatToInt (bounds.size.height)); | |||
| return true; | |||
| } | |||
| void createWindow() | |||
| { | |||
| if (wrapperWindow == 0) | |||
| { | |||
| Rect r; | |||
| r.left = getScreenX(); | |||
| r.top = getScreenY(); | |||
| r.right = r.left + getWidth(); | |||
| r.bottom = r.top + getHeight(); | |||
| CreateNewWindow (kDocumentWindowClass, | |||
| (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute | |||
| | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute), | |||
| &r, &wrapperWindow); | |||
| jassert (wrapperWindow != 0); | |||
| if (wrapperWindow == 0) | |||
| return; | |||
| NSWindow* carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow]; | |||
| NSWindow* ownerWindow = [((NSView*) getWindowHandle()) window]; | |||
| [ownerWindow addChildWindow: carbonWindow | |||
| ordered: NSWindowAbove]; | |||
| embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow)); | |||
| EventTypeSpec windowEventTypes[] = { { kEventClassWindow, kEventWindowGetClickActivation }, | |||
| { kEventClassWindow, kEventWindowHandleDeactivate } }; | |||
| EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback); | |||
| InstallWindowEventHandler (wrapperWindow, upp, | |||
| sizeof (windowEventTypes) / sizeof (EventTypeSpec), | |||
| windowEventTypes, this, &eventHandlerRef); | |||
| setOurSizeToEmbeddedViewSize(); | |||
| setEmbeddedWindowToOurSize(); | |||
| } | |||
| } | |||
| void deleteWindow() | |||
| { | |||
| removeView (embeddedView); | |||
| embeddedView = 0; | |||
| if (wrapperWindow != 0) | |||
| { | |||
| RemoveEventHandler (eventHandlerRef); | |||
| DisposeWindow (wrapperWindow); | |||
| wrapperWindow = 0; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void setOurSizeToEmbeddedViewSize() | |||
| { | |||
| int w, h; | |||
| if (getEmbeddedViewSize (w, h)) | |||
| { | |||
| if (w != getWidth() || h != getHeight()) | |||
| { | |||
| startTimer (50); | |||
| setSize (w, h); | |||
| if (getParentComponent() != 0) | |||
| getParentComponent()->setSize (w, h); | |||
| } | |||
| else | |||
| { | |||
| startTimer (jlimit (50, 500, getTimerInterval() + 20)); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| stopTimer(); | |||
| } | |||
| } | |||
| void setEmbeddedWindowToOurSize() | |||
| { | |||
| if (! recursiveResize) | |||
| { | |||
| recursiveResize = true; | |||
| if (embeddedView != 0) | |||
| { | |||
| HIRect r; | |||
| r.origin.x = 0; | |||
| r.origin.y = 0; | |||
| r.size.width = (float) getWidth(); | |||
| r.size.height = (float) getHeight(); | |||
| HIViewSetFrame (embeddedView, &r); | |||
| } | |||
| if (wrapperWindow != 0) | |||
| { | |||
| Rect wr; | |||
| wr.left = getScreenX(); | |||
| wr.top = getScreenY(); | |||
| wr.right = wr.left + getWidth(); | |||
| wr.bottom = wr.top + getHeight(); | |||
| SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr); | |||
| ShowWindow (wrapperWindow); | |||
| } | |||
| recursiveResize = false; | |||
| } | |||
| } | |||
| void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) | |||
| { | |||
| setEmbeddedWindowToOurSize(); | |||
| } | |||
| void componentPeerChanged() | |||
| { | |||
| deleteWindow(); | |||
| createWindow(); | |||
| } | |||
| void componentVisibilityChanged (Component&) | |||
| { | |||
| if (isShowing()) | |||
| createWindow(); | |||
| else | |||
| deleteWindow(); | |||
| setEmbeddedWindowToOurSize(); | |||
| } | |||
| void timerCallback() | |||
| { | |||
| setOurSizeToEmbeddedViewSize(); | |||
| } | |||
| OSStatus carbonEventHandler (EventHandlerCallRef nextHandlerRef, | |||
| EventRef event) | |||
| { | |||
| switch (GetEventKind (event)) | |||
| { | |||
| case kEventWindowHandleDeactivate: | |||
| ActivateWindow (wrapperWindow, TRUE); | |||
| break; | |||
| case kEventWindowGetClickActivation: | |||
| { | |||
| getTopLevelComponent()->toFront (false); | |||
| ClickActivationResult howToHandleClick = kActivateAndHandleClick; | |||
| SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult, | |||
| sizeof (ClickActivationResult), &howToHandleClick); | |||
| } | |||
| break; | |||
| } | |||
| return noErr; | |||
| } | |||
| static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, | |||
| EventRef event, void* userData) | |||
| { | |||
| return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event); | |||
| } | |||
| protected: | |||
| WindowRef wrapperWindow; | |||
| HIViewRef embeddedView; | |||
| bool recursiveResize; | |||
| EventHandlerRef eventHandlerRef; | |||
| }; | |||