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.

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