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.

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