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.

478 lines
15KB

  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_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. private ElementListenerBase<PaintElement>
  144. {
  145. public:
  146. ElementPositionProperty (PaintElement* e, const String& name,
  147. ComponentPositionDimension dimension_)
  148. : PositionPropertyBase (e, name, dimension_, true, false,
  149. e->getDocument()->getComponentLayout()),
  150. ElementListenerBase<PaintElement> (e)
  151. {
  152. }
  153. void setPosition (const RelativePositionedRectangle& newPos)
  154. {
  155. owner->setPosition (newPos, true);
  156. }
  157. RelativePositionedRectangle getPosition() const
  158. {
  159. return owner->getPosition();
  160. }
  161. };
  162. //==============================================================================
  163. void PaintElement::getEditableProperties (Array <PropertyComponent*>& properties)
  164. {
  165. properties.add (new ElementPositionProperty (this, "x", PositionPropertyBase::componentX));
  166. properties.add (new ElementPositionProperty (this, "y", PositionPropertyBase::componentY));
  167. properties.add (new ElementPositionProperty (this, "width", PositionPropertyBase::componentWidth));
  168. properties.add (new ElementPositionProperty (this, "height", PositionPropertyBase::componentHeight));
  169. }
  170. //==============================================================================
  171. JucerDocument* PaintElement::getDocument() const
  172. {
  173. return owner->getDocument();
  174. }
  175. void PaintElement::changed()
  176. {
  177. repaint();
  178. owner->changed();
  179. }
  180. bool PaintElement::perform (UndoableAction* action, const String& actionName)
  181. {
  182. return owner->perform (action, actionName);
  183. }
  184. void PaintElement::parentHierarchyChanged()
  185. {
  186. updateSiblingComps();
  187. }
  188. //==============================================================================
  189. void PaintElement::drawExtraEditorGraphics (Graphics&, const Rectangle<int>& /*relativeTo*/)
  190. {
  191. }
  192. void PaintElement::paint (Graphics& g)
  193. {
  194. Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  195. g.saveState();
  196. g.setOrigin (area.getX() - getX(), area.getY() - getY());
  197. area.setPosition (0, 0);
  198. g.saveState();
  199. g.reduceClipRegion (0, 0, area.getWidth(), area.getHeight());
  200. draw (g, getDocument()->getComponentLayout(), area);
  201. g.restoreState();
  202. drawExtraEditorGraphics (g, area);
  203. g.restoreState();
  204. if (selected)
  205. {
  206. const BorderSize<int> borderSize (border->getBorderThickness());
  207. drawResizableBorder (g, getWidth(), getHeight(), borderSize,
  208. (isMouseOverOrDragging() || border->isMouseOverOrDragging()));
  209. }
  210. else if (isMouseOverOrDragging())
  211. {
  212. drawMouseOverCorners (g, getWidth(), getHeight());
  213. }
  214. }
  215. void PaintElement::resized()
  216. {
  217. border->setBounds (getLocalBounds());
  218. }
  219. void PaintElement::mouseDown (const MouseEvent& e)
  220. {
  221. dragging = false;
  222. if (owner != nullptr)
  223. {
  224. owner->getSelectedPoints().deselectAll();
  225. mouseDownSelectStatus = owner->getSelectedElements().addToSelectionOnMouseDown (this, e.mods);
  226. }
  227. if (e.mods.isPopupMenu())
  228. {
  229. showPopupMenu();
  230. return; // this may be deleted now..
  231. }
  232. }
  233. void PaintElement::mouseDrag (const MouseEvent& e)
  234. {
  235. if (! e.mods.isPopupMenu())
  236. {
  237. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  238. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  239. if (selected && ! dragging)
  240. {
  241. dragging = ! e.mouseWasClicked();
  242. if (dragging)
  243. owner->startDragging (area);
  244. }
  245. if (dragging)
  246. owner->dragSelectedComps (e.getDistanceFromDragStartX(),
  247. e.getDistanceFromDragStartY(),
  248. area);
  249. }
  250. }
  251. void PaintElement::mouseUp (const MouseEvent& e)
  252. {
  253. if (dragging)
  254. owner->endDragging();
  255. if (owner != nullptr)
  256. owner->getSelectedElements().addToSelectionOnMouseUp (this, e.mods, dragging, mouseDownSelectStatus);
  257. }
  258. void PaintElement::resizeStart()
  259. {
  260. if (getHeight() > 0)
  261. originalAspectRatio = getWidth() / (double) getHeight();
  262. else
  263. originalAspectRatio = 1.0;
  264. }
  265. void PaintElement::resizeEnd()
  266. {
  267. }
  268. void PaintElement::checkBounds (Rectangle<int>& bounds,
  269. const Rectangle<int>& previousBounds,
  270. const Rectangle<int>& limits,
  271. const bool isStretchingTop,
  272. const bool isStretchingLeft,
  273. const bool isStretchingBottom,
  274. const bool isStretchingRight)
  275. {
  276. if (ModifierKeys::getCurrentModifiers().isShiftDown())
  277. setFixedAspectRatio (originalAspectRatio);
  278. else
  279. setFixedAspectRatio (0.0);
  280. ComponentBoundsConstrainer::checkBounds (bounds, previousBounds, limits, isStretchingTop, isStretchingLeft, isStretchingBottom, isStretchingRight);
  281. JucerDocument* document = getDocument();
  282. if (document != nullptr && document->isSnapActive (true))
  283. {
  284. jassert (getParentComponent() != nullptr);
  285. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  286. int x = bounds.getX();
  287. int y = bounds.getY();
  288. int w = bounds.getWidth();
  289. int h = bounds.getHeight();
  290. x += borderThickness - area.getX();
  291. y += borderThickness - area.getY();
  292. w -= borderThickness * 2;
  293. h -= borderThickness * 2;
  294. int right = x + w;
  295. int bottom = y + h;
  296. if (isStretchingRight)
  297. right = document->snapPosition (right);
  298. if (isStretchingBottom)
  299. bottom = document->snapPosition (bottom);
  300. if (isStretchingLeft)
  301. x = document->snapPosition (x);
  302. if (isStretchingTop)
  303. y = document->snapPosition (y);
  304. w = (right - x) + borderThickness * 2;
  305. h = (bottom - y) + borderThickness * 2;
  306. x -= borderThickness - area.getX();
  307. y -= borderThickness - area.getY();
  308. bounds = Rectangle<int> (x, y, w, h);
  309. }
  310. }
  311. void PaintElement::applyBoundsToComponent (Component*, const Rectangle<int>& bounds)
  312. {
  313. if (getBounds() != bounds)
  314. {
  315. getDocument()->getUndoManager().undoCurrentTransactionOnly();
  316. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  317. setCurrentBounds (bounds.expanded (-borderThickness, -borderThickness),
  318. ((PaintRoutineEditor*) getParentComponent())->getComponentArea(),
  319. true);
  320. }
  321. }
  322. Rectangle<int> PaintElement::getCurrentAbsoluteBounds() const
  323. {
  324. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  325. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  326. return position.getRectangle (area, getDocument()->getComponentLayout());
  327. }
  328. void PaintElement::getCurrentAbsoluteBoundsDouble (double& x, double& y, double& w, double& h) const
  329. {
  330. jassert (dynamic_cast <PaintRoutineEditor*> (getParentComponent()) != nullptr);
  331. const Rectangle<int> area (((PaintRoutineEditor*) getParentComponent())->getComponentArea());
  332. position.getRectangleDouble (x, y, w, h, area, getDocument()->getComponentLayout());
  333. }
  334. void PaintElement::changeListenerCallback (ChangeBroadcaster*)
  335. {
  336. const bool nowSelected = owner != nullptr && owner->getSelectedElements().isSelected (this);
  337. if (selected != nowSelected)
  338. {
  339. selected = nowSelected;
  340. border->setVisible (nowSelected);
  341. repaint();
  342. selectionChanged (nowSelected);
  343. }
  344. updateSiblingComps();
  345. }
  346. void PaintElement::selectionChanged (const bool /*isSelected*/)
  347. {
  348. }
  349. void PaintElement::createSiblingComponents()
  350. {
  351. }
  352. void PaintElement::siblingComponentsChanged()
  353. {
  354. siblingComponents.clear();
  355. selfChangeListenerList.sendChangeMessage();
  356. }
  357. void PaintElement::updateSiblingComps()
  358. {
  359. if (selected && getParentComponent() != nullptr && owner->getSelectedElements().getNumSelected() == 1)
  360. {
  361. if (siblingComponents.size() == 0)
  362. createSiblingComponents();
  363. for (int i = siblingComponents.size(); --i >= 0;)
  364. siblingComponents.getUnchecked(i)->updatePosition();
  365. }
  366. else
  367. {
  368. siblingComponents.clear();
  369. }
  370. }
  371. void PaintElement::showPopupMenu()
  372. {
  373. PopupMenu m;
  374. m.addCommandItem (commandManager, JucerCommandIDs::toFront);
  375. m.addCommandItem (commandManager, JucerCommandIDs::toBack);
  376. m.addSeparator();
  377. m.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
  378. m.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
  379. m.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
  380. m.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
  381. m.show();
  382. }