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.

243 lines
8.0KB

  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. #pragma once
  20. #include "jucer_PaintElement.h"
  21. #include "../jucer_ObjectTypes.h"
  22. //==============================================================================
  23. class PaintElementGroup : public PaintElement
  24. {
  25. public:
  26. PaintElementGroup (PaintRoutine* pr)
  27. : PaintElement (pr, "Group")
  28. {
  29. }
  30. void ungroup (const bool undoable)
  31. {
  32. getOwner()->getSelectedElements().deselectAll();
  33. getOwner()->getSelectedPoints().deselectAll();
  34. const int index = getOwner()->indexOfElement (this);
  35. for (int i = 0; i < subElements.size(); ++i)
  36. {
  37. ScopedPointer<XmlElement> xml (subElements.getUnchecked(i)->createXml());
  38. PaintElement* newOne = getOwner()->addElementFromXml (*xml, index, undoable);
  39. getOwner()->getSelectedElements().addToSelection (newOne);
  40. }
  41. getOwner()->removeElement (this, undoable);
  42. }
  43. static void groupSelected (PaintRoutine* const routine)
  44. {
  45. if (routine->getSelectedElements().getNumSelected() > 1)
  46. {
  47. PaintElementGroup* newGroup = new PaintElementGroup (routine);
  48. int frontIndex = -1;
  49. for (int i = 0; i < routine->getNumElements(); ++i)
  50. {
  51. if (routine->getSelectedElements().isSelected (routine->getElement (i)))
  52. {
  53. ScopedPointer<XmlElement> xml (routine->getElement(i)->createXml());
  54. if (PaintElement* newOne = ObjectTypes::createElementForXml (xml, routine))
  55. newGroup->subElements.add (newOne);
  56. if (i > frontIndex)
  57. frontIndex = i;
  58. }
  59. }
  60. routine->deleteSelected();
  61. PaintElement* const g = routine->addNewElement (newGroup, frontIndex, true);
  62. routine->getSelectedElements().selectOnly (g);
  63. }
  64. }
  65. int getNumElements() const noexcept { return subElements.size(); }
  66. PaintElement* getElement (const int index) const noexcept { return subElements [index]; }
  67. int indexOfElement (const PaintElement* element) const noexcept { return subElements.indexOf (element); }
  68. bool containsElement (const PaintElement* element) const
  69. {
  70. if (subElements.contains (element))
  71. return true;
  72. for (int i = subElements.size(); --i >= 0;)
  73. if (PaintElementGroup* pg = dynamic_cast<PaintElementGroup*> (subElements.getUnchecked(i)))
  74. if (pg->containsElement (element))
  75. return true;
  76. return false;
  77. }
  78. //==============================================================================
  79. void setInitialBounds (int /*parentWidth*/, int /*parentHeight*/) override
  80. {
  81. }
  82. Rectangle<int> getCurrentBounds (const Rectangle<int>& parentArea) const override
  83. {
  84. Rectangle<int> r;
  85. if (subElements.size() > 0)
  86. {
  87. r = subElements.getUnchecked(0)->getCurrentBounds (parentArea);
  88. for (int i = 1; i < subElements.size(); ++i)
  89. r = r.getUnion (subElements.getUnchecked(i)->getCurrentBounds (parentArea));
  90. }
  91. return r;
  92. }
  93. void setCurrentBounds (const Rectangle<int>& b, const Rectangle<int>& parentArea, const bool undoable) override
  94. {
  95. Rectangle<int> newBounds (b);
  96. newBounds.setSize (jmax (1, newBounds.getWidth()),
  97. jmax (1, newBounds.getHeight()));
  98. const Rectangle<int> current (getCurrentBounds (parentArea));
  99. if (newBounds != current)
  100. {
  101. const int dx = newBounds.getX() - current.getX();
  102. const int dy = newBounds.getY() - current.getY();
  103. const double scaleStartX = current.getX();
  104. const double scaleStartY = current.getY();
  105. const double scaleX = newBounds.getWidth() / (double) current.getWidth();
  106. const double scaleY = newBounds.getHeight() / (double) current.getHeight();
  107. for (int i = 0; i < subElements.size(); ++i)
  108. {
  109. PaintElement* const e = subElements.getUnchecked(i);
  110. Rectangle<int> pos (e->getCurrentBounds (parentArea));
  111. const int newX = roundToInt ((pos.getX() - scaleStartX) * scaleX + scaleStartX + dx);
  112. const int newY = roundToInt ((pos.getY() - scaleStartY) * scaleY + scaleStartY + dy);
  113. pos.setBounds (newX, newY,
  114. roundToInt ((pos.getRight() - scaleStartX) * scaleX + scaleStartX + dx) - newX,
  115. roundToInt ((pos.getBottom() - scaleStartY) * scaleY + scaleStartY + dy) - newY);
  116. e->setCurrentBounds (pos, parentArea, undoable);
  117. }
  118. }
  119. }
  120. //==============================================================================
  121. void draw (Graphics& g, const ComponentLayout* layout, const Rectangle<int>& parentArea) override
  122. {
  123. for (int i = 0; i < subElements.size(); ++i)
  124. subElements.getUnchecked(i)->draw (g, layout, parentArea);
  125. }
  126. void getEditableProperties (Array<PropertyComponent*>& props, bool multipleSelected) override
  127. {
  128. if (! multipleSelected)
  129. props.add (new UngroupProperty (this));
  130. }
  131. void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) override
  132. {
  133. for (int i = 0; i < subElements.size(); ++i)
  134. subElements.getUnchecked(i)->fillInGeneratedCode (code, paintMethodCode);
  135. }
  136. static const char* getTagName() noexcept { return "GROUP"; }
  137. XmlElement* createXml() const override
  138. {
  139. XmlElement* e = new XmlElement (getTagName());
  140. for (int i = 0; i < subElements.size(); ++i)
  141. {
  142. XmlElement* const sub = subElements.getUnchecked(i)->createXml();
  143. e->addChildElement (sub);
  144. }
  145. return e;
  146. }
  147. bool loadFromXml (const XmlElement& xml) override
  148. {
  149. if (xml.hasTagName (getTagName()))
  150. {
  151. forEachXmlChildElement (xml, e)
  152. if (PaintElement* const pe = ObjectTypes::createElementForXml (e, owner))
  153. subElements.add (pe);
  154. return true;
  155. }
  156. jassertfalse;
  157. return false;
  158. }
  159. void applyCustomPaintSnippets (StringArray& snippets) override
  160. {
  161. for (auto* e : subElements)
  162. e->applyCustomPaintSnippets (snippets);
  163. }
  164. private:
  165. OwnedArray<PaintElement> subElements;
  166. struct UngroupProperty : public ButtonPropertyComponent
  167. {
  168. UngroupProperty (PaintElementGroup* const e)
  169. : ButtonPropertyComponent ("ungroup", false),
  170. element (e)
  171. {
  172. }
  173. void buttonClicked()
  174. {
  175. element->ungroup (true);
  176. }
  177. String getButtonText() const
  178. {
  179. return "Ungroup";
  180. }
  181. PaintElementGroup* element;
  182. };
  183. };