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.

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