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.

911 lines
30KB

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