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.

511 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_DragAndDropContainer.h"
  21. #include "../juce_ComponentDeletionWatcher.h"
  22. #include "../juce_Desktop.h"
  23. #include "../../../events/juce_Timer.h"
  24. #include "../../../core/juce_Random.h"
  25. #include "../../graphics/imaging/juce_Image.h"
  26. #include "juce_FileDragAndDropTarget.h"
  27. bool juce_performDragDropFiles (const StringArray& files, const bool copyFiles, bool& shouldStop);
  28. bool juce_performDragDropText (const String& text, bool& shouldStop);
  29. //==============================================================================
  30. class DragImageComponent : public Component,
  31. public Timer
  32. {
  33. private:
  34. ScopedPointer <Image> image;
  35. Component* const source;
  36. DragAndDropContainer* const owner;
  37. ScopedPointer <ComponentDeletionWatcher> sourceWatcher, mouseDragSourceWatcher, currentlyOverWatcher;
  38. Component* mouseDragSource;
  39. DragAndDropTarget* currentlyOver;
  40. String dragDesc;
  41. const int imageX, imageY;
  42. bool hasCheckedForExternalDrag, drawImage;
  43. DragImageComponent (const DragImageComponent&);
  44. const DragImageComponent& operator= (const DragImageComponent&);
  45. public:
  46. DragImageComponent (Image* const im,
  47. const String& desc,
  48. Component* const s,
  49. DragAndDropContainer* const o,
  50. const int imageX_, const int imageY_)
  51. : image (im),
  52. source (s),
  53. owner (o),
  54. currentlyOver (0),
  55. dragDesc (desc),
  56. imageX (imageX_),
  57. imageY (imageY_),
  58. hasCheckedForExternalDrag (false),
  59. drawImage (true)
  60. {
  61. setSize (im->getWidth(), im->getHeight());
  62. sourceWatcher = new ComponentDeletionWatcher (source);
  63. mouseDragSource = Component::getComponentUnderMouse();
  64. if (mouseDragSource == 0)
  65. mouseDragSource = source;
  66. mouseDragSourceWatcher = new ComponentDeletionWatcher (mouseDragSource);
  67. mouseDragSource->addMouseListener (this, false);
  68. startTimer (200);
  69. setInterceptsMouseClicks (false, false);
  70. setAlwaysOnTop (true);
  71. }
  72. ~DragImageComponent()
  73. {
  74. if ((DragImageComponent*) owner->dragImageComponent == this)
  75. owner->dragImageComponent.release();
  76. if (! mouseDragSourceWatcher->hasBeenDeleted())
  77. {
  78. mouseDragSource->removeMouseListener (this);
  79. if (currentlyOverWatcher != 0 && ! currentlyOverWatcher->hasBeenDeleted())
  80. if (currentlyOver->isInterestedInDragSource (dragDesc, source))
  81. currentlyOver->itemDragExit (dragDesc, source);
  82. }
  83. }
  84. void paint (Graphics& g)
  85. {
  86. if (isOpaque())
  87. g.fillAll (Colours::white);
  88. if (drawImage)
  89. {
  90. g.setOpacity (1.0f);
  91. g.drawImageAt (image, 0, 0);
  92. }
  93. }
  94. DragAndDropTarget* findTarget (const int screenX, const int screenY,
  95. int& relX, int& relY) const
  96. {
  97. Component* hit = getParentComponent();
  98. if (hit == 0)
  99. {
  100. hit = Desktop::getInstance().findComponentAt (screenX, screenY);
  101. }
  102. else
  103. {
  104. int rx = screenX, ry = screenY;
  105. hit->globalPositionToRelative (rx, ry);
  106. hit = hit->getComponentAt (rx, ry);
  107. }
  108. // (note: use a local copy of the dragDesc member in case the callback runs
  109. // a modal loop and deletes this object before the method completes)
  110. const String dragDescLocal (dragDesc);
  111. while (hit != 0)
  112. {
  113. DragAndDropTarget* const ddt = dynamic_cast <DragAndDropTarget*> (hit);
  114. if (ddt != 0 && ddt->isInterestedInDragSource (dragDescLocal, source))
  115. {
  116. relX = screenX;
  117. relY = screenY;
  118. hit->globalPositionToRelative (relX, relY);
  119. return ddt;
  120. }
  121. hit = hit->getParentComponent();
  122. }
  123. return 0;
  124. }
  125. void mouseUp (const MouseEvent& e)
  126. {
  127. if (e.originalComponent != this)
  128. {
  129. if (! mouseDragSourceWatcher->hasBeenDeleted())
  130. mouseDragSource->removeMouseListener (this);
  131. bool dropAccepted = false;
  132. DragAndDropTarget* ddt = 0;
  133. int relX = 0, relY = 0;
  134. if (isVisible())
  135. {
  136. setVisible (false);
  137. ddt = findTarget (e.getScreenX(),
  138. e.getScreenY(),
  139. relX, relY);
  140. // fade this component and remove it - it'll be deleted later by the timer callback
  141. dropAccepted = ddt != 0;
  142. setVisible (true);
  143. if (dropAccepted || sourceWatcher->hasBeenDeleted())
  144. {
  145. fadeOutComponent (120);
  146. }
  147. else
  148. {
  149. int targetX = source->getWidth() / 2;
  150. int targetY = source->getHeight() / 2;
  151. source->relativePositionToGlobal (targetX, targetY);
  152. int ourCentreX = getWidth() / 2;
  153. int ourCentreY = getHeight() / 2;
  154. relativePositionToGlobal (ourCentreX, ourCentreY);
  155. fadeOutComponent (120,
  156. targetX - ourCentreX,
  157. targetY - ourCentreY);
  158. }
  159. }
  160. if (getParentComponent() != 0)
  161. getParentComponent()->removeChildComponent (this);
  162. if (dropAccepted && ddt != 0)
  163. {
  164. // (note: use a local copy of the dragDesc member in case the callback runs
  165. // a modal loop and deletes this object before the method completes)
  166. const String dragDescLocal (dragDesc);
  167. currentlyOverWatcher = 0;
  168. currentlyOver = 0;
  169. ddt->itemDropped (dragDescLocal, source, relX, relY);
  170. }
  171. // careful - this object could now be deleted..
  172. }
  173. }
  174. void updateLocation (const bool canDoExternalDrag, int x, int y)
  175. {
  176. // (note: use a local copy of the dragDesc member in case the callback runs
  177. // a modal loop and deletes this object before it returns)
  178. const String dragDescLocal (dragDesc);
  179. int newX = x + imageX;
  180. int newY = y + imageY;
  181. if (getParentComponent() != 0)
  182. getParentComponent()->globalPositionToRelative (newX, newY);
  183. //if (newX != getX() || newY != getY())
  184. {
  185. setTopLeftPosition (newX, newY);
  186. int relX = 0, relY = 0;
  187. DragAndDropTarget* const ddt = findTarget (x, y, relX, relY);
  188. drawImage = (ddt == 0) || ddt->shouldDrawDragImageWhenOver();
  189. if (ddt != currentlyOver)
  190. {
  191. if (currentlyOverWatcher != 0 && ! currentlyOverWatcher->hasBeenDeleted())
  192. {
  193. Component* const over = dynamic_cast <Component*> (currentlyOver);
  194. if (over != 0
  195. && over->isValidComponent()
  196. && ! (sourceWatcher->hasBeenDeleted())
  197. && currentlyOver->isInterestedInDragSource (dragDescLocal, source))
  198. {
  199. currentlyOver->itemDragExit (dragDescLocal, source);
  200. }
  201. }
  202. currentlyOver = ddt;
  203. currentlyOverWatcher = 0;
  204. if (ddt != 0)
  205. {
  206. currentlyOverWatcher = new ComponentDeletionWatcher (dynamic_cast <Component*> (ddt));
  207. if (currentlyOver->isInterestedInDragSource (dragDescLocal, source))
  208. currentlyOver->itemDragEnter (dragDescLocal, source, relX, relY);
  209. }
  210. }
  211. else if (currentlyOverWatcher != 0 && currentlyOverWatcher->hasBeenDeleted())
  212. {
  213. currentlyOver = 0;
  214. currentlyOverWatcher = 0;
  215. }
  216. if (currentlyOver != 0
  217. && currentlyOver->isInterestedInDragSource (dragDescLocal, source))
  218. currentlyOver->itemDragMove (dragDescLocal, source, relX, relY);
  219. if (currentlyOver == 0
  220. && canDoExternalDrag
  221. && ! hasCheckedForExternalDrag)
  222. {
  223. if (Desktop::getInstance().findComponentAt (x, y) == 0)
  224. {
  225. hasCheckedForExternalDrag = true;
  226. StringArray files;
  227. bool canMoveFiles = false;
  228. if (owner->shouldDropFilesWhenDraggedExternally (dragDescLocal, source, files, canMoveFiles)
  229. && files.size() > 0)
  230. {
  231. ComponentDeletionWatcher cdw (this);
  232. setVisible (false);
  233. if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown())
  234. DragAndDropContainer::performExternalDragDropOfFiles (files, canMoveFiles);
  235. if (! cdw.hasBeenDeleted())
  236. delete this;
  237. return;
  238. }
  239. }
  240. }
  241. }
  242. }
  243. void mouseDrag (const MouseEvent& e)
  244. {
  245. if (e.originalComponent != this)
  246. updateLocation (true, e.getScreenX(), e.getScreenY());
  247. }
  248. void timerCallback()
  249. {
  250. if (sourceWatcher->hasBeenDeleted())
  251. {
  252. delete this;
  253. }
  254. else if (! isMouseButtonDownAnywhere())
  255. {
  256. if (! mouseDragSourceWatcher->hasBeenDeleted())
  257. mouseDragSource->removeMouseListener (this);
  258. delete this;
  259. }
  260. }
  261. };
  262. //==============================================================================
  263. DragAndDropContainer::DragAndDropContainer()
  264. {
  265. }
  266. DragAndDropContainer::~DragAndDropContainer()
  267. {
  268. dragImageComponent = 0;
  269. }
  270. void DragAndDropContainer::startDragging (const String& sourceDescription,
  271. Component* sourceComponent,
  272. Image* dragImage_,
  273. const bool allowDraggingToExternalWindows,
  274. const Point* imageOffsetFromMouse)
  275. {
  276. ScopedPointer <Image> dragImage (dragImage_);
  277. if (dragImageComponent == 0)
  278. {
  279. Component* const thisComp = dynamic_cast <Component*> (this);
  280. if (thisComp != 0)
  281. {
  282. int mx, my;
  283. Desktop::getLastMouseDownPosition (mx, my);
  284. int imageX = 0, imageY = 0;
  285. if (dragImage == 0)
  286. {
  287. dragImage = sourceComponent->createComponentSnapshot (Rectangle (0, 0, sourceComponent->getWidth(), sourceComponent->getHeight()));
  288. if (dragImage->getFormat() != Image::ARGB)
  289. {
  290. Image* newIm = Image::createNativeImage (Image::ARGB, dragImage->getWidth(), dragImage->getHeight(), true);
  291. Graphics g2 (*newIm);
  292. g2.drawImageAt (dragImage, 0, 0);
  293. dragImage = newIm;
  294. }
  295. dragImage->multiplyAllAlphas (0.6f);
  296. const int lo = 150;
  297. const int hi = 400;
  298. int rx = mx, ry = my;
  299. sourceComponent->globalPositionToRelative (rx, ry);
  300. const int cx = jlimit (0, dragImage->getWidth(), rx);
  301. const int cy = jlimit (0, dragImage->getHeight(), ry);
  302. for (int y = dragImage->getHeight(); --y >= 0;)
  303. {
  304. const double dy = (y - cy) * (y - cy);
  305. for (int x = dragImage->getWidth(); --x >= 0;)
  306. {
  307. const int dx = x - cx;
  308. const int distance = roundToInt (sqrt (dx * dx + dy));
  309. if (distance > lo)
  310. {
  311. const float alpha = (distance > hi) ? 0
  312. : (hi - distance) / (float) (hi - lo)
  313. + Random::getSystemRandom().nextFloat() * 0.008f;
  314. dragImage->multiplyAlphaAt (x, y, alpha);
  315. }
  316. }
  317. }
  318. imageX = -cx;
  319. imageY = -cy;
  320. }
  321. else
  322. {
  323. if (imageOffsetFromMouse == 0)
  324. {
  325. imageX = dragImage->getWidth() / -2;
  326. imageY = dragImage->getHeight() / -2;
  327. }
  328. else
  329. {
  330. imageX = (int) imageOffsetFromMouse->getX();
  331. imageY = (int) imageOffsetFromMouse->getY();
  332. }
  333. }
  334. dragImageComponent = new DragImageComponent (dragImage.release(), sourceDescription, sourceComponent,
  335. this, imageX, imageY);
  336. currentDragDesc = sourceDescription;
  337. if (allowDraggingToExternalWindows)
  338. {
  339. if (! Desktop::canUseSemiTransparentWindows())
  340. dragImageComponent->setOpaque (true);
  341. dragImageComponent->addToDesktop (ComponentPeer::windowIgnoresMouseClicks
  342. | ComponentPeer::windowIsTemporary
  343. | ComponentPeer::windowIgnoresKeyPresses);
  344. }
  345. else
  346. thisComp->addChildComponent (dragImageComponent);
  347. ((DragImageComponent*) dragImageComponent)->updateLocation (false, mx, my);
  348. dragImageComponent->setVisible (true);
  349. }
  350. else
  351. {
  352. // this class must only be implemented by an object that
  353. // is also a Component.
  354. jassertfalse
  355. }
  356. }
  357. }
  358. bool DragAndDropContainer::isDragAndDropActive() const
  359. {
  360. return dragImageComponent != 0;
  361. }
  362. const String DragAndDropContainer::getCurrentDragDescription() const
  363. {
  364. return (dragImageComponent != 0) ? currentDragDesc
  365. : String::empty;
  366. }
  367. DragAndDropContainer* DragAndDropContainer::findParentDragContainerFor (Component* c)
  368. {
  369. if (c == 0)
  370. return 0;
  371. // (unable to use the syntax findParentComponentOfClass <DragAndDropContainer> () because of a VC6 compiler bug)
  372. return c->findParentComponentOfClass ((DragAndDropContainer*) 0);
  373. }
  374. bool DragAndDropContainer::shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&)
  375. {
  376. return false;
  377. }
  378. //==============================================================================
  379. void DragAndDropTarget::itemDragEnter (const String&, Component*, int, int)
  380. {
  381. }
  382. void DragAndDropTarget::itemDragMove (const String&, Component*, int, int)
  383. {
  384. }
  385. void DragAndDropTarget::itemDragExit (const String&, Component*)
  386. {
  387. }
  388. bool DragAndDropTarget::shouldDrawDragImageWhenOver()
  389. {
  390. return true;
  391. }
  392. //==============================================================================
  393. void FileDragAndDropTarget::fileDragEnter (const StringArray&, int, int)
  394. {
  395. }
  396. void FileDragAndDropTarget::fileDragMove (const StringArray&, int, int)
  397. {
  398. }
  399. void FileDragAndDropTarget::fileDragExit (const StringArray&)
  400. {
  401. }
  402. END_JUCE_NAMESPACE