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.

460 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 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 "../windows/juce_ComponentPeer.h"
  22. #include "../juce_Desktop.h"
  23. #include "../../../events/juce_Timer.h"
  24. #include "../../../maths/juce_Random.h"
  25. #include "../../graphics/imaging/juce_Image.h"
  26. #include "../mouse/juce_MouseEvent.h"
  27. #include "../mouse/juce_MouseInputSource.h"
  28. #include "juce_FileDragAndDropTarget.h"
  29. bool juce_performDragDropFiles (const StringArray& files, const bool copyFiles, bool& shouldStop);
  30. bool juce_performDragDropText (const String& text, bool& shouldStop);
  31. //==============================================================================
  32. class DragImageComponent : public Component,
  33. public Timer
  34. {
  35. public:
  36. DragImageComponent (const Image& im,
  37. const String& desc,
  38. Component* const sourceComponent,
  39. Component* const mouseDragSource_,
  40. DragAndDropContainer* const o,
  41. const Point<int>& imageOffset_)
  42. : image (im),
  43. source (sourceComponent),
  44. mouseDragSource (mouseDragSource_),
  45. owner (o),
  46. dragDesc (desc),
  47. imageOffset (imageOffset_),
  48. hasCheckedForExternalDrag (false),
  49. drawImage (true)
  50. {
  51. setSize (im.getWidth(), im.getHeight());
  52. if (mouseDragSource == 0)
  53. mouseDragSource = source;
  54. mouseDragSource->addMouseListener (this, false);
  55. startTimer (200);
  56. setInterceptsMouseClicks (false, false);
  57. setAlwaysOnTop (true);
  58. }
  59. ~DragImageComponent()
  60. {
  61. if (owner->dragImageComponent == this)
  62. owner->dragImageComponent.release();
  63. if (mouseDragSource != 0)
  64. {
  65. mouseDragSource->removeMouseListener (this);
  66. if (getCurrentlyOver() != 0 && getCurrentlyOver()->isInterestedInDragSource (dragDesc, source))
  67. getCurrentlyOver()->itemDragExit (dragDesc, source);
  68. }
  69. }
  70. void paint (Graphics& g)
  71. {
  72. if (isOpaque())
  73. g.fillAll (Colours::white);
  74. if (drawImage)
  75. {
  76. g.setOpacity (1.0f);
  77. g.drawImageAt (image, 0, 0);
  78. }
  79. }
  80. DragAndDropTarget* findTarget (const Point<int>& screenPos, Point<int>& relativePos)
  81. {
  82. Component* hit = getParentComponent();
  83. if (hit == 0)
  84. {
  85. hit = Desktop::getInstance().findComponentAt (screenPos);
  86. }
  87. else
  88. {
  89. const Point<int> relPos (hit->getLocalPoint (0, screenPos));
  90. hit = hit->getComponentAt (relPos.getX(), relPos.getY());
  91. }
  92. // (note: use a local copy of the dragDesc member in case the callback runs
  93. // a modal loop and deletes this object before the method completes)
  94. const String dragDescLocal (dragDesc);
  95. while (hit != 0)
  96. {
  97. DragAndDropTarget* const ddt = dynamic_cast <DragAndDropTarget*> (hit);
  98. if (ddt != 0 && ddt->isInterestedInDragSource (dragDescLocal, source))
  99. {
  100. relativePos = hit->getLocalPoint (0, screenPos);
  101. return ddt;
  102. }
  103. hit = hit->getParentComponent();
  104. }
  105. return 0;
  106. }
  107. void mouseUp (const MouseEvent& e)
  108. {
  109. if (e.originalComponent != this)
  110. {
  111. if (mouseDragSource != 0)
  112. mouseDragSource->removeMouseListener (this);
  113. bool dropAccepted = false;
  114. DragAndDropTarget* ddt = 0;
  115. Point<int> relPos;
  116. if (isVisible())
  117. {
  118. setVisible (false);
  119. ddt = findTarget (e.getScreenPosition(), relPos);
  120. // fade this component and remove it - it'll be deleted later by the timer callback
  121. dropAccepted = ddt != 0;
  122. setVisible (true);
  123. if (dropAccepted || source == 0)
  124. {
  125. Desktop::getInstance().getAnimator().fadeOut (this, 120);
  126. }
  127. else
  128. {
  129. const Point<int> target (source->localPointToGlobal (source->getLocalBounds().getCentre()));
  130. const Point<int> ourCentre (localPointToGlobal (getLocalBounds().getCentre()));
  131. Desktop::getInstance().getAnimator().animateComponent (this,
  132. getBounds() + (target - ourCentre),
  133. 0.0f, 120,
  134. true, 1.0, 1.0);
  135. }
  136. }
  137. if (getParentComponent() != 0)
  138. getParentComponent()->removeChildComponent (this);
  139. if (dropAccepted && ddt != 0)
  140. {
  141. // (note: use a local copy of the dragDesc member in case the callback runs
  142. // a modal loop and deletes this object before the method completes)
  143. const String dragDescLocal (dragDesc);
  144. currentlyOverComp = 0;
  145. ddt->itemDropped (dragDescLocal, source, relPos.getX(), relPos.getY());
  146. }
  147. // careful - this object could now be deleted..
  148. }
  149. }
  150. void updateLocation (const bool canDoExternalDrag, const Point<int>& screenPos)
  151. {
  152. // (note: use a local copy of the dragDesc member in case the callback runs
  153. // a modal loop and deletes this object before it returns)
  154. const String dragDescLocal (dragDesc);
  155. Point<int> newPos (screenPos + imageOffset);
  156. if (getParentComponent() != 0)
  157. newPos = getParentComponent()->getLocalPoint (0, newPos);
  158. //if (newX != getX() || newY != getY())
  159. {
  160. setTopLeftPosition (newPos.getX(), newPos.getY());
  161. Point<int> relPos;
  162. DragAndDropTarget* const ddt = findTarget (screenPos, relPos);
  163. Component* ddtComp = dynamic_cast <Component*> (ddt);
  164. drawImage = (ddt == 0) || ddt->shouldDrawDragImageWhenOver();
  165. if (ddtComp != currentlyOverComp)
  166. {
  167. if (currentlyOverComp != 0 && source != 0
  168. && getCurrentlyOver()->isInterestedInDragSource (dragDescLocal, source))
  169. {
  170. getCurrentlyOver()->itemDragExit (dragDescLocal, source);
  171. }
  172. currentlyOverComp = ddtComp;
  173. if (ddt != 0 && ddt->isInterestedInDragSource (dragDescLocal, source))
  174. ddt->itemDragEnter (dragDescLocal, source, relPos.getX(), relPos.getY());
  175. }
  176. DragAndDropTarget* target = getCurrentlyOver();
  177. if (target != 0 && target->isInterestedInDragSource (dragDescLocal, source))
  178. target->itemDragMove (dragDescLocal, source, relPos.getX(), relPos.getY());
  179. if (getCurrentlyOver() == 0 && canDoExternalDrag && ! hasCheckedForExternalDrag)
  180. {
  181. if (Desktop::getInstance().findComponentAt (screenPos) == 0)
  182. {
  183. hasCheckedForExternalDrag = true;
  184. StringArray files;
  185. bool canMoveFiles = false;
  186. if (owner->shouldDropFilesWhenDraggedExternally (dragDescLocal, source, files, canMoveFiles)
  187. && files.size() > 0)
  188. {
  189. WeakReference<Component> cdw (this);
  190. setVisible (false);
  191. if (ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown())
  192. DragAndDropContainer::performExternalDragDropOfFiles (files, canMoveFiles);
  193. if (cdw != 0)
  194. delete this;
  195. return;
  196. }
  197. }
  198. }
  199. }
  200. }
  201. void mouseDrag (const MouseEvent& e)
  202. {
  203. if (e.originalComponent != this)
  204. updateLocation (true, e.getScreenPosition());
  205. }
  206. void timerCallback()
  207. {
  208. if (source == 0)
  209. {
  210. delete this;
  211. }
  212. else if (! isMouseButtonDownAnywhere())
  213. {
  214. if (mouseDragSource != 0)
  215. mouseDragSource->removeMouseListener (this);
  216. delete this;
  217. }
  218. }
  219. private:
  220. Image image;
  221. WeakReference<Component> source;
  222. WeakReference<Component> mouseDragSource;
  223. DragAndDropContainer* const owner;
  224. WeakReference<Component> currentlyOverComp;
  225. DragAndDropTarget* getCurrentlyOver()
  226. {
  227. return dynamic_cast <DragAndDropTarget*> (currentlyOverComp.get());
  228. }
  229. String dragDesc;
  230. const Point<int> imageOffset;
  231. bool hasCheckedForExternalDrag, drawImage;
  232. JUCE_DECLARE_NON_COPYABLE (DragImageComponent);
  233. };
  234. //==============================================================================
  235. DragAndDropContainer::DragAndDropContainer()
  236. {
  237. }
  238. DragAndDropContainer::~DragAndDropContainer()
  239. {
  240. dragImageComponent = 0;
  241. }
  242. void DragAndDropContainer::startDragging (const String& sourceDescription,
  243. Component* sourceComponent,
  244. const Image& dragImage_,
  245. const bool allowDraggingToExternalWindows,
  246. const Point<int>* imageOffsetFromMouse)
  247. {
  248. Image dragImage (dragImage_);
  249. if (dragImageComponent == 0)
  250. {
  251. Component* const thisComp = dynamic_cast <Component*> (this);
  252. if (thisComp == 0)
  253. {
  254. jassertfalse; // Your DragAndDropContainer needs to be a Component!
  255. return;
  256. }
  257. MouseInputSource* draggingSource = Desktop::getInstance().getDraggingMouseSource (0);
  258. if (draggingSource == 0 || ! draggingSource->isDragging())
  259. {
  260. jassertfalse; // You must call startDragging() from within a mouseDown or mouseDrag callback!
  261. return;
  262. }
  263. const Point<int> lastMouseDown (Desktop::getLastMouseDownPosition());
  264. Point<int> imageOffset;
  265. if (dragImage.isNull())
  266. {
  267. dragImage = sourceComponent->createComponentSnapshot (sourceComponent->getLocalBounds())
  268. .convertedToFormat (Image::ARGB);
  269. dragImage.multiplyAllAlphas (0.6f);
  270. const int lo = 150;
  271. const int hi = 400;
  272. Point<int> relPos (sourceComponent->getLocalPoint (0, lastMouseDown));
  273. Point<int> clipped (dragImage.getBounds().getConstrainedPoint (relPos));
  274. for (int y = dragImage.getHeight(); --y >= 0;)
  275. {
  276. const double dy = (y - clipped.getY()) * (y - clipped.getY());
  277. for (int x = dragImage.getWidth(); --x >= 0;)
  278. {
  279. const int dx = x - clipped.getX();
  280. const int distance = roundToInt (std::sqrt (dx * dx + dy));
  281. if (distance > lo)
  282. {
  283. const float alpha = (distance > hi) ? 0
  284. : (hi - distance) / (float) (hi - lo)
  285. + Random::getSystemRandom().nextFloat() * 0.008f;
  286. dragImage.multiplyAlphaAt (x, y, alpha);
  287. }
  288. }
  289. }
  290. imageOffset = -clipped;
  291. }
  292. else
  293. {
  294. if (imageOffsetFromMouse == 0)
  295. imageOffset = -dragImage.getBounds().getCentre();
  296. else
  297. imageOffset = -(dragImage.getBounds().getConstrainedPoint (-*imageOffsetFromMouse));
  298. }
  299. dragImageComponent = new DragImageComponent (dragImage, sourceDescription, sourceComponent,
  300. draggingSource->getComponentUnderMouse(), this, imageOffset);
  301. currentDragDesc = sourceDescription;
  302. if (allowDraggingToExternalWindows)
  303. {
  304. if (! Desktop::canUseSemiTransparentWindows())
  305. dragImageComponent->setOpaque (true);
  306. dragImageComponent->addToDesktop (ComponentPeer::windowIgnoresMouseClicks
  307. | ComponentPeer::windowIsTemporary
  308. | ComponentPeer::windowIgnoresKeyPresses);
  309. }
  310. else
  311. thisComp->addChildComponent (dragImageComponent);
  312. static_cast <DragImageComponent*> (static_cast <Component*> (dragImageComponent))->updateLocation (false, lastMouseDown);
  313. dragImageComponent->setVisible (true);
  314. }
  315. }
  316. bool DragAndDropContainer::isDragAndDropActive() const
  317. {
  318. return dragImageComponent != 0;
  319. }
  320. const String DragAndDropContainer::getCurrentDragDescription() const
  321. {
  322. return (dragImageComponent != 0) ? currentDragDesc
  323. : String::empty;
  324. }
  325. DragAndDropContainer* DragAndDropContainer::findParentDragContainerFor (Component* c)
  326. {
  327. return c == 0 ? 0 : c->findParentComponentOfClass ((DragAndDropContainer*) 0);
  328. }
  329. bool DragAndDropContainer::shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&)
  330. {
  331. return false;
  332. }
  333. //==============================================================================
  334. void DragAndDropTarget::itemDragEnter (const String&, Component*, int, int)
  335. {
  336. }
  337. void DragAndDropTarget::itemDragMove (const String&, Component*, int, int)
  338. {
  339. }
  340. void DragAndDropTarget::itemDragExit (const String&, Component*)
  341. {
  342. }
  343. bool DragAndDropTarget::shouldDrawDragImageWhenOver()
  344. {
  345. return true;
  346. }
  347. //==============================================================================
  348. void FileDragAndDropTarget::fileDragEnter (const StringArray&, int, int)
  349. {
  350. }
  351. void FileDragAndDropTarget::fileDragMove (const StringArray&, int, int)
  352. {
  353. }
  354. void FileDragAndDropTarget::fileDragExit (const StringArray&)
  355. {
  356. }
  357. END_JUCE_NAMESPACE