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.

397 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. ComponentOverlayComponent* overlay = getOverlayCompFor (c);
  176. bool isNewOverlay = false;
  177. if (overlay == 0)
  178. {
  179. ComponentTypeHandler* const handler = ComponentTypeHandler::getHandlerFor (*c);
  180. jassert (handler != nullptr);
  181. overlay = handler->createOverlayComponent (c, layout);
  182. addAndMakeVisible (overlay);
  183. isNewOverlay = true;
  184. }
  185. if (lastOverlay != nullptr)
  186. overlay->toBehind (lastOverlay);
  187. else
  188. overlay->toFront (false);
  189. lastOverlay = overlay;
  190. subCompHolder->addAndMakeVisible (c);
  191. if (lastComp != nullptr)
  192. c->toBehind (lastComp);
  193. else
  194. c->toFront (false);
  195. lastComp = c;
  196. c->setWantsKeyboardFocus (false);
  197. c->setFocusContainer (true);
  198. if (isNewOverlay)
  199. overlay->updateBoundsToMatchTarget();
  200. }
  201. if (grid.updateFromDesign (document))
  202. subCompHolder->repaint();
  203. subCompHolder->setBounds (getComponentArea());
  204. subCompHolder->resized();
  205. }
  206. void ComponentLayoutEditor::mouseDown (const MouseEvent& e)
  207. {
  208. if (e.mods.isPopupMenu())
  209. {
  210. PopupMenu m;
  211. m.addCommandItem (commandManager, JucerCommandIDs::editCompLayout);
  212. m.addCommandItem (commandManager, JucerCommandIDs::editCompGraphics);
  213. m.addSeparator();
  214. for (int i = 0; i < ObjectTypes::numComponentTypes; ++i)
  215. m.addCommandItem (commandManager, JucerCommandIDs::newComponentBase + i);
  216. m.show();
  217. }
  218. else
  219. {
  220. addChildComponent (&lassoComp);
  221. lassoComp.beginLasso (e, this);
  222. }
  223. }
  224. void ComponentLayoutEditor::mouseDrag (const MouseEvent& e)
  225. {
  226. lassoComp.toFront (false);
  227. lassoComp.dragLasso (e);
  228. }
  229. void ComponentLayoutEditor::mouseUp (const MouseEvent& e)
  230. {
  231. lassoComp.endLasso();
  232. removeChildComponent (&lassoComp);
  233. if (e.mouseWasClicked() && ! e.mods.isAnyModifierKeyDown())
  234. layout.getSelectedSet().deselectAll();
  235. }
  236. static void moveOrStretch (ComponentLayout& layout, int x, int y, bool snap, bool stretch)
  237. {
  238. if (stretch)
  239. layout.stretchSelectedComps (x, y, snap);
  240. else
  241. layout.moveSelectedComps (x, y, snap);
  242. }
  243. bool ComponentLayoutEditor::keyPressed (const KeyPress& key)
  244. {
  245. const bool snap = key.getModifiers().isAltDown();
  246. const bool stretch = key.getModifiers().isShiftDown();
  247. const int amount = snap ? document.getSnappingGridSize() + 1
  248. : 1;
  249. if (key.isKeyCode (KeyPress::rightKey))
  250. {
  251. moveOrStretch (layout, amount, 0, snap, stretch);
  252. }
  253. else if (key.isKeyCode (KeyPress::downKey))
  254. {
  255. moveOrStretch (layout, 0,amount, snap, stretch);
  256. }
  257. else if (key.isKeyCode (KeyPress::leftKey))
  258. {
  259. moveOrStretch (layout, -amount, 0, snap, stretch);
  260. }
  261. else if (key.isKeyCode (KeyPress::upKey))
  262. {
  263. moveOrStretch (layout, 0, -amount, snap, stretch);
  264. }
  265. else
  266. {
  267. return false;
  268. }
  269. return true;
  270. }
  271. bool ComponentLayoutEditor::isInterestedInFileDrag (const StringArray& filenames)
  272. {
  273. const File f (filenames [0]);
  274. return f.hasFileExtension (".cpp");
  275. }
  276. void ComponentLayoutEditor::filesDropped (const StringArray& filenames, int x, int y)
  277. {
  278. const File f (filenames [0]);
  279. if (JucerDocument::isValidJucerCppFile (f))
  280. {
  281. JucerComponentHandler jucerDocHandler;
  282. layout.getDocument()->beginTransaction();
  283. if (TestComponent* newOne = dynamic_cast <TestComponent*> (layout.addNewComponent (&jucerDocHandler,
  284. x - subCompHolder->getX(),
  285. y - subCompHolder->getY())))
  286. {
  287. JucerComponentHandler::setJucerComponentFile (*layout.getDocument(), newOne,
  288. f.getRelativePathFrom (document.getCppFile().getParentDirectory()));
  289. layout.getSelectedSet().selectOnly (newOne);
  290. }
  291. layout.getDocument()->beginTransaction();
  292. }
  293. }
  294. ComponentOverlayComponent* ComponentLayoutEditor::getOverlayCompFor (Component* compToFind) const
  295. {
  296. for (int i = getNumChildComponents(); --i >= 0;)
  297. {
  298. if (ComponentOverlayComponent* const overlay = dynamic_cast <ComponentOverlayComponent*> (getChildComponent (i)))
  299. if (overlay->target == compToFind)
  300. return overlay;
  301. }
  302. return nullptr;
  303. }
  304. void ComponentLayoutEditor::findLassoItemsInArea (Array <Component*>& results, const Rectangle<int>& area)
  305. {
  306. const Rectangle<int> lasso (area - subCompHolder->getPosition());
  307. for (int i = 0; i < subCompHolder->getNumChildComponents(); ++i)
  308. {
  309. Component* c = subCompHolder->getChildComponent (i);
  310. if (c->getBounds().intersects (lasso))
  311. results.add (c);
  312. }
  313. }
  314. SelectedItemSet <Component*>& ComponentLayoutEditor::getLassoSelection()
  315. {
  316. return layout.getSelectedSet();
  317. }