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.

523 lines
17KB

  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_), borderThickness (4)
  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. g.setColour (Colour::greyLevel (0.9f));
  44. g.drawRect (0, 0, getWidth(), getHeight(), borderThickness);
  45. }
  46. void resized()
  47. {
  48. componentHolder->setBounds (borderThickness, borderThickness,
  49. getWidth() - borderThickness * 2, getHeight() - borderThickness * 2);
  50. overlay->setBounds (componentHolder->getBounds());
  51. }
  52. void zoom (float newScale, const Point<int>& centre)
  53. {
  54. }
  55. ComponentEditor& getEditor() { return editor; }
  56. ComponentDocument& getDocument() { return editor.getDocument(); }
  57. ComponentDocument::SelectedItems& getSelection() { return selection; }
  58. Component* findComponentFor (const ValueTree& state)
  59. {
  60. ComponentDocument& doc = getDocument();
  61. for (int i = componentHolder->getNumChildComponents(); --i >= 0;)
  62. {
  63. Component* c = componentHolder->getChildComponent (i);
  64. if (doc.isStateForComponent (state, c))
  65. return c;
  66. }
  67. return 0;
  68. }
  69. void updateComponents()
  70. {
  71. ComponentDocument& doc = getDocument();
  72. int i;
  73. for (i = componentHolder->getNumChildComponents(); --i >= 0;)
  74. {
  75. Component* c = componentHolder->getChildComponent (i);
  76. if (! doc.containsComponent (c))
  77. {
  78. selection.deselect (c->getComponentUID());
  79. delete c;
  80. }
  81. }
  82. const int num = doc.getNumComponents();
  83. for (i = 0; i < num; ++i)
  84. {
  85. const ValueTree v (doc.getComponent (i));
  86. Component* c = findComponentFor (v);
  87. if (c == 0)
  88. {
  89. c = doc.createComponent (i);
  90. componentHolder->addAndMakeVisible (c);
  91. }
  92. else
  93. {
  94. doc.updateComponent (c);
  95. }
  96. }
  97. }
  98. void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property)
  99. {
  100. updateComponents();
  101. }
  102. void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged)
  103. {
  104. updateComponents();
  105. }
  106. void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged)
  107. {
  108. }
  109. Component* getComponentHolder() const { return componentHolder; }
  110. const Array<Component*> getSelectedComps() const
  111. {
  112. Array <Component*> comps;
  113. for (int i = 0; i < selection.getNumSelected(); ++i)
  114. {
  115. Component* c = getComponentForUID (selection.getSelectedItem (i));
  116. jassert (c != 0);
  117. if (c != 0)
  118. comps.add (c);
  119. }
  120. return comps;
  121. }
  122. private:
  123. ComponentEditor& editor;
  124. const int borderThickness;
  125. //==============================================================================
  126. class ComponentResizeFrame : public Component,
  127. public ComponentListener
  128. {
  129. public:
  130. ComponentResizeFrame (ComponentCanvas& canvas_,
  131. Component* componentToAttachTo)
  132. : canvas (canvas_),
  133. component (componentToAttachTo),
  134. borderThickness (4)
  135. {
  136. componentMovedOrResized (*componentToAttachTo, true, true);
  137. componentToAttachTo->addComponentListener (this);
  138. }
  139. ~ComponentResizeFrame()
  140. {
  141. if (component != 0)
  142. component->removeComponentListener (this);
  143. }
  144. void paint (Graphics& g)
  145. {
  146. g.setColour (Colours::red.withAlpha (0.1f));
  147. g.drawRect (0, 0, getWidth(), getHeight(), borderThickness);
  148. }
  149. void mouseEnter (const MouseEvent& e)
  150. {
  151. //repaint();
  152. updateDragZone (e.getPosition());
  153. }
  154. void mouseExit (const MouseEvent& e)
  155. {
  156. //repaint();
  157. updateDragZone (e.getPosition());
  158. }
  159. void mouseMove (const MouseEvent& e)
  160. {
  161. updateDragZone (e.getPosition());
  162. }
  163. void mouseDown (const MouseEvent& e)
  164. {
  165. jassert (component != 0);
  166. if (component != 0)
  167. {
  168. updateDragZone (e.getPosition());
  169. canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, dragZone);
  170. }
  171. }
  172. void mouseDrag (const MouseEvent& e)
  173. {
  174. if (component != 0)
  175. canvas.getDocument().continueDrag (e);
  176. }
  177. void mouseUp (const MouseEvent& e)
  178. {
  179. if (component != 0)
  180. canvas.getDocument().endDrag (e);
  181. updateDragZone (e.getPosition());
  182. }
  183. void componentMovedOrResized (Component&, bool wasMoved, bool wasResized)
  184. {
  185. if (component != 0)
  186. setBounds (component->getBounds().expanded (borderThickness, borderThickness));
  187. }
  188. bool hitTest (int x, int y)
  189. {
  190. return ! getCentreArea().contains (x, y);
  191. }
  192. uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); }
  193. private:
  194. ComponentCanvas& canvas;
  195. Component::SafePointer<Component> component;
  196. ResizableBorderComponent::Zone dragZone;
  197. const int borderThickness;
  198. const Rectangle<int> getCentreArea() const
  199. {
  200. return getLocalBounds().reduced (borderThickness, borderThickness);
  201. }
  202. void updateDragZone (const Point<int>& p)
  203. {
  204. ResizableBorderComponent::Zone newZone
  205. = ResizableBorderComponent::Zone::fromPositionOnBorder (getLocalBounds(),
  206. BorderSize (borderThickness), p);
  207. if (dragZone != newZone)
  208. {
  209. dragZone = newZone;
  210. setMouseCursor (newZone.getMouseCursor());
  211. }
  212. }
  213. };
  214. //==============================================================================
  215. class OverlayComponent : public Component,
  216. public LassoSource <ComponentDocument::SelectedItems::ItemType>,
  217. public ChangeListener
  218. {
  219. public:
  220. OverlayComponent (ComponentCanvas& canvas_)
  221. : canvas (canvas_)
  222. {
  223. setAlwaysOnTop (true);
  224. setWantsKeyboardFocus (true);
  225. canvas.getSelection().addChangeListener (this);
  226. }
  227. ~OverlayComponent()
  228. {
  229. canvas.getSelection().removeChangeListener (this);
  230. lasso = 0;
  231. deleteAllChildren();
  232. }
  233. void mouseDown (const MouseEvent& e)
  234. {
  235. lasso = 0;
  236. mouseDownCompUID = 0;
  237. isDraggingClickedComp = false;
  238. if (e.mods.isPopupMenu())
  239. {
  240. PopupMenu m;
  241. canvas.getDocument().addNewComponentMenuItems (m);
  242. const int r = m.show();
  243. canvas.getDocument().performNewComponentMenuItem (r);
  244. }
  245. else
  246. {
  247. Component* underMouse = canvas.getComponentHolder()->getComponentAt (e.x, e.y);
  248. if (underMouse == canvas.getComponentHolder())
  249. underMouse = 0;
  250. if (underMouse == 0 || e.mods.isAltDown())
  251. {
  252. addAndMakeVisible (lasso = new LassoComponent <ComponentDocument::SelectedItems::ItemType>());
  253. lasso->beginLasso (e, this);
  254. }
  255. else
  256. {
  257. mouseDownCompUID = underMouse->getComponentUID();
  258. mouseDownResult = canvas.getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods);
  259. }
  260. }
  261. }
  262. void mouseDrag (const MouseEvent& e)
  263. {
  264. if (lasso != 0)
  265. {
  266. lasso->dragLasso (e);
  267. }
  268. else if (mouseDownCompUID != 0 && (! e.mouseWasClicked()) && (! e.mods.isPopupMenu()))
  269. {
  270. if (! isDraggingClickedComp)
  271. {
  272. isDraggingClickedComp = true;
  273. canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult);
  274. canvas.getDocument().beginDrag (canvas.getSelectedComps(), e,
  275. ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre));
  276. }
  277. canvas.getDocument().continueDrag (e);
  278. }
  279. }
  280. void mouseUp (const MouseEvent& e)
  281. {
  282. if (lasso != 0)
  283. {
  284. lasso->endLasso();
  285. lasso = 0;
  286. if (e.mouseWasClicked())
  287. canvas.getSelection().deselectAll();
  288. }
  289. else if (! e.mods.isPopupMenu())
  290. {
  291. if (! isDraggingClickedComp)
  292. canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult);
  293. }
  294. }
  295. void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height)
  296. {
  297. const Rectangle<int> lassoArea (x, y, width, height);
  298. for (int i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  299. {
  300. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  301. if (c->getBounds().intersects (lassoArea))
  302. itemsFound.add (c->getComponentUID());
  303. }
  304. }
  305. ComponentDocument::SelectedItems& getLassoSelection() { return canvas.getSelection(); }
  306. void changeListenerCallback (void*)
  307. {
  308. updateSelectedComponentResizeFrames();
  309. }
  310. void modifierKeysChanged (const ModifierKeys&)
  311. {
  312. Desktop::getInstance().getMainMouseSource().triggerFakeMove();
  313. }
  314. private:
  315. ComponentCanvas& canvas;
  316. ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso;
  317. bool mouseDownResult, isDraggingClickedComp;
  318. uint32 mouseDownCompUID;
  319. ComponentResizeFrame* getSelectorFrameFor (Component* c) const
  320. {
  321. for (int i = getNumChildComponents(); --i >= 0;)
  322. {
  323. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  324. if (resizer != 0 && resizer->getTargetComponentUID() == c->getComponentUID())
  325. return resizer;
  326. }
  327. return 0;
  328. }
  329. void updateSelectedComponentResizeFrames()
  330. {
  331. ComponentDocument::SelectedItems& selection = canvas.getSelection();
  332. int i;
  333. for (i = getNumChildComponents(); --i >= 0;)
  334. {
  335. ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i));
  336. if (resizer != 0 && ! selection.isSelected (resizer->getTargetComponentUID()))
  337. delete resizer;
  338. }
  339. for (i = canvas.getComponentHolder()->getNumChildComponents(); --i >= 0;)
  340. {
  341. Component* c = canvas.getComponentHolder()->getChildComponent(i);
  342. if (c != this && selection.isSelected (c->getComponentUID()) && getSelectorFrameFor (c) == 0)
  343. addAndMakeVisible (new ComponentResizeFrame (canvas, c));
  344. }
  345. }
  346. };
  347. Component* componentHolder;
  348. OverlayComponent* overlay;
  349. ComponentDocument::SelectedItems selection;
  350. Component* getComponentForUID (const uint32 uid) const
  351. {
  352. for (int i = componentHolder->getNumChildComponents(); --i >= 0;)
  353. if (componentHolder->getChildComponent (i)->getComponentUID() == uid)
  354. return componentHolder->getChildComponent (i);
  355. return 0;
  356. }
  357. };
  358. //==============================================================================
  359. ComponentEditor::ComponentEditor (OpenDocumentManager::Document* document,
  360. Project* project_, ComponentDocument* componentDocument_)
  361. : DocumentEditorComponent (document),
  362. project (project_),
  363. componentDocument (componentDocument_)
  364. {
  365. setOpaque (true);
  366. addAndMakeVisible (viewport = new Viewport());
  367. if (document != 0)
  368. viewport->setViewedComponent (new ComponentCanvas (*this));
  369. }
  370. ComponentEditor::~ComponentEditor()
  371. {
  372. deleteAllChildren();
  373. }
  374. void ComponentEditor::paint (Graphics& g)
  375. {
  376. g.fillAll (Colours::white);
  377. }
  378. void ComponentEditor::resized()
  379. {
  380. viewport->setBounds (0, 0, getWidth(), getHeight());
  381. }
  382. void ComponentEditor::getAllCommands (Array <CommandID>& commands)
  383. {
  384. DocumentEditorComponent::getAllCommands (commands);
  385. const CommandID ids[] = { CommandIDs::undo,
  386. CommandIDs::redo };
  387. commands.addArray (ids, numElementsInArray (ids));
  388. }
  389. void ComponentEditor::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
  390. {
  391. result.setActive (document != 0);
  392. switch (commandID)
  393. {
  394. case CommandIDs::undo:
  395. result.setInfo ("Undo", "Undoes the last change",
  396. CommandCategories::general, 0);
  397. result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier, 0));
  398. break;
  399. case CommandIDs::redo:
  400. result.setInfo ("Redo", "Redoes the last change",
  401. CommandCategories::general, 0);
  402. result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0));
  403. result.defaultKeypresses.add (KeyPress ('y', ModifierKeys::commandModifier, 0));
  404. break;
  405. default:
  406. DocumentEditorComponent::getCommandInfo (commandID, result);
  407. break;
  408. }
  409. }
  410. bool ComponentEditor::perform (const InvocationInfo& info)
  411. {
  412. switch (info.commandID)
  413. {
  414. case CommandIDs::undo:
  415. getDocument().getUndoManager()->undo();
  416. return true;
  417. case CommandIDs::redo:
  418. getDocument().getUndoManager()->redo();
  419. return true;
  420. default:
  421. break;
  422. }
  423. return DocumentEditorComponent::perform (info);
  424. }