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.

330 lines
11KB

  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_H_INCLUDED
  18. #define JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_H_INCLUDED
  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. repaintChildOnCreation (true)
  38. {
  39. }
  40. ~CarbonViewWrapperComponent()
  41. {
  42. jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor!
  43. }
  44. virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0;
  45. virtual void removeView (HIViewRef embeddedView) = 0;
  46. virtual void handleMouseDown (int, int) {}
  47. virtual void handlePaint() {}
  48. virtual bool getEmbeddedViewSize (int& w, int& h)
  49. {
  50. if (embeddedView == 0)
  51. return false;
  52. HIRect bounds;
  53. HIViewGetBounds (embeddedView, &bounds);
  54. w = jmax (1, roundToInt (bounds.size.width));
  55. h = jmax (1, roundToInt (bounds.size.height));
  56. return true;
  57. }
  58. void createWindow()
  59. {
  60. if (wrapperWindow == 0)
  61. {
  62. Rect r;
  63. r.left = (short) getScreenX();
  64. r.top = (short) getScreenY();
  65. r.right = (short) (r.left + getWidth());
  66. r.bottom = (short) (r.top + getHeight());
  67. CreateNewWindow (kDocumentWindowClass,
  68. (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute
  69. | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute),
  70. &r, &wrapperWindow);
  71. jassert (wrapperWindow != 0);
  72. if (wrapperWindow == 0)
  73. return;
  74. carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow];
  75. [getOwnerWindow() addChildWindow: carbonWindow
  76. ordered: NSWindowAbove];
  77. embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow));
  78. // Check for the plugin creating its own floating window, and if there is one,
  79. // we need to reparent it to make it visible..
  80. if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0])
  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 (Component* p = getParentComponent())
  133. p->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 = (short) getScreenX();
  163. wr.top = (short) getScreenY();
  164. wr.right = (short) (wr.left + getWidth());
  165. wr.bottom = (short) (wr.top + getHeight());
  166. SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
  167. // This group stuff is mainly a workaround for Mackie plugins like FinalMix..
  168. WindowGroupRef group = GetWindowGroup (wrapperWindow);
  169. WindowRef attachedWindow;
  170. if (GetIndexedWindow (group, 2, kWindowGroupContentsReturnWindows, &attachedWindow) == noErr)
  171. {
  172. SelectWindow (attachedWindow);
  173. ActivateWindow (attachedWindow, TRUE);
  174. HideWindow (wrapperWindow);
  175. }
  176. ShowWindow (wrapperWindow);
  177. }
  178. recursiveResize = false;
  179. }
  180. }
  181. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  182. {
  183. setEmbeddedWindowToOurSize();
  184. }
  185. // (overridden to intercept movements of the top-level window)
  186. void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) override
  187. {
  188. ComponentMovementWatcher::componentMovedOrResized (component, wasMoved, wasResized);
  189. if (&component == getTopLevelComponent())
  190. setEmbeddedWindowToOurSize();
  191. }
  192. void componentPeerChanged() override
  193. {
  194. deleteWindow();
  195. createWindow();
  196. }
  197. void componentVisibilityChanged() override
  198. {
  199. if (isShowing())
  200. createWindow();
  201. else if (! keepPluginWindowWhenHidden)
  202. deleteWindow();
  203. setEmbeddedWindowToOurSize();
  204. }
  205. static void recursiveHIViewRepaint (HIViewRef view)
  206. {
  207. HIViewSetNeedsDisplay (view, true);
  208. HIViewRef child = HIViewGetFirstSubview (view);
  209. while (child != 0)
  210. {
  211. recursiveHIViewRepaint (child);
  212. child = HIViewGetNextView (child);
  213. }
  214. }
  215. void timerCallback() override
  216. {
  217. if (isShowing())
  218. {
  219. setOurSizeToEmbeddedViewSize();
  220. // To avoid strange overpainting problems when the UI is first opened, we'll
  221. // repaint it a few times during the first second that it's on-screen..
  222. if (repaintChildOnCreation && (Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
  223. recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
  224. }
  225. }
  226. void setRepaintsChildHIViewWhenCreated (bool b) noexcept
  227. {
  228. repaintChildOnCreation = b;
  229. }
  230. OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
  231. {
  232. switch (GetEventKind (event))
  233. {
  234. case kEventWindowHandleDeactivate:
  235. ActivateWindow (wrapperWindow, TRUE);
  236. return noErr;
  237. case kEventWindowGetClickActivation:
  238. {
  239. getTopLevelComponent()->toFront (false);
  240. [carbonWindow makeKeyAndOrderFront: nil];
  241. ClickActivationResult howToHandleClick = kActivateAndHandleClick;
  242. SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
  243. sizeof (ClickActivationResult), &howToHandleClick);
  244. if (embeddedView != 0)
  245. HIViewSetNeedsDisplay (embeddedView, true);
  246. return noErr;
  247. }
  248. }
  249. return eventNotHandledErr;
  250. }
  251. static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
  252. {
  253. return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
  254. }
  255. bool keepPluginWindowWhenHidden;
  256. protected:
  257. WindowRef wrapperWindow;
  258. NSWindow* carbonWindow;
  259. HIViewRef embeddedView;
  260. bool recursiveResize, repaintChildOnCreation;
  261. Time creationTime;
  262. EventHandlerRef eventHandlerRef;
  263. NSWindow* getOwnerWindow() const { return [((NSView*) getWindowHandle()) window]; }
  264. };
  265. #endif // JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_H_INCLUDED