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.

565 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. ModifierKeys::updateCurrentModifiers();
  81. Graphics g (&contextToPaintTo);
  82. #if JUCE_ENABLE_REPAINT_DEBUGGING
  83. g.saveState();
  84. #endif
  85. JUCE_TRY
  86. {
  87. component.paintEntireComponent (g, true);
  88. }
  89. JUCE_CATCH_EXCEPTION
  90. #if JUCE_ENABLE_REPAINT_DEBUGGING
  91. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  92. // clearly when things are being repainted.
  93. g.restoreState();
  94. static Random rng;
  95. g.fillAll (Colour ((uint8) rng.nextInt (255),
  96. (uint8) rng.nextInt (255),
  97. (uint8) rng.nextInt (255),
  98. (uint8) 0x50));
  99. #endif
  100. /** If this fails, it's probably be because your CPU floating-point precision mode has
  101. been set to low.. This setting is sometimes changed by things like Direct3D, and can
  102. mess up a lot of the calculations that the library needs to do.
  103. */
  104. jassert (roundToInt (10.1f) == 10);
  105. }
  106. Component* ComponentPeer::getTargetForKeyPress()
  107. {
  108. Component* c = Component::getCurrentlyFocusedComponent();
  109. if (c == nullptr)
  110. c = &component;
  111. if (c->isCurrentlyBlockedByAnotherModalComponent())
  112. if (Component* const currentModalComp = Component::getCurrentlyModalComponent())
  113. c = currentModalComp;
  114. return c;
  115. }
  116. bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
  117. {
  118. ModifierKeys::updateCurrentModifiers();
  119. bool keyWasUsed = false;
  120. const KeyPress keyInfo (keyCode,
  121. ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
  122. textCharacter);
  123. for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
  124. {
  125. const WeakReference<Component> deletionChecker (target);
  126. if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
  127. {
  128. for (int i = keyListeners->size(); --i >= 0;)
  129. {
  130. keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);
  131. if (keyWasUsed || deletionChecker == nullptr)
  132. return keyWasUsed;
  133. i = jmin (i, keyListeners->size());
  134. }
  135. }
  136. keyWasUsed = target->keyPressed (keyInfo);
  137. if (keyWasUsed || deletionChecker == nullptr)
  138. break;
  139. if (Component* const currentlyFocused = Component::getCurrentlyFocusedComponent())
  140. {
  141. const bool isTab = (keyInfo == KeyPress::tabKey);
  142. const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
  143. if (isTab || isShiftTab)
  144. {
  145. currentlyFocused->moveKeyboardFocusToSibling (isTab);
  146. keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
  147. if (keyWasUsed || deletionChecker == nullptr)
  148. break;
  149. }
  150. }
  151. }
  152. return keyWasUsed;
  153. }
  154. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  155. {
  156. ModifierKeys::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. ModifierKeys::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. ModifierKeys::updateCurrentModifiers();
  202. component.internalBroughtToFront();
  203. }
  204. void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
  205. {
  206. constrainer = newConstrainer;
  207. }
  208. void ComponentPeer::handleMovedOrResized()
  209. {
  210. ModifierKeys::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. ModifierKeys::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. ModifierKeys::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. ModifierKeys::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. ModifierKeys::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. ModifierKeys::updateCurrentModifiers();
  422. component.userTriedToCloseWindow();
  423. }
  424. bool ComponentPeer::setDocumentEditedStatus (bool)
  425. {
  426. return false;
  427. }
  428. void ComponentPeer::setRepresentedFile (const File&)
  429. {
  430. }
  431. //==============================================================================
  432. void ComponentPeer::clearMaskedRegion()
  433. {
  434. maskedRegion.clear();
  435. }
  436. void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
  437. {
  438. maskedRegion.add (area);
  439. }
  440. //==============================================================================
  441. StringArray ComponentPeer::getAvailableRenderingEngines() { return StringArray ("Software Renderer"); }
  442. int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
  443. void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); (void) index; }