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.

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