Audio plugin host https://kx.studio/carla
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.

284 lines
7.5KB

  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. class ModalComponentManager::ModalItem : public ComponentMovementWatcher
  18. {
  19. public:
  20. ModalItem (Component* const comp, const bool autoDelete_)
  21. : ComponentMovementWatcher (comp),
  22. component (comp), returnValue (0),
  23. isActive (true), autoDelete (autoDelete_)
  24. {
  25. jassert (comp != nullptr);
  26. }
  27. void componentMovedOrResized (bool, bool) override {}
  28. void componentPeerChanged() override
  29. {
  30. if (! component->isShowing())
  31. cancel();
  32. }
  33. void componentVisibilityChanged() override
  34. {
  35. if (! component->isShowing())
  36. cancel();
  37. }
  38. void componentBeingDeleted (Component& comp) override
  39. {
  40. ComponentMovementWatcher::componentBeingDeleted (comp);
  41. if (component == &comp || comp.isParentOf (component))
  42. {
  43. autoDelete = false;
  44. cancel();
  45. }
  46. }
  47. void cancel()
  48. {
  49. if (isActive)
  50. {
  51. isActive = false;
  52. ModalComponentManager::getInstance()->triggerAsyncUpdate();
  53. }
  54. }
  55. Component* component;
  56. OwnedArray<Callback> callbacks;
  57. int returnValue;
  58. bool isActive, autoDelete;
  59. private:
  60. JUCE_DECLARE_NON_COPYABLE (ModalItem)
  61. };
  62. //==============================================================================
  63. ModalComponentManager::ModalComponentManager()
  64. {
  65. }
  66. ModalComponentManager::~ModalComponentManager()
  67. {
  68. clearSingletonInstance();
  69. }
  70. juce_ImplementSingleton_SingleThreaded (ModalComponentManager);
  71. //==============================================================================
  72. void ModalComponentManager::startModal (Component* component, bool autoDelete)
  73. {
  74. if (component != nullptr)
  75. stack.add (new ModalItem (component, autoDelete));
  76. }
  77. void ModalComponentManager::attachCallback (Component* component, Callback* callback)
  78. {
  79. if (callback != nullptr)
  80. {
  81. ScopedPointer<Callback> callbackDeleter (callback);
  82. for (int i = stack.size(); --i >= 0;)
  83. {
  84. ModalItem* const item = stack.getUnchecked(i);
  85. if (item->component == component)
  86. {
  87. item->callbacks.add (callback);
  88. callbackDeleter.release();
  89. break;
  90. }
  91. }
  92. }
  93. }
  94. void ModalComponentManager::endModal (Component* component)
  95. {
  96. for (int i = stack.size(); --i >= 0;)
  97. {
  98. ModalItem* const item = stack.getUnchecked(i);
  99. if (item->component == component)
  100. item->cancel();
  101. }
  102. }
  103. void ModalComponentManager::endModal (Component* component, int returnValue)
  104. {
  105. for (int i = stack.size(); --i >= 0;)
  106. {
  107. ModalItem* const item = stack.getUnchecked(i);
  108. if (item->component == component)
  109. {
  110. item->returnValue = returnValue;
  111. item->cancel();
  112. }
  113. }
  114. }
  115. int ModalComponentManager::getNumModalComponents() const
  116. {
  117. int n = 0;
  118. for (int i = 0; i < stack.size(); ++i)
  119. if (stack.getUnchecked(i)->isActive)
  120. ++n;
  121. return n;
  122. }
  123. Component* ModalComponentManager::getModalComponent (const int index) const
  124. {
  125. int n = 0;
  126. for (int i = stack.size(); --i >= 0;)
  127. {
  128. const ModalItem* const item = stack.getUnchecked(i);
  129. if (item->isActive)
  130. if (n++ == index)
  131. return item->component;
  132. }
  133. return nullptr;
  134. }
  135. bool ModalComponentManager::isModal (Component* const comp) const
  136. {
  137. for (int i = stack.size(); --i >= 0;)
  138. {
  139. const ModalItem* const item = stack.getUnchecked(i);
  140. if (item->isActive && item->component == comp)
  141. return true;
  142. }
  143. return false;
  144. }
  145. bool ModalComponentManager::isFrontModalComponent (Component* const comp) const
  146. {
  147. return comp == getModalComponent (0);
  148. }
  149. void ModalComponentManager::handleAsyncUpdate()
  150. {
  151. for (int i = stack.size(); --i >= 0;)
  152. {
  153. const ModalItem* const item = stack.getUnchecked(i);
  154. if (! item->isActive)
  155. {
  156. ScopedPointer<ModalItem> deleter (stack.removeAndReturn (i));
  157. Component::SafePointer<Component> compToDelete (item->autoDelete ? item->component : nullptr);
  158. for (int j = item->callbacks.size(); --j >= 0;)
  159. item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
  160. compToDelete.deleteAndZero();
  161. }
  162. }
  163. }
  164. void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
  165. {
  166. ComponentPeer* lastOne = nullptr;
  167. for (int i = 0; i < getNumModalComponents(); ++i)
  168. {
  169. Component* const c = getModalComponent (i);
  170. if (c == nullptr)
  171. break;
  172. ComponentPeer* peer = c->getPeer();
  173. if (peer != nullptr && peer != lastOne)
  174. {
  175. if (lastOne == nullptr)
  176. {
  177. peer->toFront (topOneShouldGrabFocus);
  178. if (topOneShouldGrabFocus)
  179. peer->grabFocus();
  180. }
  181. else
  182. peer->toBehind (lastOne);
  183. lastOne = peer;
  184. }
  185. }
  186. }
  187. #if JUCE_MODAL_LOOPS_PERMITTED
  188. class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback
  189. {
  190. public:
  191. ReturnValueRetriever (int& v, bool& done) : value (v), finished (done) {}
  192. void modalStateFinished (int returnValue)
  193. {
  194. finished = true;
  195. value = returnValue;
  196. }
  197. private:
  198. int& value;
  199. bool& finished;
  200. JUCE_DECLARE_NON_COPYABLE (ReturnValueRetriever)
  201. };
  202. int ModalComponentManager::runEventLoopForCurrentComponent()
  203. {
  204. // This can only be run from the message thread!
  205. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  206. int returnValue = 0;
  207. if (Component* currentlyModal = getModalComponent (0))
  208. {
  209. WeakReference<Component> prevFocused (Component::getCurrentlyFocusedComponent());
  210. bool finished = false;
  211. attachCallback (currentlyModal, new ReturnValueRetriever (returnValue, finished));
  212. JUCE_TRY
  213. {
  214. while (! finished)
  215. {
  216. if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
  217. break;
  218. }
  219. }
  220. JUCE_CATCH_EXCEPTION
  221. if (prevFocused != nullptr && ! prevFocused->isCurrentlyBlockedByAnotherModalComponent())
  222. prevFocused->grabKeyboardFocus();
  223. }
  224. return returnValue;
  225. }
  226. #endif