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.

493 lines
16KB

  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 ComponentResizeFrame : public Component,
  123. public ComponentListener
  124. {
  125. public:
  126. ComponentResizeFrame (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. ~ComponentResizeFrame()
  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. bool hitTest (int x, int y)
  185. {
  186. return ! getCentreArea().contains (x, y);
  187. }
  188. uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); }
  189. private:
  190. ComponentCanvas& canvas;
  191. Component::SafePointer<Component> component;
  192. int dragZone;
  193. const int borderThickness;
  194. const Rectangle<int> getCentreArea() const
  195. {
  196. return Rectangle<int> (0, 0, getWidth(), getHeight()).reduced (borderThickness, borderThickness);
  197. }
  198. void updateDragZone (const Point<int>& p)
  199. {
  200. int newZone = 0;
  201. if (! getCentreArea().contains (p))
  202. {
  203. const int bw = jmax (borderThickness, proportionOfWidth (0.1f), jmin (10, proportionOfWidth (0.33f)));
  204. const int bh = jmax (borderThickness, proportionOfHeight (0.1f), jmin (10, proportionOfHeight (0.33f)));
  205. if (p.getX() < bw)
  206. newZone |= ComponentDocument::zoneL;
  207. else if (p.getX() >= getWidth() - bw)
  208. newZone |= ComponentDocument::zoneR;
  209. if (p.getY() < bh)
  210. newZone |= ComponentDocument::zoneT;
  211. else if (p.getY() >= getHeight() - bh)
  212. newZone |= ComponentDocument::zoneB;
  213. }
  214. if (dragZone != newZone)
  215. {
  216. dragZone = newZone;
  217. MouseCursor::StandardCursorType mc = MouseCursor::NormalCursor;
  218. switch (newZone)
  219. {
  220. case (ComponentDocument::zoneL | ComponentDocument::zoneT): mc = MouseCursor::TopLeftCornerResizeCursor; break;
  221. case ComponentDocument::zoneT: mc = MouseCursor::TopEdgeResizeCursor; break;
  222. case (ComponentDocument::zoneR | ComponentDocument::zoneT): mc = MouseCursor::TopRightCornerResizeCursor; break;
  223. case ComponentDocument::zoneL: mc = MouseCursor::LeftEdgeResizeCursor; break;
  224. case ComponentDocument::zoneR: mc = MouseCursor::RightEdgeResizeCursor; break;
  225. case (ComponentDocument::zoneL | ComponentDocument::zoneB): mc = MouseCursor::BottomLeftCornerResizeCursor; break;
  226. case ComponentDocument::zoneB: mc = MouseCursor::BottomEdgeResizeCursor; break;
  227. case (ComponentDocument::zoneR | ComponentDocument::zoneB): mc = MouseCursor::BottomRightCornerResizeCursor; break;
  228. default: mc = MouseCursor::NormalCursor; break;
  229. }
  230. setMouseCursor (mc);
  231. }
  232. }
  233. };
  234. //==============================================================================
  235. class OverlayComponent : public Component,
  236. public LassoSource <ComponentDocument::SelectedItems::ItemType>,
  237. public ChangeListener
  238. {
  239. public:
  240. OverlayComponent (ComponentCanvas& canvas_)
  241. : canvas (canvas_)
  242. {
  243. setAlwaysOnTop (true);
  244. setWantsKeyboardFocus (true);
  245. canvas.getSelection().addChangeListener (this);
  246. }
  247. ~OverlayComponent()
  248. {
  249. canvas.getSelection().removeChangeListener (this);
  250. lasso = 0;
  251. deleteAllChildren();
  252. }
  253. void mouseDown (const MouseEvent& e)
  254. {
  255. lasso = 0;
  256. mouseDownCompUID = 0;
  257. isDraggingClickedComp = false;
  258. if (e.mods.isPopupMenu())
  259. {
  260. PopupMenu m;
  261. canvas.getDocument().addNewComponentMenuItems (m);
  262. const int r = m.show();
  263. canvas.getDocument().performNewComponentMenuItem (r);
  264. }
  265. else
  266. {
  267. Component* underMouse = canvas.getComponentHolder()->getComponentAt (e.x, e.y);
  268. if (underMouse == canvas.getComponentHolder())
  269. underMouse = 0;
  270. if (underMouse == 0 || e.mods.isAltDown())
  271. {
  272. addAndMakeVisible (lasso = new LassoComponent <ComponentDocument::SelectedItems::ItemType>());
  273. lasso->beginLasso (e, this);
  274. }
  275. else
  276. {
  277. mouseDownCompUID = underMouse->getComponentUID();
  278. mouseDownResult = canvas.getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods);
  279. }
  280. }
  281. }
  282. void mouseDrag (const MouseEvent& e)
  283. {
  284. if (lasso != 0)
  285. {
  286. lasso->dragLasso (e);
  287. }
  288. else if (mouseDownCompUID != 0 && (! e.mouseWasClicked()) && (! e.mods.isPopupMenu()))
  289. {
  290. if (! isDraggingClickedComp)
  291. {
  292. isDraggingClickedComp = true;
  293. canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult);
  294. canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, 0);
  295. }
  296. canvas.getDocument().continueDrag (e);
  297. }
  298. }
  299. void mouseUp (const MouseEvent& e)
  300. {
  301. if (lasso != 0)
  302. {
  303. lasso->endLasso();
  304. lasso = 0;
  305. if (e.mouseWasClicked())
  306. canvas.getSelection().deselectAll();
  307. }
  308. else if (! e.mods.isPopupMenu())
  309. {
  310. if (! isDraggingClickedComp)
  311. canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult);
  312. }
  313. }
  314. void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height)
  315. {
  316. const Rectangle<int> lassoArea (x, y, width, height);
  317. for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  318. {
  319. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  320. if (c->getBounds().intersects (lassoArea))
  321. itemsFound.add (c->getComponentUID());
  322. }
  323. }
  324. ComponentDocument::SelectedItems& getLassoSelection() { return canvas.getSelection(); }
  325. void changeListenerCallback (void*)
  326. {
  327. updateSelectedComponentResizeFrames();
  328. }
  329. void modifierKeysChanged (const ModifierKeys&)
  330. {
  331. Desktop::getInstance().getMainMouseSource().triggerFakeMove();
  332. }
  333. private:
  334. ComponentCanvas& canvas;
  335. ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso;
  336. bool mouseDownResult, isDraggingClickedComp;
  337. uint32 mouseDownCompUID;
  338. ComponentResizeFrame* getSelectorFrameFor (Component* c) const
  339. {
  340. for (int i = getNumChildComponents(); --i >= 0;)
  341. {
  342. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  343. if (resizer != 0 && resizer->getTargetComponentUID() == c->getComponentUID())
  344. return resizer;
  345. }
  346. return 0;
  347. }
  348. void updateSelectedComponentResizeFrames()
  349. {
  350. ComponentDocument::SelectedItems& selection = canvas.getSelection();
  351. int i;
  352. for (i = getNumChildComponents(); --i >= 0;)
  353. {
  354. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  355. if (resizer != 0 && ! selection.isSelected (resizer->getTargetComponentUID()))
  356. delete resizer;
  357. }
  358. for (i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  359. {
  360. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  361. if (c != this && selection.isSelected (c->getComponentUID()) && getSelectorFrameFor (c) == 0)
  362. addAndMakeVisible (new ComponentResizeFrame (canvas, c));
  363. }
  364. }
  365. };
  366. Component* componentHolder;
  367. OverlayComponent* overlay;
  368. ComponentDocument::SelectedItems selection;
  369. Component* getComponentForUID (const uint32 uid) const
  370. {
  371. for (int i = componentHolder->getNumChildComponents(); --i >= 0;)
  372. if (componentHolder->getChildComponent (i)->getComponentUID() == uid)
  373. return componentHolder->getChildComponent (i);
  374. return 0;
  375. }
  376. };
  377. //==============================================================================
  378. ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document,
  379. Project* project_, ComponentDocument* componentDocument_)
  380. : DocumentEditorComponent (document),
  381. project (project_),
  382. componentDocument (componentDocument_)
  383. {
  384. jassert (componentDocument != 0);
  385. setOpaque (true);
  386. addAndMakeVisible (viewport = new Viewport());
  387. viewport->setViewedComponent (new ComponentCanvas (*this));
  388. }
  389. ComponentEditor::~ComponentEditor()
  390. {
  391. deleteAllChildren();
  392. }
  393. void ComponentEditor::paint (Graphics& g)
  394. {
  395. g.fillAll (Colours::white);
  396. }
  397. void ComponentEditor::resized()
  398. {
  399. viewport->setBounds (0, 0, getWidth(), getHeight());
  400. }