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.

339 lines
13KB

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