The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
9.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__
  19. #define __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__
  20. //==============================================================================
  21. /**
  22. Creates a floating carbon window that can be used to hold a carbon UI.
  23. This is a handy class that's designed to be inlined where needed, e.g.
  24. in the audio plugin hosting code.
  25. */
  26. class CarbonViewWrapperComponent : public Component,
  27. public ComponentMovementWatcher,
  28. public Timer
  29. {
  30. public:
  31. CarbonViewWrapperComponent()
  32. : ComponentMovementWatcher (this),
  33. wrapperWindow (0),
  34. carbonWindow (0),
  35. embeddedView (0),
  36. recursiveResize (false)
  37. {
  38. }
  39. virtual ~CarbonViewWrapperComponent()
  40. {
  41. jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor!
  42. }
  43. virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0;
  44. virtual void removeView (HIViewRef embeddedView) = 0;
  45. virtual void mouseDown (int, int) {}
  46. virtual void paint() {}
  47. virtual bool getEmbeddedViewSize (int& w, int& h)
  48. {
  49. if (embeddedView == 0)
  50. return false;
  51. HIRect bounds;
  52. HIViewGetBounds (embeddedView, &bounds);
  53. w = jmax (1, roundToInt (bounds.size.width));
  54. h = jmax (1, roundToInt (bounds.size.height));
  55. return true;
  56. }
  57. void createWindow()
  58. {
  59. if (wrapperWindow == 0)
  60. {
  61. Rect r;
  62. r.left = getScreenX();
  63. r.top = getScreenY();
  64. r.right = r.left + getWidth();
  65. r.bottom = r.top + getHeight();
  66. CreateNewWindow (kDocumentWindowClass,
  67. (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute
  68. | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute),
  69. &r, &wrapperWindow);
  70. jassert (wrapperWindow != 0);
  71. if (wrapperWindow == 0)
  72. return;
  73. carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow];
  74. [getOwnerWindow() addChildWindow: carbonWindow
  75. ordered: NSWindowAbove];
  76. embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow));
  77. // Check for the plugin creating its own floating window, and if there is one,
  78. // we need to reparent it to make it visible..
  79. NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0];
  80. if (floatingChildWindow != nil)
  81. [getOwnerWindow() addChildWindow: floatingChildWindow
  82. ordered: NSWindowAbove];
  83. EventTypeSpec windowEventTypes[] =
  84. {
  85. { kEventClassWindow, kEventWindowGetClickActivation },
  86. { kEventClassWindow, kEventWindowHandleDeactivate },
  87. { kEventClassWindow, kEventWindowBoundsChanging },
  88. { kEventClassMouse, kEventMouseDown },
  89. { kEventClassMouse, kEventMouseMoved },
  90. { kEventClassMouse, kEventMouseDragged },
  91. { kEventClassMouse, kEventMouseUp},
  92. { kEventClassWindow, kEventWindowDrawContent },
  93. { kEventClassWindow, kEventWindowShown },
  94. { kEventClassWindow, kEventWindowHidden }
  95. };
  96. EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback);
  97. InstallWindowEventHandler (wrapperWindow, upp,
  98. sizeof (windowEventTypes) / sizeof (EventTypeSpec),
  99. windowEventTypes, this, &eventHandlerRef);
  100. setOurSizeToEmbeddedViewSize();
  101. setEmbeddedWindowToOurSize();
  102. creationTime = Time::getCurrentTime();
  103. }
  104. }
  105. void deleteWindow()
  106. {
  107. removeView (embeddedView);
  108. embeddedView = 0;
  109. if (wrapperWindow != 0)
  110. {
  111. NSWindow* ownerWindow = getOwnerWindow();
  112. if ([[ownerWindow childWindows] count] > 0)
  113. {
  114. [ownerWindow removeChildWindow: carbonWindow];
  115. [carbonWindow close];
  116. }
  117. RemoveEventHandler (eventHandlerRef);
  118. DisposeWindow (wrapperWindow);
  119. wrapperWindow = 0;
  120. }
  121. }
  122. //==============================================================================
  123. void setOurSizeToEmbeddedViewSize()
  124. {
  125. int w, h;
  126. if (getEmbeddedViewSize (w, h))
  127. {
  128. if (w != getWidth() || h != getHeight())
  129. {
  130. startTimer (50);
  131. setSize (w, h);
  132. if (getParentComponent() != nullptr)
  133. getParentComponent()->setSize (w, h);
  134. }
  135. else
  136. {
  137. startTimer (jlimit (50, 500, getTimerInterval() + 20));
  138. }
  139. }
  140. else
  141. {
  142. stopTimer();
  143. }
  144. }
  145. void setEmbeddedWindowToOurSize()
  146. {
  147. if (! recursiveResize)
  148. {
  149. recursiveResize = true;
  150. if (embeddedView != 0)
  151. {
  152. HIRect r;
  153. r.origin.x = 0;
  154. r.origin.y = 0;
  155. r.size.width = (float) getWidth();
  156. r.size.height = (float) getHeight();
  157. HIViewSetFrame (embeddedView, &r);
  158. }
  159. if (wrapperWindow != 0)
  160. {
  161. Rect wr;
  162. wr.left = getScreenX();
  163. wr.top = getScreenY();
  164. wr.right = wr.left + getWidth();
  165. wr.bottom = wr.top + getHeight();
  166. SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
  167. ShowWindow (wrapperWindow);
  168. }
  169. recursiveResize = false;
  170. }
  171. }
  172. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  173. {
  174. setEmbeddedWindowToOurSize();
  175. }
  176. void componentPeerChanged()
  177. {
  178. deleteWindow();
  179. createWindow();
  180. }
  181. void componentVisibilityChanged()
  182. {
  183. if (isShowing())
  184. createWindow();
  185. else
  186. deleteWindow();
  187. setEmbeddedWindowToOurSize();
  188. }
  189. static void recursiveHIViewRepaint (HIViewRef view)
  190. {
  191. HIViewSetNeedsDisplay (view, true);
  192. HIViewRef child = HIViewGetFirstSubview (view);
  193. while (child != 0)
  194. {
  195. recursiveHIViewRepaint (child);
  196. child = HIViewGetNextView (child);
  197. }
  198. }
  199. void timerCallback()
  200. {
  201. setOurSizeToEmbeddedViewSize();
  202. // To avoid strange overpainting problems when the UI is first opened, we'll
  203. // repaint it a few times during the first second that it's on-screen..
  204. if ((Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
  205. recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
  206. }
  207. OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
  208. {
  209. switch (GetEventKind (event))
  210. {
  211. case kEventWindowHandleDeactivate:
  212. ActivateWindow (wrapperWindow, TRUE);
  213. return noErr;
  214. case kEventWindowGetClickActivation:
  215. {
  216. getTopLevelComponent()->toFront (false);
  217. [carbonWindow makeKeyAndOrderFront: nil];
  218. ClickActivationResult howToHandleClick = kActivateAndHandleClick;
  219. SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
  220. sizeof (ClickActivationResult), &howToHandleClick);
  221. HIViewSetNeedsDisplay (embeddedView, true);
  222. return noErr;
  223. }
  224. }
  225. return eventNotHandledErr;
  226. }
  227. static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
  228. {
  229. return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
  230. }
  231. protected:
  232. WindowRef wrapperWindow;
  233. NSWindow* carbonWindow;
  234. HIViewRef embeddedView;
  235. bool recursiveResize;
  236. Time creationTime;
  237. EventHandlerRef eventHandlerRef;
  238. NSWindow* getOwnerWindow() const { return [((NSView*) getWindowHandle()) window]; }
  239. };
  240. #endif // __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__