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.

469 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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 "../../jucer_Headers.h"
  19. #include "jucer_ComponentEditor.h"
  20. //==============================================================================
  21. class ComponentCanvas : public Component,
  22. public ValueTree::Listener
  23. {
  24. public:
  25. ComponentCanvas (ComponentEditor& editor_)
  26. : editor (editor_)
  27. {
  28. setOpaque (true);
  29. addAndMakeVisible (componentHolder = new Component());
  30. addAndMakeVisible (overlay = new OverlayComponent (*this));
  31. setSize (500, 500);
  32. getDocument().getRoot().addListener (this);
  33. updateComponents();
  34. }
  35. ~ComponentCanvas()
  36. {
  37. getDocument().getRoot().removeListener (this);
  38. deleteAllChildren();
  39. }
  40. void paint (Graphics& g)
  41. {
  42. g.fillAll (Colours::white);
  43. }
  44. void resized()
  45. {
  46. componentHolder->setSize (getWidth(), getHeight());
  47. overlay->setSize (getWidth(), getHeight());
  48. }
  49. void zoom (float newScale, const Point<int>& centre)
  50. {
  51. }
  52. ComponentEditor& getEditor() { return editor; }
  53. ComponentDocument& getDocument() { return editor.getDocument(); }
  54. ComponentDocument::SelectedItems& getSelection() { return selection; }
  55. Component* findComponentFor (const ValueTree& state)
  56. {
  57. ComponentDocument& doc = getDocument();
  58. for (int i = componentHolder->getNumChildComponents(); --i >= 0;)
  59. {
  60. Component* c = componentHolder->getChildComponent (i);
  61. if (doc.isStateForComponent (state, c))
  62. return c;
  63. }
  64. return 0;
  65. }
  66. void updateComponents()
  67. {
  68. ComponentDocument& doc = getDocument();
  69. int i;
  70. for (i = componentHolder->getNumChildComponents(); --i >= 0;)
  71. {
  72. Component* c = componentHolder->getChildComponent (i);
  73. if (! doc.containsComponent (c))
  74. {
  75. selection.deselect (c->getComponentUID());
  76. delete c;
  77. }
  78. }
  79. const int num = doc.getNumComponents();
  80. for (i = 0; i < num; ++i)
  81. {
  82. const ValueTree v (doc.getComponent (i));
  83. Component* c = findComponentFor (v);
  84. if (c == 0)
  85. {
  86. c = doc.createComponent (i);
  87. componentHolder->addAndMakeVisible (c);
  88. }
  89. else
  90. {
  91. doc.updateComponent (c);
  92. }
  93. }
  94. }
  95. void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property)
  96. {
  97. updateComponents();
  98. }
  99. void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged)
  100. {
  101. updateComponents();
  102. }
  103. void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged)
  104. {
  105. }
  106. Component* getComponentHolder() const { return componentHolder; }
  107. const Array<Component*> getSelectedComps() const
  108. {
  109. Array <Component*> comps;
  110. for (int i = 0; i < selection.getNumSelected(); ++i)
  111. {
  112. Component* c = getComponentForUID (selection.getSelectedItem (i));
  113. jassert (c != 0);
  114. if (c != 0)
  115. comps.add (c);
  116. }
  117. return comps;
  118. }
  119. private:
  120. ComponentEditor& editor;
  121. //==============================================================================
  122. class ComponentSelectorFrame : public Component,
  123. public ComponentListener
  124. {
  125. public:
  126. ComponentSelectorFrame (ComponentCanvas& canvas_,
  127. Component* componentToAttachTo)
  128. : canvas (canvas_),
  129. component (componentToAttachTo),
  130. borderThickness (4)
  131. {
  132. componentMovedOrResized (*componentToAttachTo, true, true);
  133. componentToAttachTo->addComponentListener (this);
  134. }
  135. ~ComponentSelectorFrame()
  136. {
  137. if (component != 0)
  138. component->removeComponentListener (this);
  139. }
  140. void paint (Graphics& g)
  141. {
  142. g.setColour (Colours::red.withAlpha (0.1f));
  143. g.drawRect (0, 0, getWidth(), getHeight(), borderThickness);
  144. }
  145. void mouseEnter (const MouseEvent& e)
  146. {
  147. repaint();
  148. updateDragZone (e.getPosition());
  149. }
  150. void mouseExit (const MouseEvent& e)
  151. {
  152. repaint();
  153. updateDragZone (e.getPosition());
  154. }
  155. void mouseMove (const MouseEvent& e)
  156. {
  157. updateDragZone (e.getPosition());
  158. }
  159. void mouseDown (const MouseEvent& e)
  160. {
  161. jassert (component != 0);
  162. if (component != 0)
  163. {
  164. updateDragZone (e.getPosition());
  165. canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, dragZone);
  166. }
  167. }
  168. void mouseDrag (const MouseEvent& e)
  169. {
  170. if (component != 0)
  171. canvas.getDocument().continueDrag (e);
  172. }
  173. void mouseUp (const MouseEvent& e)
  174. {
  175. if (component != 0)
  176. canvas.getDocument().endDrag (e);
  177. updateDragZone (e.getPosition());
  178. }
  179. void componentMovedOrResized (Component&, bool wasMoved, bool wasResized)
  180. {
  181. if (component != 0)
  182. setBounds (component->getBounds().expanded (borderThickness, borderThickness));
  183. }
  184. void resized()
  185. {
  186. }
  187. uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); }
  188. private:
  189. ComponentCanvas& canvas;
  190. Component::SafePointer<Component> component;
  191. int dragZone;
  192. const int borderThickness;
  193. void updateDragZone (const Point<int>& p)
  194. {
  195. int newZone = 0;
  196. Rectangle<int> r (0, 0, getWidth(), getHeight());
  197. r = r.reduced (borderThickness, borderThickness);
  198. if (! r.contains (p))
  199. {
  200. const int bw = jmax (borderThickness, proportionOfWidth (0.1f), jmin (10, proportionOfWidth (0.33f)));
  201. const int bh = jmax (borderThickness, proportionOfHeight (0.1f), jmin (10, proportionOfHeight (0.33f)));
  202. if (p.getX() < bw)
  203. newZone |= ComponentDocument::zoneL;
  204. else if (p.getX() >= getWidth() - bw)
  205. newZone |= ComponentDocument::zoneR;
  206. if (p.getY() < bh)
  207. newZone |= ComponentDocument::zoneT;
  208. else if (p.getY() >= getHeight() - bh)
  209. newZone |= ComponentDocument::zoneB;
  210. }
  211. if (dragZone != newZone)
  212. {
  213. dragZone = newZone;
  214. MouseCursor::StandardCursorType mc = MouseCursor::NormalCursor;
  215. switch (newZone)
  216. {
  217. case (ComponentDocument::zoneL | ComponentDocument::zoneT): mc = MouseCursor::TopLeftCornerResizeCursor; break;
  218. case ComponentDocument::zoneT: mc = MouseCursor::TopEdgeResizeCursor; break;
  219. case (ComponentDocument::zoneR | ComponentDocument::zoneT): mc = MouseCursor::TopRightCornerResizeCursor; break;
  220. case ComponentDocument::zoneL: mc = MouseCursor::LeftEdgeResizeCursor; break;
  221. case ComponentDocument::zoneR: mc = MouseCursor::RightEdgeResizeCursor; break;
  222. case (ComponentDocument::zoneL | ComponentDocument::zoneB): mc = MouseCursor::BottomLeftCornerResizeCursor; break;
  223. case ComponentDocument::zoneB: mc = MouseCursor::BottomEdgeResizeCursor; break;
  224. case (ComponentDocument::zoneR | ComponentDocument::zoneB): mc = MouseCursor::BottomRightCornerResizeCursor; break;
  225. default: mc = MouseCursor::NormalCursor; break;
  226. }
  227. setMouseCursor (mc);
  228. }
  229. }
  230. };
  231. //==============================================================================
  232. class OverlayComponent : public Component,
  233. public LassoSource <ComponentDocument::SelectedItems::ItemType>,
  234. public ChangeListener
  235. {
  236. public:
  237. OverlayComponent (ComponentCanvas& canvas_)
  238. : canvas (canvas_)
  239. {
  240. setAlwaysOnTop (true);
  241. setWantsKeyboardFocus (true);
  242. canvas.getSelection().addChangeListener (this);
  243. }
  244. ~OverlayComponent()
  245. {
  246. canvas.getSelection().removeChangeListener (this);
  247. lasso = 0;
  248. deleteAllChildren();
  249. }
  250. void mouseDown (const MouseEvent& e)
  251. {
  252. lasso = 0;
  253. if (e.mods.isPopupMenu())
  254. {
  255. PopupMenu m;
  256. canvas.getDocument().addNewComponentMenuItems (m);
  257. const int r = m.show();
  258. canvas.getDocument().performNewComponentMenuItem (r);
  259. }
  260. else
  261. {
  262. Component* underMouse = canvas.getComponentHolder()->getComponentAt (e.x, e.y);
  263. if (underMouse == canvas.getComponentHolder())
  264. underMouse = 0;
  265. if (underMouse == 0 || e.mods.isAltDown())
  266. {
  267. addAndMakeVisible (lasso = new LassoComponent <ComponentDocument::SelectedItems::ItemType>());
  268. lasso->beginLasso (e, this);
  269. }
  270. else
  271. {
  272. mouseDownCompUID = underMouse->getComponentUID();
  273. mouseDownResult = canvas.getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods);
  274. }
  275. }
  276. }
  277. void mouseDrag (const MouseEvent& e)
  278. {
  279. if (lasso != 0)
  280. lasso->dragLasso (e);
  281. }
  282. void mouseUp (const MouseEvent& e)
  283. {
  284. if (lasso != 0)
  285. {
  286. lasso->endLasso();
  287. lasso = 0;
  288. if (e.mouseWasClicked())
  289. canvas.getSelection().deselectAll();
  290. }
  291. else
  292. {
  293. canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult);
  294. }
  295. }
  296. void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height)
  297. {
  298. const Rectangle<int> lassoArea (x, y, width, height);
  299. for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  300. {
  301. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  302. if (c != this && c->getBounds().intersects (lassoArea))
  303. itemsFound.add (c->getComponentUID());
  304. }
  305. }
  306. ComponentDocument::SelectedItems& getLassoSelection() { return canvas.getSelection(); }
  307. void changeListenerCallback (void*)
  308. {
  309. updateSelectedComponentOverlays();
  310. }
  311. private:
  312. ComponentCanvas& canvas;
  313. ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso;
  314. bool mouseDownResult;
  315. uint32 mouseDownCompUID;
  316. ComponentSelectorFrame* getSelectorFrameFor (Component* c) const
  317. {
  318. for (int i = getNumChildComponents(); --i >= 0;)
  319. {
  320. ComponentSelectorFrame* overlay = dynamic_cast <ComponentSelectorFrame*> (getChildComponent(i));
  321. if (overlay != 0 && overlay->getTargetComponentUID() == c->getComponentUID())
  322. return overlay;
  323. }
  324. return 0;
  325. }
  326. void updateSelectedComponentOverlays()
  327. {
  328. ComponentDocument::SelectedItems& selection = canvas.getSelection();
  329. int i;
  330. for (i = getNumChildComponents(); --i >= 0;)
  331. {
  332. ComponentSelectorFrame* overlay = dynamic_cast <ComponentSelectorFrame*> (getChildComponent(i));
  333. if (overlay != 0 && ! selection.isSelected (overlay->getTargetComponentUID()))
  334. delete overlay;
  335. }
  336. for (i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  337. {
  338. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  339. if (c != this && selection.isSelected (c->getComponentUID()) && getSelectorFrameFor (c) == 0)
  340. addAndMakeVisible (new ComponentSelectorFrame (canvas, c));
  341. }
  342. }
  343. };
  344. Component* componentHolder;
  345. OverlayComponent* overlay;
  346. ComponentDocument::SelectedItems selection;
  347. Component* getComponentForUID (const uint32 uid) const
  348. {
  349. for (int i = getNumChildComponents(); --i >= 0;)
  350. if (componentHolder->getChildComponent (i)->getComponentUID() == uid)
  351. return componentHolder->getChildComponent (i);
  352. return 0;
  353. }
  354. };
  355. //==============================================================================
  356. ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document,
  357. Project* project_, ComponentDocument* componentDocument_)
  358. : DocumentEditorComponent (document),
  359. project (project_),
  360. componentDocument (componentDocument_)
  361. {
  362. jassert (componentDocument != 0);
  363. setOpaque (true);
  364. addAndMakeVisible (viewport = new Viewport());
  365. viewport->setViewedComponent (new ComponentCanvas (*this));
  366. }
  367. ComponentEditor::~ComponentEditor()
  368. {
  369. deleteAllChildren();
  370. }
  371. void ComponentEditor::paint (Graphics& g)
  372. {
  373. g.fillAll (Colours::white);
  374. }
  375. void ComponentEditor::resized()
  376. {
  377. viewport->setBounds (0, 0, getWidth(), getHeight());
  378. }