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.

926 lines
30KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI 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_ColouredElement.h"
  19. #include "jucer_GradientPointComponent.h"
  20. #include "../properties/jucer_PositionPropertyBase.h"
  21. #include "../properties/jucer_ColourPropertyComponent.h"
  22. #include "jucer_PaintElementUndoableAction.h"
  23. #include "jucer_PaintElementPath.h"
  24. #include "jucer_ImageResourceProperty.h"
  25. //==============================================================================
  26. class ElementFillModeProperty : public ChoicePropertyComponent
  27. {
  28. public:
  29. ElementFillModeProperty (ColouredElement* const e, const bool isForStroke_)
  30. : ChoicePropertyComponent ("fill mode"), listener (e),
  31. isForStroke (isForStroke_)
  32. {
  33. listener.setPropertyToRefresh (*this);
  34. choices.add ("Solid Colour");
  35. choices.add ("Linear Gradient");
  36. choices.add ("Radial Gradient");
  37. choices.add ("Image Brush");
  38. }
  39. void setIndex (int newIndex)
  40. {
  41. JucerFillType fill (isForStroke ? listener.owner->getStrokeType().fill
  42. : listener.owner->getFillType());
  43. switch (newIndex)
  44. {
  45. case 0: fill.mode = JucerFillType::solidColour; break;
  46. case 1: fill.mode = JucerFillType::linearGradient; break;
  47. case 2: fill.mode = JucerFillType::radialGradient; break;
  48. case 3: fill.mode = JucerFillType::imageBrush; break;
  49. default: jassertfalse; break;
  50. }
  51. if (! isForStroke)
  52. listener.owner->setFillType (fill, true);
  53. else
  54. listener.owner->setStrokeFill (fill, true);
  55. }
  56. int getIndex() const
  57. {
  58. switch (isForStroke ? listener.owner->getStrokeType().fill.mode
  59. : listener.owner->getFillType().mode)
  60. {
  61. case JucerFillType::solidColour: return 0;
  62. case JucerFillType::linearGradient: return 1;
  63. case JucerFillType::radialGradient: return 2;
  64. case JucerFillType::imageBrush: return 3;
  65. default: jassertfalse; break;
  66. }
  67. return 0;
  68. }
  69. private:
  70. ElementListener<ColouredElement> listener;
  71. const bool isForStroke;
  72. };
  73. //==============================================================================
  74. class ElementFillColourProperty : public JucerColourPropertyComponent
  75. {
  76. public:
  77. enum ColourType
  78. {
  79. solidColour,
  80. gradientColour1,
  81. gradientColour2
  82. };
  83. ElementFillColourProperty (const String& name,
  84. ColouredElement* const owner_,
  85. const ColourType type_,
  86. const bool isForStroke_)
  87. : JucerColourPropertyComponent (name, false),
  88. listener (owner_),
  89. type (type_),
  90. isForStroke (isForStroke_)
  91. {
  92. listener.setPropertyToRefresh (*this);
  93. }
  94. void setColour (Colour newColour) override
  95. {
  96. listener.owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  97. JucerFillType fill (isForStroke ? listener.owner->getStrokeType().fill
  98. : listener.owner->getFillType());
  99. switch (type)
  100. {
  101. case solidColour: fill.colour = newColour; break;
  102. case gradientColour1: fill.gradCol1 = newColour; break;
  103. case gradientColour2: fill.gradCol2 = newColour; break;
  104. default: jassertfalse; break;
  105. }
  106. if (! isForStroke)
  107. listener.owner->setFillType (fill, true);
  108. else
  109. listener.owner->setStrokeFill (fill, true);
  110. }
  111. Colour getColour() const override
  112. {
  113. const JucerFillType fill (isForStroke ? listener.owner->getStrokeType().fill
  114. : listener.owner->getFillType());
  115. switch (type)
  116. {
  117. case solidColour: return fill.colour; break;
  118. case gradientColour1: return fill.gradCol1; break;
  119. case gradientColour2: return fill.gradCol2; break;
  120. default: jassertfalse; break;
  121. }
  122. return Colours::black;
  123. }
  124. void resetToDefault() override
  125. {
  126. jassertfalse; // option shouldn't be visible
  127. }
  128. private:
  129. ElementListener<ColouredElement> listener;
  130. const ColourType type;
  131. const bool isForStroke;
  132. };
  133. //==============================================================================
  134. class ElementFillPositionProperty : public PositionPropertyBase
  135. {
  136. public:
  137. ElementFillPositionProperty (ColouredElement* const owner_,
  138. const String& name,
  139. ComponentPositionDimension dimension_,
  140. const bool isStart_,
  141. const bool isForStroke_)
  142. : PositionPropertyBase (owner_, name, dimension_, false, false,
  143. owner_->getDocument()->getComponentLayout()),
  144. listener (owner_),
  145. isStart (isStart_),
  146. isForStroke (isForStroke_)
  147. {
  148. listener.setPropertyToRefresh (*this);
  149. }
  150. void setPosition (const RelativePositionedRectangle& newPos)
  151. {
  152. JucerFillType fill (isForStroke ? listener.owner->getStrokeType().fill
  153. : listener.owner->getFillType());
  154. if (isStart)
  155. fill.gradPos1 = newPos;
  156. else
  157. fill.gradPos2 = newPos;
  158. if (! isForStroke)
  159. listener.owner->setFillType (fill, true);
  160. else
  161. listener.owner->setStrokeFill (fill, true);
  162. }
  163. RelativePositionedRectangle getPosition() const
  164. {
  165. const JucerFillType fill (isForStroke ? listener.owner->getStrokeType().fill
  166. : listener.owner->getFillType());
  167. return isStart ? fill.gradPos1
  168. : fill.gradPos2;
  169. }
  170. private:
  171. ElementListener<ColouredElement> listener;
  172. const bool isStart, isForStroke;
  173. };
  174. //==============================================================================
  175. class EnableStrokeProperty : public BooleanPropertyComponent
  176. {
  177. public:
  178. EnableStrokeProperty (ColouredElement* const owner_)
  179. : BooleanPropertyComponent ("outline", "Outline enabled", "No outline"),
  180. listener (owner_)
  181. {
  182. listener.setPropertyToRefresh (*this);
  183. }
  184. //==============================================================================
  185. void setState (bool newState) { listener.owner->enableStroke (newState, true); }
  186. bool getState() const { return listener.owner->isStrokeEnabled(); }
  187. ElementListener<ColouredElement> listener;
  188. };
  189. //==============================================================================
  190. class StrokeThicknessProperty : public SliderPropertyComponent
  191. {
  192. public:
  193. StrokeThicknessProperty (ColouredElement* const owner_)
  194. : SliderPropertyComponent ("outline thickness", 0.1, 200.0, 0.1, 0.3),
  195. listener (owner_)
  196. {
  197. listener.setPropertyToRefresh (*this);
  198. }
  199. void setValue (double newValue)
  200. {
  201. listener.owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  202. listener.owner->setStrokeType (PathStrokeType ((float) newValue,
  203. listener.owner->getStrokeType().stroke.getJointStyle(),
  204. listener.owner->getStrokeType().stroke.getEndStyle()),
  205. true);
  206. }
  207. double getValue() const { return listener.owner->getStrokeType().stroke.getStrokeThickness(); }
  208. ElementListener<ColouredElement> listener;
  209. };
  210. //==============================================================================
  211. class StrokeJointProperty : public ChoicePropertyComponent
  212. {
  213. public:
  214. StrokeJointProperty (ColouredElement* const owner_)
  215. : ChoicePropertyComponent ("joint style"),
  216. listener (owner_)
  217. {
  218. listener.setPropertyToRefresh (*this);
  219. choices.add ("mitered");
  220. choices.add ("curved");
  221. choices.add ("beveled");
  222. }
  223. void setIndex (int newIndex)
  224. {
  225. const PathStrokeType::JointStyle joints[] = { PathStrokeType::mitered,
  226. PathStrokeType::curved,
  227. PathStrokeType::beveled };
  228. jassert (newIndex >= 0 && newIndex < 3);
  229. listener.owner->setStrokeType (PathStrokeType (listener.owner->getStrokeType().stroke.getStrokeThickness(),
  230. joints [newIndex],
  231. listener.owner->getStrokeType().stroke.getEndStyle()),
  232. true);
  233. }
  234. int getIndex() const
  235. {
  236. switch (listener.owner->getStrokeType().stroke.getJointStyle())
  237. {
  238. case PathStrokeType::mitered: return 0;
  239. case PathStrokeType::curved: return 1;
  240. case PathStrokeType::beveled: return 2;
  241. default: jassertfalse; break;
  242. }
  243. return 0;
  244. }
  245. ElementListener<ColouredElement> listener;
  246. };
  247. //==============================================================================
  248. class StrokeEndCapProperty : public ChoicePropertyComponent
  249. {
  250. public:
  251. StrokeEndCapProperty (ColouredElement* const owner_)
  252. : ChoicePropertyComponent ("end-cap style"),
  253. listener (owner_)
  254. {
  255. listener.setPropertyToRefresh (*this);
  256. choices.add ("butt");
  257. choices.add ("square");
  258. choices.add ("round");
  259. }
  260. void setIndex (int newIndex)
  261. {
  262. const PathStrokeType::EndCapStyle ends[] = { PathStrokeType::butt,
  263. PathStrokeType::square,
  264. PathStrokeType::rounded };
  265. jassert (newIndex >= 0 && newIndex < 3);
  266. listener.owner->setStrokeType (PathStrokeType (listener.owner->getStrokeType().stroke.getStrokeThickness(),
  267. listener.owner->getStrokeType().stroke.getJointStyle(),
  268. ends [newIndex]),
  269. true);
  270. }
  271. int getIndex() const
  272. {
  273. switch (listener.owner->getStrokeType().stroke.getEndStyle())
  274. {
  275. case PathStrokeType::butt: return 0;
  276. case PathStrokeType::square: return 1;
  277. case PathStrokeType::rounded: return 2;
  278. default: jassertfalse; break;
  279. }
  280. return 0;
  281. }
  282. ElementListener<ColouredElement> listener;
  283. };
  284. //==============================================================================
  285. class ImageBrushResourceProperty : public ImageResourceProperty <ColouredElement>
  286. {
  287. public:
  288. ImageBrushResourceProperty (ColouredElement* const e, const bool isForStroke_)
  289. : ImageResourceProperty <ColouredElement> (e, isForStroke_ ? "stroke image"
  290. : "fill image"),
  291. isForStroke (isForStroke_)
  292. {
  293. }
  294. //==============================================================================
  295. void setResource (const String& newName)
  296. {
  297. if (element != nullptr)
  298. {
  299. if (isForStroke)
  300. {
  301. JucerFillType type (element->getStrokeType().fill);
  302. type.imageResourceName = newName;
  303. element->setStrokeFill (type, true);
  304. }
  305. else
  306. {
  307. JucerFillType type (element->getFillType());
  308. type.imageResourceName = newName;
  309. element->setFillType (type, true);
  310. }
  311. }
  312. }
  313. String getResource() const
  314. {
  315. if (element == nullptr)
  316. return {};
  317. if (isForStroke)
  318. return element->getStrokeType().fill.imageResourceName;
  319. return element->getFillType().imageResourceName;
  320. }
  321. private:
  322. bool isForStroke;
  323. };
  324. //==============================================================================
  325. class ImageBrushPositionProperty : public PositionPropertyBase
  326. {
  327. public:
  328. ImageBrushPositionProperty (ColouredElement* const owner_,
  329. const String& name,
  330. ComponentPositionDimension dimension_,
  331. const bool isForStroke_)
  332. : PositionPropertyBase (owner_, name, dimension_, false, false,
  333. owner_->getDocument()->getComponentLayout()),
  334. listener (owner_),
  335. isForStroke (isForStroke_)
  336. {
  337. listener.setPropertyToRefresh (*this);
  338. }
  339. void setPosition (const RelativePositionedRectangle& newPos)
  340. {
  341. if (isForStroke)
  342. {
  343. JucerFillType type (listener.owner->getStrokeType().fill);
  344. type.imageAnchor = newPos;
  345. listener.owner->setStrokeFill (type, true);
  346. }
  347. else
  348. {
  349. JucerFillType type (listener.owner->getFillType());
  350. type.imageAnchor = newPos;
  351. listener.owner->setFillType (type, true);
  352. }
  353. }
  354. RelativePositionedRectangle getPosition() const
  355. {
  356. if (isForStroke)
  357. return listener.owner->getStrokeType().fill.imageAnchor;
  358. return listener.owner->getFillType().imageAnchor;
  359. }
  360. private:
  361. ElementListener<ColouredElement> listener;
  362. const bool isForStroke;
  363. };
  364. //==============================================================================
  365. class ImageBrushOpacityProperty : public SliderPropertyComponent
  366. {
  367. public:
  368. ImageBrushOpacityProperty (ColouredElement* const e, const bool isForStroke_)
  369. : SliderPropertyComponent ("opacity", 0.0, 1.0, 0.001),
  370. listener (e),
  371. isForStroke (isForStroke_)
  372. {
  373. listener.setPropertyToRefresh (*this);
  374. }
  375. void setValue (double newValue)
  376. {
  377. if (listener.owner != nullptr)
  378. {
  379. listener.owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  380. if (isForStroke)
  381. {
  382. JucerFillType type (listener.owner->getStrokeType().fill);
  383. type.imageOpacity = newValue;
  384. listener.owner->setStrokeFill (type, true);
  385. }
  386. else
  387. {
  388. JucerFillType type (listener.owner->getFillType());
  389. type.imageOpacity = newValue;
  390. listener.owner->setFillType (type, true);
  391. }
  392. }
  393. }
  394. double getValue() const
  395. {
  396. if (listener.owner == nullptr)
  397. return 0;
  398. if (isForStroke)
  399. return listener.owner->getStrokeType().fill.imageOpacity;
  400. return listener.owner->getFillType().imageOpacity;
  401. }
  402. private:
  403. ElementListener<ColouredElement> listener;
  404. bool isForStroke;
  405. };
  406. //==============================================================================
  407. ColouredElement::ColouredElement (PaintRoutine* owner_,
  408. const String& name,
  409. const bool showOutline_,
  410. const bool showJointAndEnd_)
  411. : PaintElement (owner_, name),
  412. isStrokePresent (false),
  413. showOutline (showOutline_),
  414. showJointAndEnd (showJointAndEnd_)
  415. {
  416. }
  417. ColouredElement::~ColouredElement()
  418. {
  419. }
  420. //==============================================================================
  421. void ColouredElement::getEditableProperties (Array <PropertyComponent*>& props)
  422. {
  423. PaintElement::getEditableProperties (props);
  424. getColourSpecificProperties (props);
  425. }
  426. void ColouredElement::getColourSpecificProperties (Array <PropertyComponent*>& props)
  427. {
  428. props.add (new ElementFillModeProperty (this, false));
  429. switch (getFillType().mode)
  430. {
  431. case JucerFillType::solidColour:
  432. props.add (new ElementFillColourProperty ("colour", this, ElementFillColourProperty::solidColour, false));
  433. break;
  434. case JucerFillType::linearGradient:
  435. case JucerFillType::radialGradient:
  436. props.add (new ElementFillColourProperty ("colour 1", this, ElementFillColourProperty::gradientColour1, false));
  437. props.add (new ElementFillPositionProperty (this, "x1", PositionPropertyBase::componentX, true, false));
  438. props.add (new ElementFillPositionProperty (this, "y1", PositionPropertyBase::componentY, true, false));
  439. props.add (new ElementFillColourProperty ("colour 2", this, ElementFillColourProperty::gradientColour2, false));
  440. props.add (new ElementFillPositionProperty (this, "x2", PositionPropertyBase::componentX, false, false));
  441. props.add (new ElementFillPositionProperty (this, "y2", PositionPropertyBase::componentY, false, false));
  442. break;
  443. case JucerFillType::imageBrush:
  444. props.add (new ImageBrushResourceProperty (this, false));
  445. props.add (new ImageBrushPositionProperty (this, "anchor x", PositionPropertyBase::componentX, false));
  446. props.add (new ImageBrushPositionProperty (this, "anchor y", PositionPropertyBase::componentY, false));
  447. props.add (new ImageBrushOpacityProperty (this, false));
  448. break;
  449. default:
  450. jassertfalse;
  451. break;
  452. }
  453. if (showOutline)
  454. {
  455. props.add (new EnableStrokeProperty (this));
  456. if (isStrokePresent)
  457. {
  458. props.add (new StrokeThicknessProperty (this));
  459. if (showJointAndEnd)
  460. {
  461. props.add (new StrokeJointProperty (this));
  462. props.add (new StrokeEndCapProperty (this));
  463. }
  464. props.add (new ElementFillModeProperty (this, true));
  465. switch (getStrokeType().fill.mode)
  466. {
  467. case JucerFillType::solidColour:
  468. props.add (new ElementFillColourProperty ("colour", this, ElementFillColourProperty::solidColour, true));
  469. break;
  470. case JucerFillType::linearGradient:
  471. case JucerFillType::radialGradient:
  472. props.add (new ElementFillColourProperty ("colour 1", this, ElementFillColourProperty::gradientColour1, true));
  473. props.add (new ElementFillPositionProperty (this, "x1", PositionPropertyBase::componentX, true, true));
  474. props.add (new ElementFillPositionProperty (this, "y1", PositionPropertyBase::componentY, true, true));
  475. props.add (new ElementFillColourProperty ("colour 2", this, ElementFillColourProperty::gradientColour2, true));
  476. props.add (new ElementFillPositionProperty (this, "x2", PositionPropertyBase::componentX, false, true));
  477. props.add (new ElementFillPositionProperty (this, "y2", PositionPropertyBase::componentY, false, true));
  478. break;
  479. case JucerFillType::imageBrush:
  480. props.add (new ImageBrushResourceProperty (this, true));
  481. props.add (new ImageBrushPositionProperty (this, "stroke anchor x", PositionPropertyBase::componentX, true));
  482. props.add (new ImageBrushPositionProperty (this, "stroke anchor y", PositionPropertyBase::componentY, true));
  483. props.add (new ImageBrushOpacityProperty (this, true));
  484. break;
  485. default:
  486. jassertfalse;
  487. break;
  488. }
  489. }
  490. }
  491. }
  492. //==============================================================================
  493. const JucerFillType& ColouredElement::getFillType() noexcept
  494. {
  495. return fillType;
  496. }
  497. class FillTypeChangeAction : public PaintElementUndoableAction <ColouredElement>
  498. {
  499. public:
  500. FillTypeChangeAction (ColouredElement* const element, const JucerFillType& newState_)
  501. : PaintElementUndoableAction <ColouredElement> (element),
  502. newState (newState_)
  503. {
  504. oldState = element->getFillType();
  505. }
  506. bool perform()
  507. {
  508. showCorrectTab();
  509. getElement()->setFillType (newState, false);
  510. return true;
  511. }
  512. bool undo()
  513. {
  514. showCorrectTab();
  515. getElement()->setFillType (oldState, false);
  516. return true;
  517. }
  518. private:
  519. JucerFillType newState, oldState;
  520. };
  521. void ColouredElement::setFillType (const JucerFillType& newType, const bool undoable)
  522. {
  523. if (fillType != newType)
  524. {
  525. if (undoable)
  526. {
  527. perform (new FillTypeChangeAction (this, newType),
  528. "Change fill type");
  529. }
  530. else
  531. {
  532. repaint();
  533. if (fillType.mode != newType.mode)
  534. {
  535. owner->getSelectedElements().changed();
  536. siblingComponentsChanged();
  537. }
  538. fillType = newType;
  539. changed();
  540. }
  541. }
  542. }
  543. //==============================================================================
  544. bool ColouredElement::isStrokeEnabled() const noexcept
  545. {
  546. return isStrokePresent && showOutline;
  547. }
  548. class StrokeEnableChangeAction : public PaintElementUndoableAction <ColouredElement>
  549. {
  550. public:
  551. StrokeEnableChangeAction (ColouredElement* const element, const bool newState_)
  552. : PaintElementUndoableAction <ColouredElement> (element),
  553. newState (newState_)
  554. {
  555. oldState = element->isStrokeEnabled();
  556. }
  557. bool perform()
  558. {
  559. showCorrectTab();
  560. getElement()->enableStroke (newState, false);
  561. return true;
  562. }
  563. bool undo()
  564. {
  565. showCorrectTab();
  566. getElement()->enableStroke (oldState, false);
  567. return true;
  568. }
  569. private:
  570. bool newState, oldState;
  571. };
  572. void ColouredElement::enableStroke (bool enable, const bool undoable)
  573. {
  574. enable = enable && showOutline;
  575. if (isStrokePresent != enable)
  576. {
  577. if (undoable)
  578. {
  579. perform (new StrokeEnableChangeAction (this, enable),
  580. "Change stroke mode");
  581. }
  582. else
  583. {
  584. repaint();
  585. isStrokePresent = enable;
  586. siblingComponentsChanged();
  587. owner->changed();
  588. owner->getSelectedElements().changed();
  589. }
  590. }
  591. }
  592. //==============================================================================
  593. const StrokeType& ColouredElement::getStrokeType() noexcept
  594. {
  595. return strokeType;
  596. }
  597. class StrokeTypeChangeAction : public PaintElementUndoableAction <ColouredElement>
  598. {
  599. public:
  600. StrokeTypeChangeAction (ColouredElement* const element, const PathStrokeType& newState_)
  601. : PaintElementUndoableAction <ColouredElement> (element),
  602. newState (newState_),
  603. oldState (element->getStrokeType().stroke)
  604. {
  605. }
  606. bool perform()
  607. {
  608. showCorrectTab();
  609. getElement()->setStrokeType (newState, false);
  610. return true;
  611. }
  612. bool undo()
  613. {
  614. showCorrectTab();
  615. getElement()->setStrokeType (oldState, false);
  616. return true;
  617. }
  618. private:
  619. PathStrokeType newState, oldState;
  620. };
  621. void ColouredElement::setStrokeType (const PathStrokeType& newType, const bool undoable)
  622. {
  623. if (strokeType.stroke != newType)
  624. {
  625. if (undoable)
  626. {
  627. perform (new StrokeTypeChangeAction (this, newType),
  628. "Change stroke type");
  629. }
  630. else
  631. {
  632. repaint();
  633. strokeType.stroke = newType;
  634. changed();
  635. }
  636. }
  637. }
  638. class StrokeFillTypeChangeAction : public PaintElementUndoableAction <ColouredElement>
  639. {
  640. public:
  641. StrokeFillTypeChangeAction (ColouredElement* const element, const JucerFillType& newState_)
  642. : PaintElementUndoableAction <ColouredElement> (element),
  643. newState (newState_)
  644. {
  645. oldState = element->getStrokeType().fill;
  646. }
  647. bool perform()
  648. {
  649. showCorrectTab();
  650. getElement()->setStrokeFill (newState, false);
  651. return true;
  652. }
  653. bool undo()
  654. {
  655. showCorrectTab();
  656. getElement()->setStrokeFill (oldState, false);
  657. return true;
  658. }
  659. private:
  660. JucerFillType newState, oldState;
  661. };
  662. void ColouredElement::setStrokeFill (const JucerFillType& newType, const bool undoable)
  663. {
  664. if (strokeType.fill != newType)
  665. {
  666. if (undoable)
  667. {
  668. perform (new StrokeFillTypeChangeAction (this, newType),
  669. "Change stroke fill type");
  670. }
  671. else
  672. {
  673. repaint();
  674. if (strokeType.fill.mode != newType.mode)
  675. {
  676. siblingComponentsChanged();
  677. owner->getSelectedElements().changed();
  678. }
  679. strokeType.fill = newType;
  680. changed();
  681. }
  682. }
  683. }
  684. //==============================================================================
  685. void ColouredElement::createSiblingComponents()
  686. {
  687. {
  688. GradientPointComponent* g1 = new GradientPointComponent (this, false, true);
  689. siblingComponents.add (g1);
  690. GradientPointComponent* g2 = new GradientPointComponent (this, false, false);
  691. siblingComponents.add (g2);
  692. getParentComponent()->addAndMakeVisible (g1);
  693. getParentComponent()->addAndMakeVisible (g2);
  694. g1->updatePosition();
  695. g2->updatePosition();
  696. }
  697. if (isStrokePresent && showOutline)
  698. {
  699. GradientPointComponent* g1 = new GradientPointComponent (this, true, true);
  700. siblingComponents.add (g1);
  701. GradientPointComponent* g2 = new GradientPointComponent (this, true, false);
  702. siblingComponents.add (g2);
  703. getParentComponent()->addAndMakeVisible (g1);
  704. getParentComponent()->addAndMakeVisible (g2);
  705. g1->updatePosition();
  706. g2->updatePosition();
  707. }
  708. }
  709. Rectangle<int> ColouredElement::getCurrentBounds (const Rectangle<int>& parentArea) const
  710. {
  711. int borderSize = 0;
  712. if (isStrokePresent)
  713. borderSize = (int) strokeType.stroke.getStrokeThickness() / 2 + 1;
  714. return position.getRectangle (parentArea, getDocument()->getComponentLayout())
  715. .expanded (borderSize);
  716. }
  717. void ColouredElement::setCurrentBounds (const Rectangle<int>& newBounds,
  718. const Rectangle<int>& parentArea,
  719. const bool undoable)
  720. {
  721. Rectangle<int> r (newBounds);
  722. if (isStrokePresent)
  723. {
  724. r = r.expanded (-((int) strokeType.stroke.getStrokeThickness() / 2 + 1));
  725. r.setSize (jmax (1, r.getWidth()), jmax (1, r.getHeight()));
  726. }
  727. RelativePositionedRectangle pr (position);
  728. pr.updateFrom (r.getX() - parentArea.getX(),
  729. r.getY() - parentArea.getY(),
  730. r.getWidth(), r.getHeight(),
  731. Rectangle<int> (0, 0, parentArea.getWidth(), parentArea.getHeight()),
  732. getDocument()->getComponentLayout());
  733. setPosition (pr, undoable);
  734. updateBounds (parentArea);
  735. }
  736. //==============================================================================
  737. void ColouredElement::addColourAttributes (XmlElement* const e) const
  738. {
  739. e->setAttribute ("fill", fillType.toString());
  740. e->setAttribute ("hasStroke", isStrokePresent);
  741. if (isStrokePresent && showOutline)
  742. {
  743. e->setAttribute ("stroke", strokeType.toString());
  744. e->setAttribute ("strokeColour", strokeType.fill.toString());
  745. }
  746. }
  747. bool ColouredElement::loadColourAttributes (const XmlElement& xml)
  748. {
  749. fillType.restoreFromString (xml.getStringAttribute ("fill", String()));
  750. isStrokePresent = showOutline && xml.getBoolAttribute ("hasStroke", false);
  751. strokeType.restoreFromString (xml.getStringAttribute ("stroke", String()));
  752. strokeType.fill.restoreFromString (xml.getStringAttribute ("strokeColour", String()));
  753. return true;
  754. }
  755. //==============================================================================
  756. void ColouredElement::convertToNewPathElement (const Path& path)
  757. {
  758. if (! path.isEmpty())
  759. {
  760. PaintElementPath newElement (getOwner());
  761. newElement.setToPath (path);
  762. newElement.setFillType (fillType, false);
  763. newElement.enableStroke (isStrokeEnabled(), false);
  764. newElement.setStrokeType (getStrokeType().stroke, false);
  765. newElement.setStrokeFill (getStrokeType().fill, false);
  766. ScopedPointer<XmlElement> xml (newElement.createXml());
  767. PaintElement* e = getOwner()->addElementFromXml (*xml, getOwner()->indexOfElement (this), true);
  768. getOwner()->getSelectedElements().selectOnly (e);
  769. getOwner()->removeElement (this, true);
  770. }
  771. }