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.

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