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.

577 lines
18KB

  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 float x, const float y)
  75. {
  76. MouseInputSource* const mouse = Desktop::getInstance().getMouseSource (touchIndex);
  77. jassert (mouse != nullptr); // not enough sources!
  78. mouse->handleWheel (this, positionWithinPeer, time, x, y);
  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 ComponentPeerHelpers
  319. {
  320. FileDragAndDropTarget* findDragAndDropTarget (Component* c,
  321. const StringArray& files,
  322. FileDragAndDropTarget* const lastOne)
  323. {
  324. while (c != nullptr)
  325. {
  326. FileDragAndDropTarget* const t = dynamic_cast <FileDragAndDropTarget*> (c);
  327. if (t != nullptr && (t == lastOne || t->isInterestedInFileDrag (files)))
  328. return t;
  329. c = c->getParentComponent();
  330. }
  331. return nullptr;
  332. }
  333. }
  334. bool ComponentPeer::handleFileDragMove (const StringArray& files, const Point<int>& position)
  335. {
  336. updateCurrentModifiers();
  337. FileDragAndDropTarget* lastTarget
  338. = dynamic_cast<FileDragAndDropTarget*> (static_cast<Component*> (dragAndDropTargetComponent));
  339. FileDragAndDropTarget* newTarget = nullptr;
  340. Component* const compUnderMouse = component->getComponentAt (position);
  341. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  342. {
  343. lastDragAndDropCompUnderMouse = compUnderMouse;
  344. newTarget = ComponentPeerHelpers::findDragAndDropTarget (compUnderMouse, files, lastTarget);
  345. if (newTarget != lastTarget)
  346. {
  347. if (lastTarget != nullptr)
  348. lastTarget->fileDragExit (files);
  349. dragAndDropTargetComponent = nullptr;
  350. if (newTarget != nullptr)
  351. {
  352. dragAndDropTargetComponent = dynamic_cast <Component*> (newTarget);
  353. const Point<int> pos (dragAndDropTargetComponent->getLocalPoint (component, position));
  354. newTarget->fileDragEnter (files, pos.getX(), pos.getY());
  355. }
  356. }
  357. }
  358. else
  359. {
  360. newTarget = lastTarget;
  361. }
  362. if (newTarget == nullptr)
  363. return false;
  364. Component* const targetComp = dynamic_cast <Component*> (newTarget);
  365. const Point<int> pos (targetComp->getLocalPoint (component, position));
  366. newTarget->fileDragMove (files, pos.getX(), pos.getY());
  367. return true;
  368. }
  369. bool ComponentPeer::handleFileDragExit (const StringArray& files)
  370. {
  371. const bool used = handleFileDragMove (files, Point<int> (-1, -1));
  372. jassert (dragAndDropTargetComponent == nullptr);
  373. lastDragAndDropCompUnderMouse = nullptr;
  374. return used;
  375. }
  376. // We'll use an async message to deliver the drop, because if the target decides
  377. // to run a modal loop, it can gum-up the operating system..
  378. class AsyncFileDropMessage : public CallbackMessage
  379. {
  380. public:
  381. AsyncFileDropMessage (Component* target_, FileDragAndDropTarget* dropTarget_,
  382. const Point<int>& position_, const StringArray& files_)
  383. : target (target_), dropTarget (dropTarget_), position (position_), files (files_)
  384. {
  385. }
  386. void messageCallback()
  387. {
  388. if (target.get() != nullptr)
  389. dropTarget->filesDropped (files, position.getX(), position.getY());
  390. }
  391. private:
  392. WeakReference<Component> target;
  393. FileDragAndDropTarget* const dropTarget;
  394. const Point<int> position;
  395. const StringArray files;
  396. JUCE_DECLARE_NON_COPYABLE (AsyncFileDropMessage);
  397. };
  398. bool ComponentPeer::handleFileDragDrop (const StringArray& files, const Point<int>& position)
  399. {
  400. handleFileDragMove (files, position);
  401. if (dragAndDropTargetComponent != nullptr)
  402. {
  403. FileDragAndDropTarget* const target
  404. = dynamic_cast<FileDragAndDropTarget*> (static_cast<Component*> (dragAndDropTargetComponent));
  405. dragAndDropTargetComponent = nullptr;
  406. lastDragAndDropCompUnderMouse = nullptr;
  407. if (target != nullptr)
  408. {
  409. Component* const targetComp = dynamic_cast <Component*> (target);
  410. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  411. {
  412. targetComp->internalModalInputAttempt();
  413. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  414. return true;
  415. }
  416. (new AsyncFileDropMessage (targetComp, target, targetComp->getLocalPoint (component, position), files))->post();
  417. return true;
  418. }
  419. }
  420. return false;
  421. }
  422. //==============================================================================
  423. void ComponentPeer::handleUserClosingWindow()
  424. {
  425. updateCurrentModifiers();
  426. component->userTriedToCloseWindow();
  427. }
  428. //==============================================================================
  429. void ComponentPeer::clearMaskedRegion()
  430. {
  431. maskedRegion.clear();
  432. }
  433. void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
  434. {
  435. maskedRegion.add (area);
  436. }
  437. //==============================================================================
  438. StringArray ComponentPeer::getAvailableRenderingEngines()
  439. {
  440. return StringArray ("Software Renderer");
  441. }
  442. int ComponentPeer::getCurrentRenderingEngine() const
  443. {
  444. return 0;
  445. }
  446. void ComponentPeer::setCurrentRenderingEngine (int /*index*/)
  447. {
  448. }