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.

580 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. BEGIN_JUCE_NAMESPACE
  19. //#define JUCE_ENABLE_REPAINT_DEBUGGING 1
  20. //==============================================================================
  21. static Array <ComponentPeer*> heavyweightPeers;
  22. static uint32 lastUniqueID = 1;
  23. //==============================================================================
  24. ComponentPeer::ComponentPeer (Component* const component_, const int styleFlags_)
  25. : component (component_),
  26. styleFlags (styleFlags_),
  27. lastPaintTime (0),
  28. constrainer (nullptr),
  29. lastDragAndDropCompUnderMouse (nullptr),
  30. uniqueID (lastUniqueID += 2), // increment by 2 so that this can never hit 0
  31. fakeMouseMessageSent (false),
  32. isWindowMinimised (false)
  33. {
  34. heavyweightPeers.add (this);
  35. }
  36. ComponentPeer::~ComponentPeer()
  37. {
  38. heavyweightPeers.removeValue (this);
  39. Desktop::getInstance().triggerFocusCallback();
  40. }
  41. //==============================================================================
  42. int ComponentPeer::getNumPeers() noexcept
  43. {
  44. return heavyweightPeers.size();
  45. }
  46. ComponentPeer* ComponentPeer::getPeer (const int index) noexcept
  47. {
  48. return heavyweightPeers [index];
  49. }
  50. ComponentPeer* ComponentPeer::getPeerFor (const Component* const component) noexcept
  51. {
  52. for (int i = heavyweightPeers.size(); --i >= 0;)
  53. {
  54. ComponentPeer* const peer = heavyweightPeers.getUnchecked(i);
  55. if (peer->getComponent() == component)
  56. return peer;
  57. }
  58. return nullptr;
  59. }
  60. bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept
  61. {
  62. return heavyweightPeers.contains (const_cast <ComponentPeer*> (peer));
  63. }
  64. void ComponentPeer::updateCurrentModifiers() noexcept
  65. {
  66. ModifierKeys::updateCurrentModifiers();
  67. }
  68. //==============================================================================
  69. void ComponentPeer::handleMouseEvent (const int touchIndex, const Point<int>& positionWithinPeer, const ModifierKeys& newMods, const int64 time)
  70. {
  71. MouseInputSource* const mouse = Desktop::getInstance().getMouseSource (touchIndex);
  72. jassert (mouse != nullptr); // not enough sources!
  73. mouse->handleEvent (this, positionWithinPeer, time, newMods);
  74. }
  75. void ComponentPeer::handleMouseWheel (const int touchIndex, const Point<int>& positionWithinPeer, const int64 time, const float x, const float y)
  76. {
  77. MouseInputSource* const mouse = Desktop::getInstance().getMouseSource (touchIndex);
  78. jassert (mouse != nullptr); // not enough sources!
  79. mouse->handleWheel (this, positionWithinPeer, time, x, y);
  80. }
  81. //==============================================================================
  82. void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo)
  83. {
  84. Graphics g (&contextToPaintTo);
  85. #if JUCE_ENABLE_REPAINT_DEBUGGING
  86. g.saveState();
  87. #endif
  88. JUCE_TRY
  89. {
  90. component->paintEntireComponent (g, true);
  91. }
  92. JUCE_CATCH_EXCEPTION
  93. #if JUCE_ENABLE_REPAINT_DEBUGGING
  94. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  95. // clearly when things are being repainted.
  96. g.restoreState();
  97. static Random rng;
  98. g.fillAll (Colour ((uint8) rng.nextInt (255),
  99. (uint8) rng.nextInt (255),
  100. (uint8) rng.nextInt (255),
  101. (uint8) 0x50));
  102. #endif
  103. /** If this fails, it's probably be because your CPU floating-point precision mode has
  104. been set to low.. This setting is sometimes changed by things like Direct3D, and can
  105. mess up a lot of the calculations that the library needs to do.
  106. */
  107. jassert (roundToInt (10.1f) == 10);
  108. }
  109. bool ComponentPeer::handleKeyPress (const int keyCode,
  110. const juce_wchar textCharacter)
  111. {
  112. updateCurrentModifiers();
  113. Component* target = Component::getCurrentlyFocusedComponent() != nullptr
  114. ? Component::getCurrentlyFocusedComponent()
  115. : component;
  116. if (target->isCurrentlyBlockedByAnotherModalComponent())
  117. {
  118. Component* const currentModalComp = Component::getCurrentlyModalComponent();
  119. if (currentModalComp != nullptr)
  120. target = currentModalComp;
  121. }
  122. const KeyPress keyInfo (keyCode,
  123. ModifierKeys::getCurrentModifiers().getRawFlags()
  124. & ModifierKeys::allKeyboardModifiers,
  125. textCharacter);
  126. bool keyWasUsed = false;
  127. while (target != nullptr)
  128. {
  129. const WeakReference<Component> deletionChecker (target);
  130. const Array <KeyListener*>* const keyListeners = target->keyListeners;
  131. if (keyListeners != nullptr)
  132. {
  133. for (int i = keyListeners->size(); --i >= 0;)
  134. {
  135. keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);
  136. if (keyWasUsed || deletionChecker == nullptr)
  137. return keyWasUsed;
  138. i = jmin (i, keyListeners->size());
  139. }
  140. }
  141. keyWasUsed = target->keyPressed (keyInfo);
  142. if (keyWasUsed || deletionChecker == nullptr)
  143. break;
  144. Component* const currentlyFocused = Component::getCurrentlyFocusedComponent();
  145. if (currentlyFocused != nullptr)
  146. {
  147. const bool isTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::noModifiers, 0));
  148. const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
  149. if (isTab || isShiftTab)
  150. {
  151. currentlyFocused->moveKeyboardFocusToSibling (isTab);
  152. keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
  153. break;
  154. }
  155. }
  156. target = target->getParentComponent();
  157. }
  158. return keyWasUsed;
  159. }
  160. bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
  161. {
  162. updateCurrentModifiers();
  163. Component* target = Component::getCurrentlyFocusedComponent() != nullptr
  164. ? Component::getCurrentlyFocusedComponent()
  165. : component;
  166. if (target->isCurrentlyBlockedByAnotherModalComponent())
  167. {
  168. Component* const currentModalComp = Component::getCurrentlyModalComponent();
  169. if (currentModalComp != nullptr)
  170. target = currentModalComp;
  171. }
  172. bool keyWasUsed = false;
  173. while (target != nullptr)
  174. {
  175. const WeakReference<Component> deletionChecker (target);
  176. keyWasUsed = target->keyStateChanged (isKeyDown);
  177. if (keyWasUsed || deletionChecker == nullptr)
  178. break;
  179. const Array <KeyListener*>* const keyListeners = target->keyListeners;
  180. if (keyListeners != nullptr)
  181. {
  182. for (int i = keyListeners->size(); --i >= 0;)
  183. {
  184. keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);
  185. if (keyWasUsed || deletionChecker == nullptr)
  186. return keyWasUsed;
  187. i = jmin (i, keyListeners->size());
  188. }
  189. }
  190. target = target->getParentComponent();
  191. }
  192. return keyWasUsed;
  193. }
  194. void ComponentPeer::handleModifierKeysChange()
  195. {
  196. updateCurrentModifiers();
  197. Component* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  198. if (target == nullptr)
  199. target = Component::getCurrentlyFocusedComponent();
  200. if (target == nullptr)
  201. target = component;
  202. if (target != nullptr)
  203. target->internalModifierKeysChanged();
  204. }
  205. TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
  206. {
  207. Component* const c = Component::getCurrentlyFocusedComponent();
  208. if (component->isParentOf (c))
  209. {
  210. TextInputTarget* const ti = dynamic_cast <TextInputTarget*> (c);
  211. if (ti != nullptr && ti->isTextInputActive())
  212. return ti;
  213. }
  214. return nullptr;
  215. }
  216. void ComponentPeer::dismissPendingTextInput()
  217. {
  218. }
  219. //==============================================================================
  220. void ComponentPeer::handleBroughtToFront()
  221. {
  222. updateCurrentModifiers();
  223. if (component != nullptr)
  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 ComponentPeerHelpers
  320. {
  321. FileDragAndDropTarget* findDragAndDropTarget (Component* c,
  322. const StringArray& files,
  323. FileDragAndDropTarget* const lastOne)
  324. {
  325. while (c != nullptr)
  326. {
  327. FileDragAndDropTarget* const t = dynamic_cast <FileDragAndDropTarget*> (c);
  328. if (t != nullptr && (t == lastOne || t->isInterestedInFileDrag (files)))
  329. return t;
  330. c = c->getParentComponent();
  331. }
  332. return nullptr;
  333. }
  334. }
  335. bool ComponentPeer::handleFileDragMove (const StringArray& files, const Point<int>& position)
  336. {
  337. updateCurrentModifiers();
  338. FileDragAndDropTarget* lastTarget
  339. = dynamic_cast<FileDragAndDropTarget*> (static_cast<Component*> (dragAndDropTargetComponent));
  340. FileDragAndDropTarget* newTarget = nullptr;
  341. Component* const compUnderMouse = component->getComponentAt (position);
  342. if (compUnderMouse != lastDragAndDropCompUnderMouse)
  343. {
  344. lastDragAndDropCompUnderMouse = compUnderMouse;
  345. newTarget = ComponentPeerHelpers::findDragAndDropTarget (compUnderMouse, files, lastTarget);
  346. if (newTarget != lastTarget)
  347. {
  348. if (lastTarget != nullptr)
  349. lastTarget->fileDragExit (files);
  350. dragAndDropTargetComponent = nullptr;
  351. if (newTarget != nullptr)
  352. {
  353. dragAndDropTargetComponent = dynamic_cast <Component*> (newTarget);
  354. const Point<int> pos (dragAndDropTargetComponent->getLocalPoint (component, position));
  355. newTarget->fileDragEnter (files, pos.getX(), pos.getY());
  356. }
  357. }
  358. }
  359. else
  360. {
  361. newTarget = lastTarget;
  362. }
  363. if (newTarget == nullptr)
  364. return false;
  365. Component* const targetComp = dynamic_cast <Component*> (newTarget);
  366. const Point<int> pos (targetComp->getLocalPoint (component, position));
  367. newTarget->fileDragMove (files, pos.getX(), pos.getY());
  368. return true;
  369. }
  370. bool ComponentPeer::handleFileDragExit (const StringArray& files)
  371. {
  372. const bool used = handleFileDragMove (files, Point<int> (-1, -1));
  373. jassert (dragAndDropTargetComponent == nullptr);
  374. lastDragAndDropCompUnderMouse = nullptr;
  375. return used;
  376. }
  377. // We'll use an async message to deliver the drop, because if the target decides
  378. // to run a modal loop, it can gum-up the operating system..
  379. class AsyncFileDropMessage : public CallbackMessage
  380. {
  381. public:
  382. AsyncFileDropMessage (Component* target_, FileDragAndDropTarget* dropTarget_,
  383. const Point<int>& position_, const StringArray& files_)
  384. : target (target_), dropTarget (dropTarget_), position (position_), files (files_)
  385. {
  386. }
  387. void messageCallback()
  388. {
  389. if (target.get() != nullptr)
  390. dropTarget->filesDropped (files, position.getX(), position.getY());
  391. }
  392. private:
  393. WeakReference<Component> target;
  394. FileDragAndDropTarget* const dropTarget;
  395. const Point<int> position;
  396. const StringArray files;
  397. JUCE_DECLARE_NON_COPYABLE (AsyncFileDropMessage);
  398. };
  399. bool ComponentPeer::handleFileDragDrop (const StringArray& files, const Point<int>& position)
  400. {
  401. handleFileDragMove (files, position);
  402. if (dragAndDropTargetComponent != nullptr)
  403. {
  404. FileDragAndDropTarget* const target
  405. = dynamic_cast<FileDragAndDropTarget*> (static_cast<Component*> (dragAndDropTargetComponent));
  406. dragAndDropTargetComponent = nullptr;
  407. lastDragAndDropCompUnderMouse = nullptr;
  408. if (target != nullptr)
  409. {
  410. Component* const targetComp = dynamic_cast <Component*> (target);
  411. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  412. {
  413. targetComp->internalModalInputAttempt();
  414. if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
  415. return true;
  416. }
  417. (new AsyncFileDropMessage (targetComp, target, targetComp->getLocalPoint (component, position), files))->post();
  418. return true;
  419. }
  420. }
  421. return false;
  422. }
  423. //==============================================================================
  424. void ComponentPeer::handleUserClosingWindow()
  425. {
  426. updateCurrentModifiers();
  427. component->userTriedToCloseWindow();
  428. }
  429. //==============================================================================
  430. void ComponentPeer::clearMaskedRegion()
  431. {
  432. maskedRegion.clear();
  433. }
  434. void ComponentPeer::addMaskedRegion (const Rectangle<int>& area)
  435. {
  436. maskedRegion.add (area);
  437. }
  438. //==============================================================================
  439. StringArray ComponentPeer::getAvailableRenderingEngines()
  440. {
  441. return StringArray ("Software Renderer");
  442. }
  443. int ComponentPeer::getCurrentRenderingEngine() const
  444. {
  445. return 0;
  446. }
  447. void ComponentPeer::setCurrentRenderingEngine (int /*index*/)
  448. {
  449. }
  450. END_JUCE_NAMESPACE