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.

602 lines
19KB

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