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.

928 lines
30KB

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