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.

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