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.

399 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../../jucer_Headers.h"
  18. #include "jucer_ComponentLayoutEditor.h"
  19. #include "../ui/jucer_JucerCommandIDs.h"
  20. #include "../jucer_ObjectTypes.h"
  21. #include "../components/jucer_JucerComponentHandler.h"
  22. //==============================================================================
  23. class SubComponentHolderComp : public Component
  24. {
  25. public:
  26. SubComponentHolderComp (JucerDocument& doc,
  27. SnapGridPainter& g)
  28. : document (doc), grid (g),
  29. dontFillBackground (false)
  30. {
  31. setInterceptsMouseClicks (false, false);
  32. setWantsKeyboardFocus (false);
  33. setFocusContainer (true);
  34. }
  35. void paint (Graphics& g)
  36. {
  37. if (! dontFillBackground)
  38. {
  39. if (PaintRoutine* const background = document.getPaintRoutine (0))
  40. {
  41. background->fillWithBackground (g, false);
  42. background->drawElements (g, getLocalBounds());
  43. grid.draw (g, background);
  44. }
  45. else
  46. grid.draw (g, nullptr);
  47. }
  48. }
  49. void resized()
  50. {
  51. if (! getBounds().isEmpty())
  52. {
  53. int numTimesToTry = 10;
  54. while (--numTimesToTry >= 0)
  55. {
  56. bool anyCompsMoved = false;
  57. for (int i = 0; i < getNumChildComponents(); ++i)
  58. {
  59. Component* comp = getChildComponent (i);
  60. if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*comp))
  61. {
  62. const Rectangle<int> newBounds (type->getComponentPosition (comp)
  63. .getRectangle (getLocalBounds(),
  64. document.getComponentLayout()));
  65. anyCompsMoved = anyCompsMoved || (comp->getBounds() != newBounds);
  66. comp->setBounds (newBounds);
  67. }
  68. }
  69. // repeat this loop until they've all stopped shuffling (might require a few
  70. // loops for all the relative positioned comps to settle down)
  71. if (! anyCompsMoved)
  72. break;
  73. }
  74. }
  75. }
  76. void moved()
  77. {
  78. ((ComponentLayoutEditor*) getParentComponent())->updateOverlayPositions();
  79. }
  80. JucerDocument& document;
  81. SnapGridPainter& grid;
  82. bool dontFillBackground;
  83. };
  84. //==============================================================================
  85. ComponentLayoutEditor::ComponentLayoutEditor (JucerDocument& doc, ComponentLayout& cl)
  86. : document (doc), layout (cl), firstResize (true)
  87. {
  88. setWantsKeyboardFocus (true);
  89. addAndMakeVisible (subCompHolder = new SubComponentHolderComp (document, grid));
  90. refreshAllComponents();
  91. setSize (document.getInitialWidth(),
  92. document.getInitialHeight());
  93. }
  94. ComponentLayoutEditor::~ComponentLayoutEditor()
  95. {
  96. document.removeChangeListener (this);
  97. removeChildComponent (&lassoComp);
  98. deleteAllChildren();
  99. }
  100. //==============================================================================
  101. void ComponentLayoutEditor::visibilityChanged()
  102. {
  103. document.beginTransaction();
  104. if (isVisible())
  105. {
  106. refreshAllComponents();
  107. document.addChangeListener (this);
  108. }
  109. else
  110. {
  111. document.removeChangeListener (this);
  112. }
  113. }
  114. void ComponentLayoutEditor::changeListenerCallback (ChangeBroadcaster*)
  115. {
  116. refreshAllComponents();
  117. }
  118. void ComponentLayoutEditor::paint (Graphics&)
  119. {
  120. }
  121. void ComponentLayoutEditor::resized()
  122. {
  123. if (firstResize && getWidth() > 0 && getHeight() > 0)
  124. {
  125. firstResize = false;
  126. refreshAllComponents();
  127. }
  128. subCompHolder->setBounds (getComponentArea());
  129. updateOverlayPositions();
  130. }
  131. Rectangle<int> ComponentLayoutEditor::getComponentArea() const
  132. {
  133. const int editorEdgeGap = 4;
  134. if (document.isFixedSize())
  135. return Rectangle<int> ((getWidth() - document.getInitialWidth()) / 2,
  136. (getHeight() - document.getInitialHeight()) / 2,
  137. document.getInitialWidth(),
  138. document.getInitialHeight());
  139. return Rectangle<int> (editorEdgeGap, editorEdgeGap,
  140. getWidth() - editorEdgeGap * 2,
  141. getHeight() - editorEdgeGap * 2);
  142. }
  143. Image ComponentLayoutEditor::createComponentLayerSnapshot() const
  144. {
  145. ((SubComponentHolderComp*) subCompHolder)->dontFillBackground = true;
  146. Image im = subCompHolder->createComponentSnapshot (Rectangle<int> (0, 0, subCompHolder->getWidth(), subCompHolder->getHeight()));
  147. ((SubComponentHolderComp*) subCompHolder)->dontFillBackground = false;
  148. return im;
  149. }
  150. void ComponentLayoutEditor::updateOverlayPositions()
  151. {
  152. for (int i = getNumChildComponents(); --i >= 0;)
  153. if (ComponentOverlayComponent* const overlay = dynamic_cast <ComponentOverlayComponent*> (getChildComponent (i)))
  154. overlay->updateBoundsToMatchTarget();
  155. }
  156. void ComponentLayoutEditor::refreshAllComponents()
  157. {
  158. for (int i = getNumChildComponents(); --i >= 0;)
  159. {
  160. ScopedPointer<ComponentOverlayComponent> overlay (dynamic_cast <ComponentOverlayComponent*> (getChildComponent (i)));
  161. if (overlay != nullptr && layout.containsComponent (overlay->target))
  162. overlay.release();
  163. }
  164. for (int i = subCompHolder->getNumChildComponents(); --i >= 0;)
  165. {
  166. Component* const comp = subCompHolder->getChildComponent (i);
  167. if (! layout.containsComponent (comp))
  168. subCompHolder->removeChildComponent (comp);
  169. }
  170. Component* lastComp = nullptr;
  171. Component* lastOverlay = nullptr;
  172. for (int i = layout.getNumComponents(); --i >= 0;)
  173. {
  174. Component* const c = layout.getComponent (i);
  175. jassert (c != nullptr);
  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. }