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.

557 lines
18KB

  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. static Array <ComponentPeer*> heavyweightPeers;
  19. static uint32 lastUniqueID = 1;
  20. //==============================================================================
  21. ComponentPeer::ComponentPeer (Component& component_, const int styleFlags_)
  22. : component (component_),
  23. styleFlags (styleFlags_),
  24. constrainer (nullptr),
  25. lastDragAndDropCompUnderMouse (nullptr),
  26. uniqueID (lastUniqueID += 2), // increment by 2 so that this can never hit 0
  27. fakeMouseMessageSent (false),
  28. isWindowMinimised (false)
  29. {
  30. heavyweightPeers.add (this);
  31. }
  32. ComponentPeer::~ComponentPeer()
  33. {
  34. heavyweightPeers.removeFirstMatchingValue (this);
  35. Desktop::getInstance().triggerFocusCallback();
  36. }
  37. //==============================================================================
  38. int ComponentPeer::getNumPeers() noexcept
  39. {
  40. return heavyweightPeers.size();
  41. }
  42. ComponentPeer* ComponentPeer::getPeer (const int index) noexcept
  43. {
  44. return heavyweightPeers [index];
  45. }
  46. ComponentPeer* ComponentPeer::getPeerFor (const Component* const component) noexcept
  47. {
  48. for (int i = heavyweightPeers.size(); --i >= 0;)
  49. {
  50. ComponentPeer* const peer = heavyweightPeers.getUnchecked(i);
  51. if (&(peer->getComponent()) == component)
  52. return peer;
  53. }
  54. return nullptr;
  55. }
  56. bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept
  57. {
  58. return heavyweightPeers.contains (const_cast <ComponentPeer*> (peer));
  59. }
  60. void ComponentPeer::updateCurrentModifiers() noexcept
  61. {
  62. ModifierKeys::updateCurrentModifiers();
  63. }
  64. //==============================================================================
  65. void ComponentPeer::handleMouseEvent (const int touchIndex, const Point<int>& positionWithinPeer,
  66. const ModifierKeys& newMods, const int64 time)
  67. {
  68. MouseInputSource* const mouse = Desktop::getInstance().getMouseSource (touchIndex);
  69. jassert (mouse != nullptr); // not enough sources!
  70. mouse->handleEvent (this, positionWithinPeer, time, newMods);
  71. }
  72. void ComponentPeer::handleMouseWheel (const int touchIndex, const Point<int>& positionWithinPeer,
  73. const int64 time, const MouseWheelDetails& wheel)
  74. {
  75. MouseInputSource* const mouse = Desktop::getInstance().getMouseSource (touchIndex);
  76. jassert (mouse != nullptr); // not enough sources!
  77. mouse->handleWheel (this, positionWithinPeer, time, wheel);
  78. }
  79. //==============================================================================
  80. void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo)
  81. {
  82. Graphics g (&contextToPaintTo);
  83. #if JUCE_ENABLE_REPAINT_DEBUGGING
  84. g.saveState();
  85. #endif
  86. JUCE_TRY
  87. {
  88. component.paintEntireComponent (g, true);
  89. }
  90. JUCE_CATCH_EXCEPTION
  91. #if JUCE_ENABLE_REPAINT_DEBUGGING
  92. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  93. // clearly when things are being repainted.
  94. g.restoreState();
  95. static Random rng;
  96. g.fillAll (Colour ((uint8) rng.nextInt (255),
  97. (uint8) rng.nextInt (255),
  98. (uint8) rng.nextInt (255),
  99. (uint8) 0x50));
  100. #endif
  101. /** If this fails, it's probably be because your CPU floating-point precision mode has
  102. been set to low.. This setting is sometimes changed by things like Direct3D, and can
  103. mess up a lot of the calculations that the library needs to do.
  104. */
  105. jassert (roundToInt (10.1f) == 10);
  106. }
  107. Component* ComponentPeer::getTargetForKeyPress()
  108. {
  109. Component* c = Component::getCurrentlyFocusedComponent();
  110. if (c == nullptr)
  111. c = &component;
  112. if (c->isCurrentlyBlockedByAnotherModalComponent())
  113. if (Component* const currentModalComp = Component::getCurrentlyModalComponent())
  114. c = currentModalComp;
  115. return c;
  116. }
  117. bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
  118. {
  119. updateCurrentModifiers();
  120. bool keyWasUsed = false;
  121. const KeyPress keyInfo (keyCode,
  122. ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
  123. textCharacter);
  124. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  125. {
  126. const WeakReference<Component> deletionChecker (target);
  127. if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
  128. {
  129. for (int i = keyListeners->size(); --i >= 0;)
  130. {
  131. keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);
  132. if (keyWasUsed || deletionChecker == nullptr)
  133. return keyWasUsed;
  134. i = jmin (i, keyListeners->size());
  135. }
  136. }
  137. keyWasUsed = target->keyPressed (keyInfo);
  138. if (keyWasUsed || deletionChecker == nullptr)
  139. break;
  140. if (Component* const currentlyFocused = Component::getCurrentlyFocusedComponent())
  141. {
  142. const bool isTab = (keyInfo == KeyPress::tabKey);
  143. const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
  144. if (isTab || isShiftTab)
  145. {
  146. currentlyFocused->moveKeyboardFocusToSibling (isTab);
  147. keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
  148. if (keyWasUsed || deletionChecker == nullptr)
  149. break;
  150. }
  151. }
  152. }
  153. return keyWasUsed;
  154. }
  155. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  156. {
  157. updateCurrentModifiers();
  158. bool keyWasUsed = false;
  159. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  160. {
  161. const WeakReference<Component> deletionChecker (target);
  162. keyWasUsed = target->keyStateChanged (isKeyDown);
  163. if (keyWasUsed || deletionChecker == nullptr)
  164. break;
  165. if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
  166. {
  167. for (int i = keyListeners->size(); --i >= 0;)
  168. {
  169. keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);
  170. if (keyWasUsed || deletionChecker == nullptr)
  171. return keyWasUsed;
  172. i = jmin (i, keyListeners->size());
  173. }
  174. }
  175. }
  176. return keyWasUsed;
  177. }
  178. void ComponentPeer::handleModifierKeysChange()
  179. {
  180. updateCurrentModifiers();
  181. Component* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  182. if (target == nullptr)
  183. target = Component::getCurrentlyFocusedComponent();
  184. if (target == nullptr)
  185. target = &component;
  186. if (target != nullptr)
  187. target->internalModifierKeysChanged();
  188. }
  189. TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
  190. {
  191. Component* const c = Component::getCurrentlyFocusedComponent();
  192. if (component.isParentOf (c))
  193. if (TextInputTarget* const ti = dynamic_cast <TextInputTarget*> (c))
  194. if (ti->isTextInputActive())
  195. return ti;
  196. return nullptr;
  197. }
  198. void ComponentPeer::dismissPendingTextInput() {}
  199. //==============================================================================
  200. void ComponentPeer::handleBroughtToFront()
  201. {
  202. updateCurrentModifiers();
  203. component.internalBroughtToFront();
  204. }
  205. void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
  206. {
  207. constrainer = newConstrainer;
  208. }
  209. void ComponentPeer::handleMovedOrResized()
  210. {
  211. updateCurrentModifiers();
  212. const bool nowMinimised = isMinimised();
  213. if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
  214. {
  215. const WeakReference<Component> deletionChecker (&component);
  216. const Rectangle<int> newBounds (getBounds());
  217. const bool wasMoved = (component.getPosition() != newBounds.getPosition());
  218. const bool wasResized = (component.getWidth() != newBounds.getWidth() || component.getHeight() != newBounds.getHeight());
  219. if (wasMoved || wasResized)
  220. {
  221. component.bounds = newBounds;
  222. if (wasResized)
  223. component.repaint();
  224. component.sendMovedResizedMessages (wasMoved, wasResized);
  225. if (deletionChecker == nullptr)
  226. return;
  227. }
  228. }
  229. if (isWindowMinimised != nowMinimised)
  230. {
  231. isWindowMinimised = nowMinimised;
  232. component.minimisationStateChanged (nowMinimised);
  233. component.sendVisibilityChangeMessage();
  234. }
  235. if (! isFullScreen())
  236. lastNonFullscreenBounds = component.getBounds();
  237. }
  238. void ComponentPeer::handleFocusGain()
  239. {
  240. updateCurrentModifiers();
  241. if (component.isParentOf (lastFocusedComponent))
  242. {
  243. Component::currentlyFocusedComponent = lastFocusedComponent;
  244. Desktop::getInstance().triggerFocusCallback();
  245. lastFocusedComponent->internalFocusGain (Component::focusChangedDirectly);
  246. }
  247. else
  248. {
  249. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  250. component.grabKeyboardFocus();
  251. else
  252. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  253. }
  254. }
  255. void ComponentPeer::handleFocusLoss()
  256. {
  257. updateCurrentModifiers();
  258. if (component.hasKeyboardFocus (true))
  259. {
  260. lastFocusedComponent = Component::currentlyFocusedComponent;
  261. if (lastFocusedComponent != nullptr)
  262. {
  263. Component::currentlyFocusedComponent = nullptr;
  264. Desktop::getInstance().triggerFocusCallback();
  265. lastFocusedComponent->internalFocusLoss (Component::focusChangedByMouseClick);
  266. }
  267. }
  268. }
  269. Component* ComponentPeer::getLastFocusedSubcomponent() const noexcept
  270. {
  271. return (component.isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing())
  272. ? static_cast <Component*> (lastFocusedComponent)
  273. : &component;
  274. }
  275. void ComponentPeer::handleScreenSizeChange()
  276. {
  277. updateCurrentModifiers();
  278. component.parentSizeChanged();
  279. handleMovedOrResized();
  280. }
  281. void ComponentPeer::setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept
  282. {
  283. lastNonFullscreenBounds = newBounds;
  284. }
  285. const Rectangle<int>& ComponentPeer::getNonFullScreenBounds() const noexcept
  286. {
  287. return lastNonFullscreenBounds;
  288. }
  289. Rectangle<int> ComponentPeer::localToGlobal (const Rectangle<int>& relativePosition)
  290. {
  291. return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
  292. }
  293. Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
  294. {
  295. return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
  296. }
  297. //==============================================================================
  298. namespace DragHelpers
  299. {
  300. static bool isFileDrag (const ComponentPeer::DragInfo& info)
  301. {
  302. return info.files.size() > 0;
  303. }
  304. static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
  305. {
  306. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target) != nullptr
  307. : dynamic_cast <TextDragAndDropTarget*> (target) != nullptr;
  308. }
  309. static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
  310. {
  311. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
  312. : dynamic_cast <TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
  313. }
  314. static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* const lastOne)
  315. {
  316. for (; c != nullptr; c = c->getParentComponent())
  317. if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
  318. return c;
  319. return nullptr;
  320. }
  321. // We'll use an async message to deliver the drop, because if the target decides
  322. // to run a modal loop, it can gum-up the operating system..
  323. class AsyncDropMessage : public CallbackMessage
  324. {
  325. public:
  326. AsyncDropMessage (Component* c, const ComponentPeer::DragInfo& d) : target (c), info (d) {}
  327. void messageCallback()
  328. {
  329. if (Component* const c = target.get())
  330. {
  331. if (isFileDrag (info))
  332. dynamic_cast <FileDragAndDropTarget*> (c)->filesDropped (info.files, info.position.x, info.position.y);
  333. else
  334. dynamic_cast <TextDragAndDropTarget*> (c)->textDropped (info.text, info.position.x, info.position.y);
  335. }
  336. }
  337. private:
  338. WeakReference<Component> target;
  339. const ComponentPeer::DragInfo info;
  340. JUCE_DECLARE_NON_COPYABLE (AsyncDropMessage)
  341. };
  342. }
  343. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  344. {
  345. updateCurrentModifiers();
  346. Component* const compUnderMouse = component.getComponentAt (info.position);
  347. Component* const lastTarget = dragAndDropTargetComponent;
  348. Component* newTarget = nullptr;
  349. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  350. {
  351. lastDragAndDropCompUnderMouse = compUnderMouse;
  352. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  353. if (newTarget != lastTarget)
  354. {
  355. if (lastTarget != nullptr)
  356. {
  357. if (DragHelpers::isFileDrag (info))
  358. dynamic_cast <FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  359. else
  360. dynamic_cast <TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  361. }
  362. dragAndDropTargetComponent = nullptr;
  363. if (DragHelpers::isSuitableTarget (info, newTarget))
  364. {
  365. dragAndDropTargetComponent = newTarget;
  366. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  367. if (DragHelpers::isFileDrag (info))
  368. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  369. else
  370. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  371. }
  372. }
  373. }
  374. else
  375. {
  376. newTarget = lastTarget;
  377. }
  378. if (! DragHelpers::isSuitableTarget (info, newTarget))
  379. return false;
  380. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  381. if (DragHelpers::isFileDrag (info))
  382. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  383. else
  384. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  385. return true;
  386. }
  387. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  388. {
  389. DragInfo info2 (info);
  390. info2.position.setXY (-1, -1);
  391. const bool used = handleDragMove (info2);
  392. jassert (dragAndDropTargetComponent == nullptr);
  393. lastDragAndDropCompUnderMouse = nullptr;
  394. return used;
  395. }
  396. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  397. {
  398. handleDragMove (info);
  399. if (Component* const targetComp = dragAndDropTargetComponent)
  400. {
  401. dragAndDropTargetComponent = nullptr;
  402. lastDragAndDropCompUnderMouse = nullptr;
  403. if (DragHelpers::isSuitableTarget (info, targetComp))
  404. {
  405. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  406. {
  407. targetComp->internalModalInputAttempt();
  408. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  409. return true;
  410. }
  411. ComponentPeer::DragInfo info2 (info);
  412. info2.position = targetComp->getLocalPoint (&component, info.position);
  413. (new DragHelpers::AsyncDropMessage (targetComp, info2))->post();
  414. return true;
  415. }
  416. }
  417. return false;
  418. }
  419. //==============================================================================
  420. void ComponentPeer::handleUserClosingWindow()
  421. {
  422. updateCurrentModifiers();
  423. component.userTriedToCloseWindow();
  424. }
  425. //==============================================================================
  426. void ComponentPeer::clearMaskedRegion()
  427. {
  428. maskedRegion.clear();
  429. }
  430. void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
  431. {
  432. maskedRegion.add (area);
  433. }
  434. //==============================================================================
  435. StringArray ComponentPeer::getAvailableRenderingEngines() { return StringArray ("Software Renderer"); }
  436. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  437. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); (void) index; }