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.

482 lines
15KB

  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 "../../Application/jucer_Application.h"
  19. #include "../jucer_PaintRoutine.h"
  20. #include "../jucer_UtilityFunctions.h"
  21. #include "../ui/jucer_JucerCommandIDs.h"
  22. #include "../ui/jucer_PaintRoutineEditor.h"
  23. #include "../properties/jucer_PositionPropertyBase.h"
  24. #include "jucer_ElementSiblingComponent.h"
  25. #include "jucer_PaintElementUndoableAction.h"
  26. //==============================================================================
  27. PaintElement::PaintElement (PaintRoutine* owner_,
  28. const String& typeName_)
  29. : borderThickness (4),
  30. owner (owner_),
  31. typeName (typeName_),
  32. selected (false),
  33. dragging (false),
  34. originalAspectRatio (1.0)
  35. {
  36. setRepaintsOnMouseActivity (true);
  37. position.rect.setWidth (100);
  38. position.rect.setHeight (100);
  39. setMinimumOnscreenAmounts (0, 0, 0, 0);
  40. setSizeLimits (borderThickness * 2 + 1, borderThickness * 2 + 1, 8192, 8192);
  41. addChildComponent (border = new ResizableBorderComponent (this, this));
  42. border->setBorderThickness (BorderSize<int> (borderThickness));
  43. if (owner != nullptr)
  44. owner->getSelectedElements().addChangeListener (this);
  45. selfChangeListenerList.addChangeListener (this);
  46. siblingComponentsChanged();
  47. }
  48. PaintElement::~PaintElement()
  49. {
  50. siblingComponents.clear();
  51. if (owner != nullptr)
  52. {
  53. owner->getSelectedElements().deselect (this);
  54. owner->getSelectedElements().removeChangeListener (this);
  55. }
  56. }
  57. //==============================================================================
  58. void PaintElement::setInitialBounds (int parentWidth, int parentHeight)
  59. {
  60. RelativePositionedRectangle pr (getPosition());
  61. pr.rect.setX (parentWidth / 4 + Random::getSystemRandom().nextInt (parentWidth / 4) - parentWidth / 8);
  62. pr.rect.setY (parentHeight / 3 + Random::getSystemRandom().nextInt (parentHeight / 4) - parentHeight / 8);
  63. setPosition (pr, false);
  64. }
  65. //==============================================================================
  66. const RelativePositionedRectangle& PaintElement::getPosition() const
  67. {
  68. return position;
  69. }
  70. class PaintElementMoveAction : public PaintElementUndoableAction <PaintElement>
  71. {
  72. public:
  73. PaintElementMoveAction (PaintElement* const element, const RelativePositionedRectangle& newState_)
  74. : PaintElementUndoableAction <PaintElement> (element),
  75. newState (newState_),
  76. oldState (element->getPosition())
  77. {
  78. }
  79. bool perform()
  80. {
  81. showCorrectTab();
  82. getElement()->setPosition (newState, false);
  83. return true;
  84. }
  85. bool undo()
  86. {
  87. showCorrectTab();
  88. getElement()->setPosition (oldState, false);
  89. return true;
  90. }
  91. private:
  92. RelativePositionedRectangle newState, oldState;
  93. };
  94. void PaintElement::setPosition (const RelativePositionedRectangle& newPosition, const bool undoable)
  95. {
  96. if (position != newPosition)
  97. {
  98. if (undoable)
  99. {
  100. perform (new PaintElementMoveAction (this, newPosition),
  101. "Move " + getTypeName());
  102. }
  103. else
  104. {
  105. position = newPosition;
  106. if (owner != nullptr)
  107. owner->changed();
  108. }
  109. }
  110. }
  111. //==============================================================================
  112. Rectangle<int> PaintElement::getCurrentBounds (const Rectangle<int>& parentArea) const
  113. {
  114. return position.getRectangle (parentArea, getDocument()->getComponentLayout());
  115. }
  116. void PaintElement::setCurrentBounds (const Rectangle<int>& newBounds,
  117. const Rectangle<int>& parentArea,
  118. const bool undoable)
  119. {
  120. RelativePositionedRectangle pr (position);
  121. pr.updateFrom (newBounds.getX() - parentArea.getX(),
  122. newBounds.getY() - parentArea.getY(),
  123. jmax (1, newBounds.getWidth()),
  124. jmax (1, newBounds.getHeight()),
  125. Rectangle<int> (0, 0, parentArea.getWidth(), parentArea.getHeight()),
  126. getDocument()->getComponentLayout());
  127. setPosition (pr, undoable);
  128. updateBounds (parentArea);
  129. }
  130. void PaintElement::updateBounds (const Rectangle<int>& parentArea)
  131. {
  132. if (! parentArea.isEmpty())
  133. {
  134. setBounds (getCurrentBounds (parentArea)
  135. .expanded (borderThickness,
  136. borderThickness));
  137. for (int i = siblingComponents.size(); --i >= 0;)
  138. siblingComponents.getUnchecked(i)->updatePosition();
  139. }
  140. }
  141. //==============================================================================
  142. class ElementPositionProperty : public PositionPropertyBase
  143. {
  144. public:
  145. ElementPositionProperty (PaintElement* e, const String& name,
  146. ComponentPositionDimension dimension_)
  147. : PositionPropertyBase (e, name, dimension_, true, false,
  148. e->getDocument()->getComponentLayout()),
  149. listener (e)
  150. {
  151. listener.setPropertyToRefresh (*this);
  152. }
  153. void setPosition (const RelativePositionedRectangle& newPos)
  154. {
  155. listener.owner->setPosition (newPos, true);
  156. }
  157. RelativePositionedRectangle getPosition() const
  158. {
  159. return listener.owner->getPosition();
  160. }
  161. ElementListener<PaintElement> listener;
  162. };
  163. //==============================================================================
  164. void PaintElement::getEditableProperties (Array <PropertyComponent*>& props)
  165. {
  166. props.add (new ElementPositionProperty (this, "x", PositionPropertyBase::componentX));
  167. props.add (new ElementPositionProperty (this, "y", PositionPropertyBase::componentY));
  168. props.add (new ElementPositionProperty (this, "width", PositionPropertyBase::componentWidth));
  169. props.add (new ElementPositionProperty (this, "height", PositionPropertyBase::componentHeight));
  170. }
  171. //==============================================================================
  172. JucerDocument* PaintElement::getDocument() const
  173. {
  174. return owner->getDocument();
  175. }
  176. void PaintElement::changed()
  177. {
  178. repaint();
  179. owner->changed();
  180. }
  181. bool PaintElement::perform (UndoableAction* action, const String& actionName)
  182. {
  183. return owner->perform (action, actionName);
  184. }
  185. void PaintElement::parentHierarchyChanged()
  186. {
  187. updateSiblingComps();
  188. }
  189. //==============================================================================
  190. void PaintElement::drawExtraEditorGraphics (Graphics&, const Rectangle<int>& /*relativeTo*/)
  191. {
  192. }
  193. void PaintElement::paint (Graphics& g)
  194. {
  195. Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  196. g.saveState();
  197. g.setOrigin (area.getPosition() - Component::getPosition());
  198. area.setPosition (0, 0);
  199. g.saveState();
  200. g.reduceClipRegion (0, 0, area.getWidth(), area.getHeight());
  201. draw (g, getDocument()->getComponentLayout(), area);
  202. g.restoreState();
  203. drawExtraEditorGraphics (g, area);
  204. g.restoreState();
  205. if (selected)
  206. {
  207. const BorderSize<int> borderSize (border->getBorderThickness());
  208. drawResizableBorder (g, getWidth(), getHeight(), borderSize,
  209. (isMouseOverOrDragging() || border->isMouseOverOrDragging()));
  210. }
  211. else if (isMouseOverOrDragging())
  212. {
  213. drawMouseOverCorners (g, getWidth(), getHeight());
  214. }
  215. }
  216. void PaintElement::resized()
  217. {
  218. border->setBounds (getLocalBounds());
  219. }
  220. void PaintElement::mouseDown (const MouseEvent& e)
  221. {
  222. dragging = false;
  223. if (owner != nullptr)
  224. {
  225. owner->getSelectedPoints().deselectAll();
  226. mouseDownSelectStatus = owner->getSelectedElements().addToSelectionOnMouseDown (this, e.mods);
  227. }
  228. if (e.mods.isPopupMenu())
  229. {
  230. showPopupMenu();
  231. return; // this may be deleted now..
  232. }
  233. }
  234. void PaintElement::mouseDrag (const MouseEvent& e)
  235. {
  236. if (! e.mods.isPopupMenu())
  237. {
  238. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  239. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  240. if (selected && ! dragging)
  241. {
  242. dragging = ! e.mouseWasClicked();
  243. if (dragging)
  244. owner->startDragging (area);
  245. }
  246. if (dragging)
  247. owner->dragSelectedComps (e.getDistanceFromDragStartX(),
  248. e.getDistanceFromDragStartY(),
  249. area);
  250. }
  251. }
  252. void PaintElement::mouseUp (const MouseEvent& e)
  253. {
  254. if (dragging)
  255. owner->endDragging();
  256. if (owner != nullptr)
  257. owner->getSelectedElements().addToSelectionOnMouseUp (this, e.mods, dragging, mouseDownSelectStatus);
  258. }
  259. void PaintElement::resizeStart()
  260. {
  261. if (getHeight() > 0)
  262. originalAspectRatio = getWidth() / (double) getHeight();
  263. else
  264. originalAspectRatio = 1.0;
  265. }
  266. void PaintElement::resizeEnd()
  267. {
  268. }
  269. void PaintElement::checkBounds (Rectangle<int>& b,
  270. const Rectangle<int>& previousBounds,
  271. const Rectangle<int>& limits,
  272. const bool isStretchingTop,
  273. const bool isStretchingLeft,
  274. const bool isStretchingBottom,
  275. const bool isStretchingRight)
  276. {
  277. if (ModifierKeys::getCurrentModifiers().isShiftDown())
  278. setFixedAspectRatio (originalAspectRatio);
  279. else
  280. setFixedAspectRatio (0.0);
  281. ComponentBoundsConstrainer::checkBounds (b, previousBounds, limits, isStretchingTop, isStretchingLeft, isStretchingBottom, isStretchingRight);
  282. JucerDocument* document = getDocument();
  283. if (document != nullptr && document->isSnapActive (true))
  284. {
  285. jassert (getParentComponent() != nullptr);
  286. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  287. int x = b.getX();
  288. int y = b.getY();
  289. int w = b.getWidth();
  290. int h = b.getHeight();
  291. x += borderThickness - area.getX();
  292. y += borderThickness - area.getY();
  293. w -= borderThickness * 2;
  294. h -= borderThickness * 2;
  295. int right = x + w;
  296. int bottom = y + h;
  297. if (isStretchingRight)
  298. right = document->snapPosition (right);
  299. if (isStretchingBottom)
  300. bottom = document->snapPosition (bottom);
  301. if (isStretchingLeft)
  302. x = document->snapPosition (x);
  303. if (isStretchingTop)
  304. y = document->snapPosition (y);
  305. w = (right - x) + borderThickness * 2;
  306. h = (bottom - y) + borderThickness * 2;
  307. x -= borderThickness - area.getX();
  308. y -= borderThickness - area.getY();
  309. b = Rectangle<int> (x, y, w, h);
  310. }
  311. }
  312. void PaintElement::applyBoundsToComponent (Component*, const Rectangle<int>& newBounds)
  313. {
  314. if (getBounds() != newBounds)
  315. {
  316. getDocument()->getUndoManager().undoCurrentTransactionOnly();
  317. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  318. setCurrentBounds (newBounds.expanded (-borderThickness, -borderThickness),
  319. ((PaintRoutineEditor*) getParentComponent())->getComponentArea(),
  320. true);
  321. }
  322. }
  323. Rectangle<int> PaintElement::getCurrentAbsoluteBounds() const
  324. {
  325. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  326. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  327. return position.getRectangle (area, getDocument()->getComponentLayout());
  328. }
  329. void PaintElement::getCurrentAbsoluteBoundsDouble (double& x, double& y, double& w, double& h) const
  330. {
  331. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  332. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  333. position.getRectangleDouble (x, y, w, h, area, getDocument()->getComponentLayout());
  334. }
  335. void PaintElement::changeListenerCallback (ChangeBroadcaster*)
  336. {
  337. const bool nowSelected = owner != nullptr && owner->getSelectedElements().isSelected (this);
  338. if (selected != nowSelected)
  339. {
  340. selected = nowSelected;
  341. border->setVisible (nowSelected);
  342. repaint();
  343. selectionChanged (nowSelected);
  344. }
  345. updateSiblingComps();
  346. }
  347. void PaintElement::selectionChanged (const bool /*isSelected*/)
  348. {
  349. }
  350. void PaintElement::createSiblingComponents()
  351. {
  352. }
  353. void PaintElement::siblingComponentsChanged()
  354. {
  355. siblingComponents.clear();
  356. selfChangeListenerList.sendChangeMessage();
  357. }
  358. void PaintElement::updateSiblingComps()
  359. {
  360. if (selected && getParentComponent() != nullptr && owner->getSelectedElements().getNumSelected() == 1)
  361. {
  362. if (siblingComponents.size() == 0)
  363. createSiblingComponents();
  364. for (int i = siblingComponents.size(); --i >= 0;)
  365. siblingComponents.getUnchecked(i)->updatePosition();
  366. }
  367. else
  368. {
  369. siblingComponents.clear();
  370. }
  371. }
  372. void PaintElement::showPopupMenu()
  373. {
  374. ApplicationCommandManager* commandManager = &IntrojucerApp::getCommandManager();
  375. PopupMenu m;
  376. m.addCommandItem (commandManager, JucerCommandIDs::toFront);
  377. m.addCommandItem (commandManager, JucerCommandIDs::toBack);
  378. m.addSeparator();
  379. m.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  380. m.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  381. m.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  382. m.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  383. m.show();
  384. }