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.

441 lines
13KB

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