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.

555 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. break;
  149. }
  150. }
  151. }
  152. return keyWasUsed;
  153. }
  154. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  155. {
  156. updateCurrentModifiers();
  157. bool keyWasUsed = false;
  158. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  159. {
  160. const WeakReference<Component> deletionChecker (target);
  161. keyWasUsed = target->keyStateChanged (isKeyDown);
  162. if (keyWasUsed || deletionChecker == nullptr)
  163. break;
  164. if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
  165. {
  166. for (int i = keyListeners->size(); --i >= 0;)
  167. {
  168. keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);
  169. if (keyWasUsed || deletionChecker == nullptr)
  170. return keyWasUsed;
  171. i = jmin (i, keyListeners->size());
  172. }
  173. }
  174. }
  175. return keyWasUsed;
  176. }
  177. void ComponentPeer::handleModifierKeysChange()
  178. {
  179. updateCurrentModifiers();
  180. Component* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  181. if (target == nullptr)
  182. target = Component::getCurrentlyFocusedComponent();
  183. if (target == nullptr)
  184. target = &component;
  185. if (target != nullptr)
  186. target->internalModifierKeysChanged();
  187. }
  188. TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
  189. {
  190. Component* const c = Component::getCurrentlyFocusedComponent();
  191. if (component.isParentOf (c))
  192. if (TextInputTarget* const ti = dynamic_cast <TextInputTarget*> (c))
  193. if (ti->isTextInputActive())
  194. return ti;
  195. return nullptr;
  196. }
  197. void ComponentPeer::dismissPendingTextInput() {}
  198. //==============================================================================
  199. void ComponentPeer::handleBroughtToFront()
  200. {
  201. updateCurrentModifiers();
  202. component.internalBroughtToFront();
  203. }
  204. void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
  205. {
  206. constrainer = newConstrainer;
  207. }
  208. void ComponentPeer::handleMovedOrResized()
  209. {
  210. updateCurrentModifiers();
  211. const bool nowMinimised = isMinimised();
  212. if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
  213. {
  214. const WeakReference<Component> deletionChecker (&component);
  215. const Rectangle<int> newBounds (getBounds());
  216. const bool wasMoved = (component.getPosition() != newBounds.getPosition());
  217. const bool wasResized = (component.getWidth() != newBounds.getWidth() || component.getHeight() != newBounds.getHeight());
  218. if (wasMoved || wasResized)
  219. {
  220. component.bounds = newBounds;
  221. if (wasResized)
  222. component.repaint();
  223. component.sendMovedResizedMessages (wasMoved, wasResized);
  224. if (deletionChecker == nullptr)
  225. return;
  226. }
  227. }
  228. if (isWindowMinimised != nowMinimised)
  229. {
  230. isWindowMinimised = nowMinimised;
  231. component.minimisationStateChanged (nowMinimised);
  232. component.sendVisibilityChangeMessage();
  233. }
  234. if (! isFullScreen())
  235. lastNonFullscreenBounds = component.getBounds();
  236. }
  237. void ComponentPeer::handleFocusGain()
  238. {
  239. updateCurrentModifiers();
  240. if (component.isParentOf (lastFocusedComponent))
  241. {
  242. Component::currentlyFocusedComponent = lastFocusedComponent;
  243. Desktop::getInstance().triggerFocusCallback();
  244. lastFocusedComponent->internalFocusGain (Component::focusChangedDirectly);
  245. }
  246. else
  247. {
  248. if (! component.isCurrentlyBlockedByAnotherModalComponent())
  249. component.grabKeyboardFocus();
  250. else
  251. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  252. }
  253. }
  254. void ComponentPeer::handleFocusLoss()
  255. {
  256. updateCurrentModifiers();
  257. if (component.hasKeyboardFocus (true))
  258. {
  259. lastFocusedComponent = Component::currentlyFocusedComponent;
  260. if (lastFocusedComponent != nullptr)
  261. {
  262. Component::currentlyFocusedComponent = nullptr;
  263. Desktop::getInstance().triggerFocusCallback();
  264. lastFocusedComponent->internalFocusLoss (Component::focusChangedByMouseClick);
  265. }
  266. }
  267. }
  268. Component* ComponentPeer::getLastFocusedSubcomponent() const noexcept
  269. {
  270. return (component.isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing())
  271. ? static_cast <Component*> (lastFocusedComponent)
  272. : &component;
  273. }
  274. void ComponentPeer::handleScreenSizeChange()
  275. {
  276. updateCurrentModifiers();
  277. component.parentSizeChanged();
  278. handleMovedOrResized();
  279. }
  280. void ComponentPeer::setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept
  281. {
  282. lastNonFullscreenBounds = newBounds;
  283. }
  284. const Rectangle<int>& ComponentPeer::getNonFullScreenBounds() const noexcept
  285. {
  286. return lastNonFullscreenBounds;
  287. }
  288. Rectangle<int> ComponentPeer::localToGlobal (const Rectangle<int>& relativePosition)
  289. {
  290. return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
  291. }
  292. Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
  293. {
  294. return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
  295. }
  296. //==============================================================================
  297. namespace DragHelpers
  298. {
  299. static bool isFileDrag (const ComponentPeer::DragInfo& info)
  300. {
  301. return info.files.size() > 0;
  302. }
  303. static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
  304. {
  305. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target) != nullptr
  306. : dynamic_cast <TextDragAndDropTarget*> (target) != nullptr;
  307. }
  308. static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
  309. {
  310. return isFileDrag (info) ? dynamic_cast <FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
  311. : dynamic_cast <TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
  312. }
  313. static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* const lastOne)
  314. {
  315. for (; c != nullptr; c = c->getParentComponent())
  316. if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
  317. return c;
  318. return nullptr;
  319. }
  320. // We'll use an async message to deliver the drop, because if the target decides
  321. // to run a modal loop, it can gum-up the operating system..
  322. class AsyncDropMessage : public CallbackMessage
  323. {
  324. public:
  325. AsyncDropMessage (Component* c, const ComponentPeer::DragInfo& d) : target (c), info (d) {}
  326. void messageCallback()
  327. {
  328. if (Component* const c = target.get())
  329. {
  330. if (isFileDrag (info))
  331. dynamic_cast <FileDragAndDropTarget*> (c)->filesDropped (info.files, info.position.x, info.position.y);
  332. else
  333. dynamic_cast <TextDragAndDropTarget*> (c)->textDropped (info.text, info.position.x, info.position.y);
  334. }
  335. }
  336. private:
  337. WeakReference<Component> target;
  338. const ComponentPeer::DragInfo info;
  339. JUCE_DECLARE_NON_COPYABLE (AsyncDropMessage);
  340. };
  341. }
  342. bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
  343. {
  344. updateCurrentModifiers();
  345. Component* const compUnderMouse = component.getComponentAt (info.position);
  346. Component* const lastTarget = dragAndDropTargetComponent;
  347. Component* newTarget = nullptr;
  348. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  349. {
  350. lastDragAndDropCompUnderMouse = compUnderMouse;
  351. newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
  352. if (newTarget != lastTarget)
  353. {
  354. if (lastTarget != nullptr)
  355. {
  356. if (DragHelpers::isFileDrag (info))
  357. dynamic_cast <FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
  358. else
  359. dynamic_cast <TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
  360. }
  361. dragAndDropTargetComponent = nullptr;
  362. if (DragHelpers::isSuitableTarget (info, newTarget))
  363. {
  364. dragAndDropTargetComponent = newTarget;
  365. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  366. if (DragHelpers::isFileDrag (info))
  367. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
  368. else
  369. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
  370. }
  371. }
  372. }
  373. else
  374. {
  375. newTarget = lastTarget;
  376. }
  377. if (! DragHelpers::isSuitableTarget (info, newTarget))
  378. return false;
  379. const Point<int> pos (newTarget->getLocalPoint (&component, info.position));
  380. if (DragHelpers::isFileDrag (info))
  381. dynamic_cast <FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
  382. else
  383. dynamic_cast <TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
  384. return true;
  385. }
  386. bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
  387. {
  388. DragInfo info2 (info);
  389. info2.position.setXY (-1, -1);
  390. const bool used = handleDragMove (info2);
  391. jassert (dragAndDropTargetComponent == nullptr);
  392. lastDragAndDropCompUnderMouse = nullptr;
  393. return used;
  394. }
  395. bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
  396. {
  397. handleDragMove (info);
  398. if (Component* const targetComp = dragAndDropTargetComponent)
  399. {
  400. dragAndDropTargetComponent = nullptr;
  401. lastDragAndDropCompUnderMouse = nullptr;
  402. if (DragHelpers::isSuitableTarget (info, targetComp))
  403. {
  404. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  405. {
  406. targetComp->internalModalInputAttempt();
  407. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  408. return true;
  409. }
  410. ComponentPeer::DragInfo info2 (info);
  411. info2.position = targetComp->getLocalPoint (&component, info.position);
  412. (new DragHelpers::AsyncDropMessage (targetComp, info2))->post();
  413. return true;
  414. }
  415. }
  416. return false;
  417. }
  418. //==============================================================================
  419. void ComponentPeer::handleUserClosingWindow()
  420. {
  421. updateCurrentModifiers();
  422. component.userTriedToCloseWindow();
  423. }
  424. //==============================================================================
  425. void ComponentPeer::clearMaskedRegion()
  426. {
  427. maskedRegion.clear();
  428. }
  429. void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
  430. {
  431. maskedRegion.add (area);
  432. }
  433. //==============================================================================
  434. StringArray ComponentPeer::getAvailableRenderingEngines() { return StringArray ("Software Renderer"); }
  435. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  436. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); (void) index; }