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.

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