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.

398 lines
11KB

  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. class PropertyPanel::SectionComponent : public Component
  19. {
  20. public:
  21. SectionComponent (const String& sectionTitle,
  22. const Array <PropertyComponent*>& newProperties,
  23. const bool sectionIsOpen_)
  24. : Component (sectionTitle),
  25. titleHeight (sectionTitle.isNotEmpty() ? 22 : 0),
  26. sectionIsOpen (sectionIsOpen_)
  27. {
  28. propertyComps.addArray (newProperties);
  29. for (int i = propertyComps.size(); --i >= 0;)
  30. {
  31. addAndMakeVisible (propertyComps.getUnchecked(i));
  32. propertyComps.getUnchecked(i)->refresh();
  33. }
  34. }
  35. ~SectionComponent()
  36. {
  37. propertyComps.clear();
  38. }
  39. void paint (Graphics& g)
  40. {
  41. if (titleHeight > 0)
  42. getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen(), getWidth(), titleHeight);
  43. }
  44. void resized()
  45. {
  46. int y = titleHeight;
  47. for (int i = 0; i < propertyComps.size(); ++i)
  48. {
  49. PropertyComponent* const pec = propertyComps.getUnchecked (i);
  50. pec->setBounds (1, y, getWidth() - 2, pec->getPreferredHeight());
  51. y = pec->getBottom();
  52. }
  53. }
  54. int getPreferredHeight() const
  55. {
  56. int y = titleHeight;
  57. if (isOpen())
  58. {
  59. for (int i = propertyComps.size(); --i >= 0;)
  60. y += propertyComps.getUnchecked(i)->getPreferredHeight();
  61. }
  62. return y;
  63. }
  64. void setOpen (const bool open)
  65. {
  66. if (sectionIsOpen != open)
  67. {
  68. sectionIsOpen = open;
  69. for (int i = propertyComps.size(); --i >= 0;)
  70. propertyComps.getUnchecked(i)->setVisible (open);
  71. PropertyPanel* const pp = findParentComponentOfClass<PropertyPanel>();
  72. if (pp != nullptr)
  73. pp->resized();
  74. }
  75. }
  76. bool isOpen() const
  77. {
  78. return sectionIsOpen;
  79. }
  80. void refreshAll() const
  81. {
  82. for (int i = propertyComps.size(); --i >= 0;)
  83. propertyComps.getUnchecked (i)->refresh();
  84. }
  85. void mouseUp (const MouseEvent& e)
  86. {
  87. if (e.getMouseDownX() < titleHeight
  88. && e.x < titleHeight
  89. && e.y < titleHeight
  90. && e.getNumberOfClicks() != 2)
  91. {
  92. setOpen (! isOpen());
  93. }
  94. }
  95. void mouseDoubleClick (const MouseEvent& e)
  96. {
  97. if (e.y < titleHeight)
  98. setOpen (! isOpen());
  99. }
  100. private:
  101. OwnedArray <PropertyComponent> propertyComps;
  102. int titleHeight;
  103. bool sectionIsOpen;
  104. JUCE_DECLARE_NON_COPYABLE (SectionComponent);
  105. };
  106. //==============================================================================
  107. class PropertyPanel::PropertyHolderComponent : public Component
  108. {
  109. public:
  110. PropertyHolderComponent() {}
  111. void paint (Graphics&) {}
  112. void updateLayout (int width)
  113. {
  114. int y = 0;
  115. for (int i = 0; i < sections.size(); ++i)
  116. {
  117. SectionComponent* const section = sections.getUnchecked(i);
  118. section->setBounds (0, y, width, section->getPreferredHeight());
  119. y = section->getBottom();
  120. }
  121. setSize (width, y);
  122. repaint();
  123. }
  124. void refreshAll() const
  125. {
  126. for (int i = 0; i < sections.size(); ++i)
  127. sections.getUnchecked(i)->refreshAll();
  128. }
  129. void clear()
  130. {
  131. sections.clear();
  132. }
  133. void addSection (SectionComponent* newSection)
  134. {
  135. sections.add (newSection);
  136. addAndMakeVisible (newSection, 0);
  137. }
  138. int getNumSections() const noexcept { return sections.size(); }
  139. SectionComponent* getSection (const int index) const { return sections [index]; }
  140. private:
  141. OwnedArray<SectionComponent> sections;
  142. JUCE_DECLARE_NON_COPYABLE (PropertyHolderComponent);
  143. };
  144. //==============================================================================
  145. PropertyPanel::PropertyPanel()
  146. {
  147. messageWhenEmpty = TRANS("(nothing selected)");
  148. addAndMakeVisible (&viewport);
  149. viewport.setViewedComponent (propertyHolderComponent = new PropertyHolderComponent());
  150. viewport.setFocusContainer (true);
  151. }
  152. PropertyPanel::~PropertyPanel()
  153. {
  154. clear();
  155. }
  156. //==============================================================================
  157. void PropertyPanel::paint (Graphics& g)
  158. {
  159. if (propertyHolderComponent->getNumSections() == 0)
  160. {
  161. g.setColour (Colours::black.withAlpha (0.5f));
  162. g.setFont (14.0f);
  163. g.drawText (messageWhenEmpty, getLocalBounds().withHeight (30),
  164. Justification::centred, true);
  165. }
  166. }
  167. void PropertyPanel::resized()
  168. {
  169. viewport.setBounds (getLocalBounds());
  170. updatePropHolderLayout();
  171. }
  172. //==============================================================================
  173. void PropertyPanel::clear()
  174. {
  175. if (propertyHolderComponent->getNumSections() > 0)
  176. {
  177. propertyHolderComponent->clear();
  178. repaint();
  179. }
  180. }
  181. void PropertyPanel::addProperties (const Array <PropertyComponent*>& newProperties)
  182. {
  183. if (propertyHolderComponent->getNumSections() == 0)
  184. repaint();
  185. propertyHolderComponent->addSection (new SectionComponent (String::empty, newProperties, true));
  186. updatePropHolderLayout();
  187. }
  188. void PropertyPanel::addSection (const String& sectionTitle,
  189. const Array <PropertyComponent*>& newProperties,
  190. const bool shouldBeOpen)
  191. {
  192. jassert (sectionTitle.isNotEmpty());
  193. if (propertyHolderComponent->getNumSections() == 0)
  194. repaint();
  195. propertyHolderComponent->addSection (new SectionComponent (sectionTitle, newProperties, shouldBeOpen));
  196. updatePropHolderLayout();
  197. }
  198. void PropertyPanel::updatePropHolderLayout() const
  199. {
  200. const int maxWidth = viewport.getMaximumVisibleWidth();
  201. propertyHolderComponent->updateLayout (maxWidth);
  202. const int newMaxWidth = viewport.getMaximumVisibleWidth();
  203. if (maxWidth != newMaxWidth)
  204. {
  205. // need to do this twice because of scrollbars changing the size, etc.
  206. propertyHolderComponent->updateLayout (newMaxWidth);
  207. }
  208. }
  209. void PropertyPanel::refreshAll() const
  210. {
  211. propertyHolderComponent->refreshAll();
  212. }
  213. //==============================================================================
  214. StringArray PropertyPanel::getSectionNames() const
  215. {
  216. StringArray s;
  217. for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
  218. {
  219. SectionComponent* const section = propertyHolderComponent->getSection (i);
  220. if (section->getName().isNotEmpty())
  221. s.add (section->getName());
  222. }
  223. return s;
  224. }
  225. bool PropertyPanel::isSectionOpen (const int sectionIndex) const
  226. {
  227. int index = 0;
  228. for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
  229. {
  230. SectionComponent* const section = propertyHolderComponent->getSection (i);
  231. if (section->getName().isNotEmpty())
  232. {
  233. if (index == sectionIndex)
  234. return section->isOpen();
  235. ++index;
  236. }
  237. }
  238. return false;
  239. }
  240. void PropertyPanel::setSectionOpen (const int sectionIndex, const bool shouldBeOpen)
  241. {
  242. int index = 0;
  243. for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
  244. {
  245. SectionComponent* const section = propertyHolderComponent->getSection (i);
  246. if (section->getName().isNotEmpty())
  247. {
  248. if (index == sectionIndex)
  249. {
  250. section->setOpen (shouldBeOpen);
  251. break;
  252. }
  253. ++index;
  254. }
  255. }
  256. }
  257. void PropertyPanel::setSectionEnabled (const int sectionIndex, const bool shouldBeEnabled)
  258. {
  259. int index = 0;
  260. for (int i = 0; i < propertyHolderComponent->getNumSections(); ++i)
  261. {
  262. SectionComponent* const section = propertyHolderComponent->getSection (i);
  263. if (section->getName().isNotEmpty())
  264. {
  265. if (index == sectionIndex)
  266. {
  267. section->setEnabled (shouldBeEnabled);
  268. break;
  269. }
  270. ++index;
  271. }
  272. }
  273. }
  274. //==============================================================================
  275. XmlElement* PropertyPanel::getOpennessState() const
  276. {
  277. XmlElement* const xml = new XmlElement ("PROPERTYPANELSTATE");
  278. xml->setAttribute ("scrollPos", viewport.getViewPositionY());
  279. const StringArray sections (getSectionNames());
  280. for (int i = 0; i < sections.size(); ++i)
  281. {
  282. if (sections[i].isNotEmpty())
  283. {
  284. XmlElement* const e = xml->createNewChildElement ("SECTION");
  285. e->setAttribute ("name", sections[i]);
  286. e->setAttribute ("open", isSectionOpen (i) ? 1 : 0);
  287. }
  288. }
  289. return xml;
  290. }
  291. void PropertyPanel::restoreOpennessState (const XmlElement& xml)
  292. {
  293. if (xml.hasTagName ("PROPERTYPANELSTATE"))
  294. {
  295. const StringArray sections (getSectionNames());
  296. forEachXmlChildElementWithTagName (xml, e, "SECTION")
  297. {
  298. setSectionOpen (sections.indexOf (e->getStringAttribute ("name")),
  299. e->getBoolAttribute ("open"));
  300. }
  301. viewport.setViewPosition (viewport.getViewPositionX(),
  302. xml.getIntAttribute ("scrollPos", viewport.getViewPositionY()));
  303. }
  304. }
  305. //==============================================================================
  306. void PropertyPanel::setMessageWhenEmpty (const String& newMessage)
  307. {
  308. if (messageWhenEmpty != newMessage)
  309. {
  310. messageWhenEmpty = newMessage;
  311. repaint();
  312. }
  313. }
  314. const String& PropertyPanel::getMessageWhenEmpty() const
  315. {
  316. return messageWhenEmpty;
  317. }