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.

351 lines
12KB

  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_COMPONENTEDITORCANVAS_H_37C33B56__
  19. #define __JUCER_COMPONENTEDITORCANVAS_H_37C33B56__
  20. //==============================================================================
  21. class ComponentEditorCanvas : public EditorCanvasBase,
  22. public Timer
  23. {
  24. public:
  25. //==============================================================================
  26. ComponentEditorCanvas (ComponentEditor& editor_)
  27. : editor (editor_)
  28. {
  29. initialise();
  30. getDocument().getRoot().addListener (this);
  31. }
  32. ~ComponentEditorCanvas()
  33. {
  34. getDocument().getRoot().removeListener (this);
  35. shutdown();
  36. }
  37. ComponentEditor& getEditor() { return editor; }
  38. ComponentDocument& getDocument() { return editor.getDocument(); }
  39. void timerCallback()
  40. {
  41. stopTimer();
  42. if (! Component::isMouseButtonDownAnywhere())
  43. getDocument().beginNewTransaction();
  44. }
  45. Component* createComponentHolder()
  46. {
  47. return new ComponentHolder (getDocument().getBackgroundColour());
  48. }
  49. void documentChanged()
  50. {
  51. getDocument().updateComponentsIn (getComponentHolder());
  52. startTimer (500);
  53. }
  54. const Rectangle<int> getCanvasBounds()
  55. {
  56. return Rectangle<int> (0, 0, getDocument().getCanvasWidth().getValue(),
  57. getDocument().getCanvasHeight().getValue());
  58. }
  59. void setCanvasBounds (const Rectangle<int>& newBounds)
  60. {
  61. jassert (newBounds.getPosition().isOrigin());
  62. getDocument().getCanvasWidth() = newBounds.getWidth();
  63. getDocument().getCanvasHeight() = newBounds.getHeight();
  64. }
  65. bool canResizeCanvas() const { return true; }
  66. ComponentDocument::MarkerList& getMarkerList (bool isX)
  67. {
  68. return getDocument().getMarkerList (isX);
  69. }
  70. double limitMarkerPosition (double pos)
  71. {
  72. return jmax (0.0, pos);
  73. }
  74. const SelectedItems::ItemType findObjectIdAt (const Point<int>& position)
  75. {
  76. for (int i = getComponentHolder()->getNumChildComponents(); --i >= 0;)
  77. {
  78. Component* const c = getComponentHolder()->getChildComponent(i);
  79. if (c->getBounds().contains (position))
  80. return ComponentDocument::getJucerIDFor (c);
  81. }
  82. return String::empty;
  83. }
  84. void showPopupMenu (bool isClickOnSelectedObject)
  85. {
  86. if (isClickOnSelectedObject)
  87. {
  88. PopupMenu m;
  89. m.addCommandItem (commandManager, CommandIDs::toFront);
  90. m.addCommandItem (commandManager, CommandIDs::toBack);
  91. m.addSeparator();
  92. m.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  93. const int r = m.show();
  94. (void) r;
  95. }
  96. else
  97. {
  98. editor.showNewComponentMenu (0);
  99. }
  100. }
  101. void objectDoubleClicked (const MouseEvent& e, const ValueTree& state)
  102. {
  103. getDocument().componentDoubleClicked (e, state);
  104. }
  105. const ValueTree getObjectState (const String& objectId)
  106. {
  107. return getDocument().getComponentWithID (objectId);
  108. }
  109. const Rectangle<int> getObjectPosition (const ValueTree& state)
  110. {
  111. return getDocument().getCoordsFor (state).resolve (&getDocument()).getSmallestIntegerContainer();
  112. }
  113. bool hasSizeGuides() const { return true; }
  114. RelativeRectangle getObjectCoords (const ValueTree& state)
  115. {
  116. return getDocument().getCoordsFor (state);
  117. }
  118. void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray<OverlayItemComponent>& existingComps)
  119. {
  120. }
  121. SelectedItems& getSelection()
  122. {
  123. return editor.getSelection();
  124. }
  125. void deselectNonDraggableObjects()
  126. {
  127. editor.deselectNonComponents();
  128. }
  129. void findLassoItemsInArea (Array <SelectedItems::ItemType>& itemsFound, const Rectangle<int>& area)
  130. {
  131. for (int i = getComponentHolder()->getNumChildComponents(); --i >= 0;)
  132. {
  133. Component* c = getComponentHolder()->getChildComponent(i);
  134. if (c->getBounds().intersects (area))
  135. itemsFound.add (ComponentDocument::getJucerIDFor (c));
  136. }
  137. }
  138. //==============================================================================
  139. class DragOperation : public EditorDragOperation
  140. {
  141. public:
  142. DragOperation (ComponentEditorCanvas* canvas_,
  143. const MouseEvent& e, const Point<int>& mousePos,
  144. Component* snapGuideParentComp_,
  145. const ResizableBorderComponent::Zone& zone_)
  146. : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_)
  147. {
  148. }
  149. ~DragOperation()
  150. {
  151. getUndoManager().beginNewTransaction();
  152. }
  153. protected:
  154. ComponentDocument& getDocument() throw() { return static_cast <ComponentEditorCanvas*> (canvas)->getDocument(); }
  155. void getSnapPointsX (Array<float>& points, bool includeCentre)
  156. {
  157. const float width = getDocument().getCanvasWidth().getValue();
  158. points.add (0.0f);
  159. points.add (width);
  160. if (includeCentre)
  161. points.add (width / 2.0f);
  162. }
  163. void getSnapPointsY (Array<float>& points, bool includeCentre)
  164. {
  165. const float height = getDocument().getCanvasHeight().getValue();
  166. points.add (0.0f);
  167. points.add (height);
  168. if (includeCentre)
  169. points.add (height / 2.0f);
  170. }
  171. int getCanvasWidth() { return getDocument().getCanvasWidth().getValue(); }
  172. int getCanvasHeight() { return getDocument().getCanvasHeight().getValue(); }
  173. UndoManager& getUndoManager() { return *getDocument().getUndoManager(); }
  174. void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps)
  175. {
  176. ComponentDocument& doc = getDocument();
  177. RelativeRectangle pr (doc.getCoordsFor (state));
  178. StringArray anchors;
  179. anchors.addIfNotAlreadyThere (pr.left.getAnchorName1());
  180. anchors.addIfNotAlreadyThere (pr.left.getAnchorName2());
  181. anchors.addIfNotAlreadyThere (pr.top.getAnchorName1());
  182. anchors.addIfNotAlreadyThere (pr.top.getAnchorName2());
  183. anchors.addIfNotAlreadyThere (pr.right.getAnchorName1());
  184. anchors.addIfNotAlreadyThere (pr.right.getAnchorName2());
  185. anchors.addIfNotAlreadyThere (pr.bottom.getAnchorName1());
  186. anchors.addIfNotAlreadyThere (pr.bottom.getAnchorName2());
  187. for (int i = 0; i < anchors.size(); ++i)
  188. {
  189. const String anchor (anchors[i]);
  190. if (anchor.isNotEmpty() && ! anchor.startsWith ("parent."))
  191. {
  192. const ValueTree v (doc.getComponentWithMemberName (anchor.upToFirstOccurrenceOf (".", false, false)));
  193. if (v.isValid())
  194. deps.add (v);
  195. }
  196. }
  197. }
  198. const Rectangle<float> getObjectPosition (const ValueTree& state)
  199. {
  200. ComponentDocument& doc = getDocument();
  201. return doc.getCoordsFor (state).resolve (&doc);
  202. }
  203. void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds)
  204. {
  205. ComponentDocument& doc = getDocument();
  206. RelativeRectangle pr (doc.getCoordsFor (state));
  207. pr.moveToAbsolute (newBounds, &doc);
  208. doc.setCoordsFor (state, pr);
  209. }
  210. float getMarkerPosition (const ValueTree& marker, bool isX)
  211. {
  212. ComponentDocument& doc = getDocument();
  213. return (float) doc.getMarkerList (isX).getCoordinate (marker).resolve (&doc);
  214. }
  215. };
  216. DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent,
  217. const ResizableBorderComponent::Zone& zone)
  218. {
  219. DragOperation* d = new DragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone);
  220. Array<ValueTree> selected, unselected;
  221. for (int i = getDocument().getNumComponents(); --i >= 0;)
  222. {
  223. const ValueTree v (getDocument().getComponent (i));
  224. if (editor.getSelection().isSelected (v [ComponentDocument::idProperty]))
  225. selected.add (v);
  226. else
  227. unselected.add (v);
  228. }
  229. d->initialise (selected, unselected);
  230. return d;
  231. }
  232. UndoManager& getUndoManager()
  233. {
  234. return *getDocument().getUndoManager();
  235. }
  236. private:
  237. //==============================================================================
  238. ComponentEditor& editor;
  239. class ComponentHolder : public Component,
  240. public Value::Listener
  241. {
  242. public:
  243. ComponentHolder (const Value& backgroundColour_)
  244. : backgroundColour (backgroundColour_)
  245. {
  246. setOpaque (true);
  247. updateColour();
  248. backgroundColour.addListener (this);
  249. }
  250. ~ComponentHolder()
  251. {
  252. deleteAllChildren();
  253. }
  254. void updateColour()
  255. {
  256. Colour newColour (Colours::white);
  257. if (backgroundColour.toString().isNotEmpty())
  258. newColour = Colour::fromString (backgroundColour.toString());
  259. if (newColour != colour)
  260. {
  261. colour = newColour;
  262. repaint();
  263. }
  264. }
  265. void paint (Graphics& g)
  266. {
  267. if (colour.isOpaque())
  268. g.fillAll (colour);
  269. else
  270. g.fillCheckerBoard (0, 0, getWidth(), getHeight(), 24, 24,
  271. Colour (0xffeeeeee).overlaidWith (colour),
  272. Colour (0xffffffff).overlaidWith (colour));
  273. }
  274. void valueChanged (Value&)
  275. {
  276. updateColour();
  277. }
  278. private:
  279. Value backgroundColour;
  280. Colour colour;
  281. };
  282. };
  283. #endif // __JUCER_COMPONENTEDITORCANVAS_H_37C33B56__