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.

315 lines
10KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #ifndef __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__
  18. #define __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__
  19. //==============================================================================
  20. /**
  21. Creates a floating carbon window that can be used to hold a carbon UI.
  22. This is a handy class that's designed to be inlined where needed, e.g.
  23. in the audio plugin hosting code.
  24. */
  25. class CarbonViewWrapperComponent : public Component,
  26. public ComponentMovementWatcher,
  27. public Timer
  28. {
  29. public:
  30. CarbonViewWrapperComponent()
  31. : ComponentMovementWatcher (this),
  32. keepPluginWindowWhenHidden (false),
  33. wrapperWindow (0),
  34. carbonWindow (0),
  35. embeddedView (0),
  36. recursiveResize (false)
  37. {
  38. }
  39. ~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 handleMouseDown (int, int) {}
  46. virtual void handlePaint() {}
  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 = (short) getScreenX();
  63. r.top = (short) getScreenY();
  64. r.right = (short) (r.left + getWidth());
  65. r.bottom = (short) (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. if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0])
  80. [getOwnerWindow() addChildWindow: floatingChildWindow
  81. ordered: NSWindowAbove];
  82. EventTypeSpec windowEventTypes[] =
  83. {
  84. { kEventClassWindow, kEventWindowGetClickActivation },
  85. { kEventClassWindow, kEventWindowHandleDeactivate },
  86. { kEventClassWindow, kEventWindowBoundsChanging },
  87. { kEventClassMouse, kEventMouseDown },
  88. { kEventClassMouse, kEventMouseMoved },
  89. { kEventClassMouse, kEventMouseDragged },
  90. { kEventClassMouse, kEventMouseUp },
  91. { kEventClassWindow, kEventWindowDrawContent },
  92. { kEventClassWindow, kEventWindowShown },
  93. { kEventClassWindow, kEventWindowHidden }
  94. };
  95. EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback);
  96. InstallWindowEventHandler (wrapperWindow, upp,
  97. sizeof (windowEventTypes) / sizeof (EventTypeSpec),
  98. windowEventTypes, this, &eventHandlerRef);
  99. setOurSizeToEmbeddedViewSize();
  100. setEmbeddedWindowToOurSize();
  101. creationTime = Time::getCurrentTime();
  102. }
  103. }
  104. void deleteWindow()
  105. {
  106. removeView (embeddedView);
  107. embeddedView = 0;
  108. if (wrapperWindow != 0)
  109. {
  110. NSWindow* ownerWindow = getOwnerWindow();
  111. if ([[ownerWindow childWindows] count] > 0)
  112. {
  113. [ownerWindow removeChildWindow: carbonWindow];
  114. [carbonWindow close];
  115. }
  116. RemoveEventHandler (eventHandlerRef);
  117. DisposeWindow (wrapperWindow);
  118. wrapperWindow = 0;
  119. }
  120. }
  121. //==============================================================================
  122. void setOurSizeToEmbeddedViewSize()
  123. {
  124. int w, h;
  125. if (getEmbeddedViewSize (w, h))
  126. {
  127. if (w != getWidth() || h != getHeight())
  128. {
  129. startTimer (50);
  130. setSize (w, h);
  131. if (Component* p = getParentComponent())
  132. p->setSize (w, h);
  133. }
  134. else
  135. {
  136. startTimer (jlimit (50, 500, getTimerInterval() + 20));
  137. }
  138. }
  139. else
  140. {
  141. stopTimer();
  142. }
  143. }
  144. void setEmbeddedWindowToOurSize()
  145. {
  146. if (! recursiveResize)
  147. {
  148. recursiveResize = true;
  149. if (embeddedView != 0)
  150. {
  151. HIRect r;
  152. r.origin.x = 0;
  153. r.origin.y = 0;
  154. r.size.width = (float) getWidth();
  155. r.size.height = (float) getHeight();
  156. HIViewSetFrame (embeddedView, &r);
  157. }
  158. if (wrapperWindow != 0)
  159. {
  160. Rect wr;
  161. wr.left = (short) getScreenX();
  162. wr.top = (short) getScreenY();
  163. wr.right = (short) (wr.left + getWidth());
  164. wr.bottom = (short) (wr.top + getHeight());
  165. SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
  166. // This group stuff is mainly a workaround for Mackie plugins like FinalMix..
  167. WindowGroupRef group = GetWindowGroup (wrapperWindow);
  168. WindowRef attachedWindow;
  169. if (GetIndexedWindow (group, 2, kWindowGroupContentsReturnWindows, &attachedWindow) == noErr)
  170. {
  171. SelectWindow (attachedWindow);
  172. ActivateWindow (attachedWindow, TRUE);
  173. HideWindow (wrapperWindow);
  174. }
  175. ShowWindow (wrapperWindow);
  176. }
  177. recursiveResize = false;
  178. }
  179. }
  180. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  181. {
  182. setEmbeddedWindowToOurSize();
  183. }
  184. void componentPeerChanged()
  185. {
  186. deleteWindow();
  187. createWindow();
  188. }
  189. void componentVisibilityChanged()
  190. {
  191. if (isShowing())
  192. createWindow();
  193. else if (! keepPluginWindowWhenHidden)
  194. deleteWindow();
  195. setEmbeddedWindowToOurSize();
  196. }
  197. static void recursiveHIViewRepaint (HIViewRef view)
  198. {
  199. HIViewSetNeedsDisplay (view, true);
  200. HIViewRef child = HIViewGetFirstSubview (view);
  201. while (child != 0)
  202. {
  203. recursiveHIViewRepaint (child);
  204. child = HIViewGetNextView (child);
  205. }
  206. }
  207. void timerCallback()
  208. {
  209. if (isShowing())
  210. {
  211. setOurSizeToEmbeddedViewSize();
  212. // To avoid strange overpainting problems when the UI is first opened, we'll
  213. // repaint it a few times during the first second that it's on-screen..
  214. if ((Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
  215. recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
  216. }
  217. }
  218. OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
  219. {
  220. switch (GetEventKind (event))
  221. {
  222. case kEventWindowHandleDeactivate:
  223. ActivateWindow (wrapperWindow, TRUE);
  224. return noErr;
  225. case kEventWindowGetClickActivation:
  226. {
  227. getTopLevelComponent()->toFront (false);
  228. [carbonWindow makeKeyAndOrderFront: nil];
  229. ClickActivationResult howToHandleClick = kActivateAndHandleClick;
  230. SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
  231. sizeof (ClickActivationResult), &howToHandleClick);
  232. if (embeddedView != 0)
  233. HIViewSetNeedsDisplay (embeddedView, true);
  234. return noErr;
  235. }
  236. }
  237. return eventNotHandledErr;
  238. }
  239. static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
  240. {
  241. return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
  242. }
  243. bool keepPluginWindowWhenHidden;
  244. protected:
  245. WindowRef wrapperWindow;
  246. NSWindow* carbonWindow;
  247. HIViewRef embeddedView;
  248. bool recursiveResize;
  249. Time creationTime;
  250. EventHandlerRef eventHandlerRef;
  251. NSWindow* getOwnerWindow() const { return [((NSView*) getWindowHandle()) window]; }
  252. };
  253. #endif // __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__