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.

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