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.

355 lines
14KB

  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. #ifndef __JUCER_EDITORDRAGOPERATION_H_720CD068__
  19. #define __JUCER_EDITORDRAGOPERATION_H_720CD068__
  20. #include "jucer_EditorCanvas.h"
  21. //==============================================================================
  22. class EditorDragOperation : public EditorCanvasBase::DragOperation
  23. {
  24. public:
  25. EditorDragOperation (EditorCanvasBase* canvas_, const MouseEvent& e,
  26. Component* snapGuideParentComp_,
  27. const ResizableBorderComponent::Zone& zone_)
  28. : canvas (canvas_),
  29. snapGuideParentComp (snapGuideParentComp_),
  30. zone (zone_),
  31. mouseDownPos (e.getPosition())
  32. {
  33. }
  34. ~EditorDragOperation()
  35. {
  36. }
  37. void initialise (const Array<ValueTree>& objects,
  38. const Array<ValueTree>& objectsToSnapTo)
  39. {
  40. draggedObjects = objects;
  41. int i;
  42. for (i = 0; i < objects.size(); ++i)
  43. {
  44. const Rectangle<float> floatPos (getObjectPosition (objects.getReference(i)));
  45. originalPositions.add (floatPos);
  46. if (zone.isDraggingWholeObject() || zone.isDraggingLeftEdge())
  47. verticalSnapPositions.add (SnapLine (floatPos.getX(), floatPos.getY(), floatPos.getBottom()));
  48. if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge()))
  49. verticalSnapPositions.add (SnapLine ((float) (int) floatPos.getCentreX(), floatPos.getY(), floatPos.getBottom()));
  50. if (zone.isDraggingWholeObject() || zone.isDraggingRightEdge())
  51. verticalSnapPositions.add (SnapLine (floatPos.getRight(), floatPos.getY(), floatPos.getBottom()));
  52. if (zone.isDraggingWholeObject() || zone.isDraggingTopEdge())
  53. horizontalSnapPositions.add (SnapLine (floatPos.getY(), floatPos.getX(), floatPos.getRight()));
  54. if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge()))
  55. horizontalSnapPositions.add (SnapLine ((float) (int) floatPos.getCentreY(), floatPos.getX(), floatPos.getRight()));
  56. if (zone.isDraggingWholeObject() || zone.isDraggingBottomEdge())
  57. horizontalSnapPositions.add (SnapLine (floatPos.getBottom(), floatPos.getX(), floatPos.getRight()));
  58. }
  59. if (isDraggingLeftRight())
  60. {
  61. const float y1 = -100.0f, y2 = 10000.0f;
  62. verticalSnapTargets.add (SnapLine (0, y1, y2));
  63. verticalSnapTargets.add (SnapLine ((float) getCanvasWidth(), y1, y2));
  64. if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge()))
  65. verticalSnapTargets.add (SnapLine ((float) getCanvasWidth() / 2.0f, y1, y2));
  66. MarkerListBase& markers = canvas->getMarkerList (true);
  67. for (int i = markers.size(); --i >= 0;)
  68. verticalSnapTargets.add (SnapLine (getMarkerPosition (markers.getMarker(i), true), y1, y2));
  69. }
  70. if (isDraggingUpDown())
  71. {
  72. const float x1 = -100.0f, x2 = 10000.0f;
  73. horizontalSnapTargets.add (SnapLine (0, x1, x2));
  74. horizontalSnapTargets.add (SnapLine ((float) getCanvasHeight(), x1, x2));
  75. if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge()))
  76. horizontalSnapTargets.add (SnapLine ((float) getCanvasHeight() / 2.0f, x1, x2));
  77. MarkerListBase& markers = canvas->getMarkerList (false);
  78. for (int i = markers.size(); --i >= 0;)
  79. horizontalSnapTargets.add (SnapLine (getMarkerPosition (markers.getMarker(i), false), x1, x2));
  80. }
  81. for (i = 0; i < objectsToSnapTo.size(); ++i)
  82. {
  83. const Rectangle<float> floatPos (getObjectPosition (objectsToSnapTo.getReference (i)));
  84. if (isDraggingLeftRight())
  85. {
  86. verticalSnapTargets.add (SnapLine (floatPos.getX(), floatPos.getY(), floatPos.getBottom()));
  87. verticalSnapTargets.add (SnapLine (floatPos.getRight(), floatPos.getY(), floatPos.getBottom()));
  88. }
  89. if (zone.isDraggingWholeObject() || (zone.isDraggingLeftEdge() && zone.isDraggingRightEdge()))
  90. verticalSnapTargets.add (SnapLine ((float) (int) floatPos.getCentreX(), floatPos.getY(), floatPos.getBottom()));
  91. if (isDraggingUpDown())
  92. {
  93. horizontalSnapTargets.add (SnapLine (floatPos.getY(), floatPos.getX(), floatPos.getRight()));
  94. horizontalSnapTargets.add (SnapLine (floatPos.getBottom(), floatPos.getX(), floatPos.getRight()));
  95. }
  96. if (zone.isDraggingWholeObject() || (zone.isDraggingTopEdge() && zone.isDraggingBottomEdge()))
  97. horizontalSnapTargets.add (SnapLine ((float) (int) floatPos.getCentreY(), floatPos.getX(), floatPos.getRight()));
  98. }
  99. mergeSnapLines (verticalSnapTargets);
  100. mergeSnapLines (horizontalSnapTargets);
  101. getUndoManager().beginNewTransaction();
  102. }
  103. //==============================================================================
  104. struct SnapLine
  105. {
  106. SnapLine() : position (0), start (0), end (0) {}
  107. SnapLine (const float position_, const float start_, const float end_)
  108. : position (position_), start (start_), end (end_)
  109. {}
  110. float position, start, end;
  111. };
  112. //==============================================================================
  113. class AlignmentHintComponent : public EditorCanvasBase::OverlayItemComponent
  114. {
  115. public:
  116. AlignmentHintComponent (EditorCanvasBase* canvas_, const SnapLine& line_, bool isVertical_, Component* parent)
  117. : OverlayItemComponent (canvas_), line (line_), isVertical (isVertical_)
  118. {
  119. const int extraEndLength = 5;
  120. setAlwaysOnTop (true);
  121. parent->addAndMakeVisible (this);
  122. if (isVertical)
  123. setBoundsInTargetSpace (Rectangle<int> (roundToInt (line.position), roundToInt (line.start) - extraEndLength,
  124. 1, roundToInt (line.end - line.start) + extraEndLength * 2));
  125. else
  126. setBoundsInTargetSpace (Rectangle<int> (roundToInt (line.start) - extraEndLength, roundToInt (line.position),
  127. roundToInt (line.end - line.start) + extraEndLength * 2, 1));
  128. }
  129. void paint (Graphics& g)
  130. {
  131. g.fillAll (alignmentMarkerColour);
  132. }
  133. private:
  134. const SnapLine line;
  135. const bool isVertical;
  136. AlignmentHintComponent (const AlignmentHintComponent&);
  137. AlignmentHintComponent& operator= (const AlignmentHintComponent&);
  138. };
  139. //==============================================================================
  140. void drag (const MouseEvent& e)
  141. {
  142. getUndoManager().undoCurrentTransactionOnly();
  143. // (can't use getOffsetFromDragStart() because of auto-scrolling)
  144. Point<int> distance (e.getPosition() - mouseDownPos);
  145. if (! isDraggingLeftRight())
  146. distance = distance.withX (0);
  147. if (! isDraggingUpDown())
  148. distance = distance.withY (0);
  149. snapGuides.clear();
  150. if (canvas->getPanel()->isSnappingEnabled() != e.mods.isCommandDown())
  151. {
  152. performSnap (verticalSnapTargets, getVerticalSnapPositions (distance), true, distance);
  153. performSnap (horizontalSnapTargets, getHorizontalSnapPositions (distance), false, distance);
  154. }
  155. for (int n = 50;;)
  156. {
  157. // Need to repeatedly apply the new positions until they all settle down, in case some of
  158. // the coords are relative to each other..
  159. bool anyUpdated = false;
  160. for (int i = 0; i < draggedObjects.size(); ++i)
  161. if (dragItem (draggedObjects.getReference(i), distance, originalPositions.getReference(i)))
  162. anyUpdated = true;
  163. if (! anyUpdated)
  164. break;
  165. if (--n == 0)
  166. {
  167. jassertfalse;
  168. break;
  169. }
  170. }
  171. }
  172. bool dragItem (ValueTree& v, const Point<int>& distance, const Rectangle<float>& originalPos)
  173. {
  174. const Rectangle<float> newBounds (zone.resizeRectangleBy (originalPos, Point<float> ((float) distance.getX(),
  175. (float) distance.getY())));
  176. return setObjectPosition (v, newBounds);
  177. }
  178. protected:
  179. //==============================================================================
  180. virtual int getCanvasWidth() = 0;
  181. virtual int getCanvasHeight() = 0;
  182. virtual float getMarkerPosition (const ValueTree& marker, bool isX) = 0;
  183. virtual const Rectangle<float> getObjectPosition (const ValueTree& state) = 0;
  184. virtual bool setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) = 0;
  185. virtual UndoManager& getUndoManager() = 0;
  186. EditorCanvasBase* canvas;
  187. private:
  188. //==============================================================================
  189. Array <ValueTree> draggedObjects;
  190. Array <Rectangle<float> > originalPositions;
  191. Array <SnapLine> verticalSnapPositions, horizontalSnapPositions;
  192. Array <SnapLine> verticalSnapTargets, horizontalSnapTargets;
  193. const ResizableBorderComponent::Zone zone;
  194. OwnedArray<Component> snapGuides;
  195. Component* snapGuideParentComp;
  196. Point<int> mouseDownPos;
  197. static void mergeSnapLines (Array <SnapLine>& lines)
  198. {
  199. for (int i = lines.size(); --i > 0;)
  200. {
  201. SnapLine& s1 = lines.getReference(i);
  202. for (int j = i; --j >= 0;)
  203. {
  204. SnapLine& s2 = lines.getReference(j);
  205. if (s1.position == s2.position)
  206. {
  207. s2.start = jmin (s1.start, s2.start);
  208. s2.end = jmax (s1.end, s2.end);
  209. lines.remove (i);
  210. }
  211. }
  212. }
  213. }
  214. void performSnap (const Array<SnapLine>& targets, const Array<SnapLine>& sources, bool isVertical, Point<int>& distance)
  215. {
  216. if (targets.size() == 0 || sources.size() == 0)
  217. return;
  218. float best = std::numeric_limits<float>::max();
  219. float absBest = fabsf (best);
  220. Array <SnapLine> lines;
  221. for (int i = 0; i < targets.size(); ++i)
  222. {
  223. const SnapLine& target = targets.getReference(i);
  224. for (int j = 0; j < sources.size(); ++j)
  225. {
  226. const SnapLine& source = sources.getReference(j);
  227. const float diff = target.position - source.position;
  228. const float absDiff = fabsf (diff);
  229. if (absDiff <= absBest)
  230. {
  231. if (absDiff < absBest)
  232. {
  233. absBest = absDiff;
  234. best = diff;
  235. lines.clearQuick();
  236. }
  237. lines.add (SnapLine (target.position, jmin (target.start, source.start), jmax (target.end, source.end)));
  238. }
  239. }
  240. }
  241. jassert (absBest < std::numeric_limits<float>::max());
  242. if (absBest < snapDistance)
  243. {
  244. distance += isVertical ? Point<int> (roundToInt (best), 0) : Point<int> (0, roundToInt (best));
  245. for (int i = lines.size(); --i >= 0;)
  246. snapGuides.add (new AlignmentHintComponent (canvas, lines.getReference(i), isVertical, snapGuideParentComp));
  247. }
  248. }
  249. const Array<SnapLine> getVerticalSnapPositions (const Point<int>& distance) const
  250. {
  251. Array<SnapLine> p (verticalSnapPositions);
  252. for (int i = p.size(); --i >= 0;)
  253. {
  254. SnapLine& s = p.getReference(i);
  255. s.position += distance.getX();
  256. s.start += distance.getY();
  257. s.end += distance.getY();
  258. }
  259. return p;
  260. }
  261. const Array<SnapLine> getHorizontalSnapPositions (const Point<int>& distance) const
  262. {
  263. Array<SnapLine> p (horizontalSnapPositions);
  264. for (int i = p.size(); --i >= 0;)
  265. {
  266. SnapLine& s = p.getReference(i);
  267. s.position += distance.getY();
  268. s.start += distance.getX();
  269. s.end += distance.getX();
  270. }
  271. return p;
  272. }
  273. bool isDraggingLeftRight() const { return zone.isDraggingWholeObject() || zone.isDraggingLeftEdge() || zone.isDraggingRightEdge(); }
  274. bool isDraggingUpDown() const { return zone.isDraggingWholeObject() || zone.isDraggingTopEdge() || zone.isDraggingBottomEdge(); }
  275. EditorDragOperation (const EditorDragOperation&);
  276. EditorDragOperation& operator= (const EditorDragOperation&);
  277. };
  278. #endif // __JUCER_EDITORDRAGOPERATION_H_720CD068__