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.

563 lines
18KB

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