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.

912 lines
30KB

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