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.

398 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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_ComponentLayoutEditor.h"
  20. #include "../ui/jucer_JucerCommandIDs.h"
  21. #include "../jucer_ObjectTypes.h"
  22. #include "../components/jucer_JucerComponentHandler.h"
  23. //==============================================================================
  24. class SubComponentHolderComp : public Component
  25. {
  26. public:
  27. SubComponentHolderComp (JucerDocument& doc,
  28. SnapGridPainter& g)
  29. : document (doc), grid (g),
  30. dontFillBackground (false)
  31. {
  32. setInterceptsMouseClicks (false, false);
  33. setWantsKeyboardFocus (false);
  34. setFocusContainer (true);
  35. }
  36. void paint (Graphics& g)
  37. {
  38. if (! dontFillBackground)
  39. {
  40. if (PaintRoutine* const background = document.getPaintRoutine (0))
  41. {
  42. background->fillWithBackground (g, false);
  43. background->drawElements (g, getLocalBounds());
  44. grid.draw (g, background);
  45. }
  46. else
  47. grid.draw (g, nullptr);
  48. }
  49. }
  50. void resized()
  51. {
  52. if (! getBounds().isEmpty())
  53. {
  54. int numTimesToTry = 10;
  55. while (--numTimesToTry >= 0)
  56. {
  57. bool anyCompsMoved = false;
  58. for (int i = 0; i < getNumChildComponents(); ++i)
  59. {
  60. Component* comp = getChildComponent (i);
  61. if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*comp))
  62. {
  63. const Rectangle<int> newBounds (type->getComponentPosition (comp)
  64. .getRectangle (getLocalBounds(),
  65. document.getComponentLayout()));
  66. anyCompsMoved = anyCompsMoved || (comp->getBounds() != newBounds);
  67. comp->setBounds (newBounds);
  68. }
  69. }
  70. // repeat this loop until they've all stopped shuffling (might require a few
  71. // loops for all the relative positioned comps to settle down)
  72. if (! anyCompsMoved)
  73. break;
  74. }
  75. }
  76. }
  77. void moved()
  78. {
  79. ((ComponentLayoutEditor*) getParentComponent())->updateOverlayPositions();
  80. }
  81. JucerDocument& document;
  82. SnapGridPainter& grid;
  83. bool dontFillBackground;
  84. };
  85. //==============================================================================
  86. ComponentLayoutEditor::ComponentLayoutEditor (JucerDocument& doc, ComponentLayout& cl)
  87. : document (doc), layout (cl), firstResize (true)
  88. {
  89. setWantsKeyboardFocus (true);
  90. addAndMakeVisible (subCompHolder = new SubComponentHolderComp (document, grid));
  91. refreshAllComponents();
  92. setSize (document.getInitialWidth(),
  93. document.getInitialHeight());
  94. }
  95. ComponentLayoutEditor::~ComponentLayoutEditor()
  96. {
  97. document.removeChangeListener (this);
  98. removeChildComponent (&lassoComp);
  99. deleteAllChildren();
  100. }
  101. //==============================================================================
  102. void ComponentLayoutEditor::visibilityChanged()
  103. {
  104. document.beginTransaction();
  105. if (isVisible())
  106. {
  107. refreshAllComponents();
  108. document.addChangeListener (this);
  109. }
  110. else
  111. {
  112. document.removeChangeListener (this);
  113. }
  114. }
  115. void ComponentLayoutEditor::changeListenerCallback (ChangeBroadcaster*)
  116. {
  117. refreshAllComponents();
  118. }
  119. void ComponentLayoutEditor::paint (Graphics&)
  120. {
  121. }
  122. void ComponentLayoutEditor::resized()
  123. {
  124. if (firstResize && getWidth() > 0 && getHeight() > 0)
  125. {
  126. firstResize = false;
  127. refreshAllComponents();
  128. }
  129. subCompHolder->setBounds (getComponentArea());
  130. updateOverlayPositions();
  131. }
  132. Rectangle<int> ComponentLayoutEditor::getComponentArea() const
  133. {
  134. const int editorEdgeGap = 4;
  135. if (document.isFixedSize())
  136. return Rectangle<int> ((getWidth() - document.getInitialWidth()) / 2,
  137. (getHeight() - document.getInitialHeight()) / 2,
  138. document.getInitialWidth(),
  139. document.getInitialHeight());
  140. return Rectangle<int> (editorEdgeGap, editorEdgeGap,
  141. getWidth() - editorEdgeGap * 2,
  142. getHeight() - editorEdgeGap * 2);
  143. }
  144. Image ComponentLayoutEditor::createComponentLayerSnapshot() const
  145. {
  146. ((SubComponentHolderComp*) subCompHolder)->dontFillBackground = true;
  147. Image im = subCompHolder->createComponentSnapshot (Rectangle<int> (0, 0, subCompHolder->getWidth(), subCompHolder->getHeight()));
  148. ((SubComponentHolderComp*) subCompHolder)->dontFillBackground = false;
  149. return im;
  150. }
  151. void ComponentLayoutEditor::updateOverlayPositions()
  152. {
  153. for (int i = getNumChildComponents(); --i >= 0;)
  154. if (ComponentOverlayComponent* const overlay = dynamic_cast <ComponentOverlayComponent*> (getChildComponent (i)))
  155. overlay->updateBoundsToMatchTarget();
  156. }
  157. void ComponentLayoutEditor::refreshAllComponents()
  158. {
  159. for (int i = getNumChildComponents(); --i >= 0;)
  160. {
  161. ScopedPointer<ComponentOverlayComponent> overlay (dynamic_cast <ComponentOverlayComponent*> (getChildComponent (i)));
  162. if (overlay != nullptr && layout.containsComponent (overlay->target))
  163. overlay.release();
  164. }
  165. for (int i = subCompHolder->getNumChildComponents(); --i >= 0;)
  166. {
  167. Component* const comp = subCompHolder->getChildComponent (i);
  168. if (! layout.containsComponent (comp))
  169. subCompHolder->removeChildComponent (comp);
  170. }
  171. Component* lastComp = nullptr;
  172. Component* lastOverlay = nullptr;
  173. for (int i = layout.getNumComponents(); --i >= 0;)
  174. {
  175. Component* const c = layout.getComponent (i);
  176. ComponentOverlayComponent* overlay = getOverlayCompFor (c);
  177. bool isNewOverlay = false;
  178. if (overlay == 0)
  179. {
  180. ComponentTypeHandler* const handler = ComponentTypeHandler::getHandlerFor (*c);
  181. jassert (handler != nullptr);
  182. overlay = handler->createOverlayComponent (c, layout);
  183. addAndMakeVisible (overlay);
  184. isNewOverlay = true;
  185. }
  186. if (lastOverlay != nullptr)
  187. overlay->toBehind (lastOverlay);
  188. else
  189. overlay->toFront (false);
  190. lastOverlay = overlay;
  191. subCompHolder->addAndMakeVisible (c);
  192. if (lastComp != nullptr)
  193. c->toBehind (lastComp);
  194. else
  195. c->toFront (false);
  196. lastComp = c;
  197. c->setWantsKeyboardFocus (false);
  198. c->setFocusContainer (true);
  199. if (isNewOverlay)
  200. overlay->updateBoundsToMatchTarget();
  201. }
  202. if (grid.updateFromDesign (document))
  203. subCompHolder->repaint();
  204. subCompHolder->setBounds (getComponentArea());
  205. subCompHolder->resized();
  206. }
  207. void ComponentLayoutEditor::mouseDown (const MouseEvent& e)
  208. {
  209. if (e.mods.isPopupMenu())
  210. {
  211. PopupMenu m;
  212. m.addCommandItem (commandManager, JucerCommandIDs::editCompLayout);
  213. m.addCommandItem (commandManager, JucerCommandIDs::editCompGraphics);
  214. m.addSeparator();
  215. for (int i = 0; i < ObjectTypes::numComponentTypes; ++i)
  216. m.addCommandItem (commandManager, JucerCommandIDs::newComponentBase + i);
  217. m.show();
  218. }
  219. else
  220. {
  221. addChildComponent (&lassoComp);
  222. lassoComp.beginLasso (e, this);
  223. }
  224. }
  225. void ComponentLayoutEditor::mouseDrag (const MouseEvent& e)
  226. {
  227. lassoComp.toFront (false);
  228. lassoComp.dragLasso (e);
  229. }
  230. void ComponentLayoutEditor::mouseUp (const MouseEvent& e)
  231. {
  232. lassoComp.endLasso();
  233. removeChildComponent (&lassoComp);
  234. if (e.mouseWasClicked() && ! e.mods.isAnyModifierKeyDown())
  235. layout.getSelectedSet().deselectAll();
  236. }
  237. static void moveOrStretch (ComponentLayout& layout, int x, int y, bool snap, bool stretch)
  238. {
  239. if (stretch)
  240. layout.stretchSelectedComps (x, y, snap);
  241. else
  242. layout.moveSelectedComps (x, y, snap);
  243. }
  244. bool ComponentLayoutEditor::keyPressed (const KeyPress& key)
  245. {
  246. const bool snap = key.getModifiers().isAltDown();
  247. const bool stretch = key.getModifiers().isShiftDown();
  248. const int amount = snap ? document.getSnappingGridSize() + 1
  249. : 1;
  250. if (key.isKeyCode (KeyPress::rightKey))
  251. {
  252. moveOrStretch (layout, amount, 0, snap, stretch);
  253. }
  254. else if (key.isKeyCode (KeyPress::downKey))
  255. {
  256. moveOrStretch (layout, 0,amount, snap, stretch);
  257. }
  258. else if (key.isKeyCode (KeyPress::leftKey))
  259. {
  260. moveOrStretch (layout, -amount, 0, snap, stretch);
  261. }
  262. else if (key.isKeyCode (KeyPress::upKey))
  263. {
  264. moveOrStretch (layout, 0, -amount, snap, stretch);
  265. }
  266. else
  267. {
  268. return false;
  269. }
  270. return true;
  271. }
  272. bool ComponentLayoutEditor::isInterestedInFileDrag (const StringArray& filenames)
  273. {
  274. const File f (filenames [0]);
  275. return f.hasFileExtension (".cpp");
  276. }
  277. void ComponentLayoutEditor::filesDropped (const StringArray& filenames, int x, int y)
  278. {
  279. const File f (filenames [0]);
  280. if (JucerDocument::isValidJucerCppFile (f))
  281. {
  282. JucerComponentHandler jucerDocHandler;
  283. layout.getDocument()->beginTransaction();
  284. if (TestComponent* newOne = dynamic_cast <TestComponent*> (layout.addNewComponent (&jucerDocHandler,
  285. x - subCompHolder->getX(),
  286. y - subCompHolder->getY())))
  287. {
  288. JucerComponentHandler::setJucerComponentFile (*layout.getDocument(), newOne,
  289. f.getRelativePathFrom (document.getCppFile().getParentDirectory()));
  290. layout.getSelectedSet().selectOnly (newOne);
  291. }
  292. layout.getDocument()->beginTransaction();
  293. }
  294. }
  295. ComponentOverlayComponent* ComponentLayoutEditor::getOverlayCompFor (Component* compToFind) const
  296. {
  297. for (int i = getNumChildComponents(); --i >= 0;)
  298. {
  299. if (ComponentOverlayComponent* const overlay = dynamic_cast <ComponentOverlayComponent*> (getChildComponent (i)))
  300. if (overlay->target == compToFind)
  301. return overlay;
  302. }
  303. return nullptr;
  304. }
  305. void ComponentLayoutEditor::findLassoItemsInArea (Array <Component*>& results, const Rectangle<int>& area)
  306. {
  307. const Rectangle<int> lasso (area - subCompHolder->getPosition());
  308. for (int i = 0; i < subCompHolder->getNumChildComponents(); ++i)
  309. {
  310. Component* c = subCompHolder->getChildComponent (i);
  311. if (c->getBounds().intersects (lasso))
  312. results.add (c);
  313. }
  314. }
  315. SelectedItemSet <Component*>& ComponentLayoutEditor::getLassoSelection()
  316. {
  317. return layout.getSelectedSet();
  318. }