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.

587 lines
19KB

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