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.

350 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class PropertyGroupComponent : public Component
  18. {
  19. public:
  20. PropertyGroupComponent() {}
  21. void setProperties (const PropertyListBuilder& newProps)
  22. {
  23. properties.clear();
  24. properties.addArray (newProps.components);
  25. for (int i = properties.size(); --i >= 0;)
  26. addAndMakeVisible (properties.getUnchecked(i));
  27. }
  28. int updateSize (int x, int y, int width)
  29. {
  30. int height = 38;
  31. for (int i = 0; i < properties.size(); ++i)
  32. {
  33. PropertyComponent* pp = properties.getUnchecked(i);
  34. pp->setBounds (10, height, width - 20, pp->getPreferredHeight());
  35. height += pp->getHeight();
  36. }
  37. height += 16;
  38. setBounds (x, y, width, height);
  39. return height;
  40. }
  41. void paint (Graphics& g) override
  42. {
  43. const Colour bkg (findColour (mainBackgroundColourId));
  44. g.setColour (Colours::white.withAlpha (0.35f));
  45. g.fillRect (0, 30, getWidth(), getHeight() - 38);
  46. g.setFont (Font (15.0f, Font::bold));
  47. g.setColour (bkg.contrasting (0.7f));
  48. g.drawFittedText (getName(), 12, 0, getWidth() - 16, 25, Justification::bottomLeft, 1);
  49. }
  50. OwnedArray<PropertyComponent> properties;
  51. private:
  52. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent)
  53. };
  54. //==============================================================================
  55. class RolloverHelpComp : public Component,
  56. private Timer
  57. {
  58. public:
  59. RolloverHelpComp() : lastComp (nullptr)
  60. {
  61. setInterceptsMouseClicks (false, false);
  62. startTimer (150);
  63. }
  64. void paint (Graphics& g) override
  65. {
  66. AttributedString s;
  67. s.setJustification (Justification::centredLeft);
  68. s.append (lastTip, Font (14.0f), findColour (mainBackgroundColourId).contrasting (0.7f));
  69. TextLayout tl;
  70. tl.createLayoutWithBalancedLineLengths (s, getWidth() - 10.0f);
  71. if (tl.getNumLines() > 3)
  72. tl.createLayout (s, getWidth() - 10.0f);
  73. tl.draw (g, getLocalBounds().toFloat());
  74. }
  75. private:
  76. Component* lastComp;
  77. String lastTip;
  78. void timerCallback() override
  79. {
  80. Component* newComp = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  81. if (newComp != nullptr
  82. && (newComp->getTopLevelComponent() != getTopLevelComponent()
  83. || newComp->isCurrentlyBlockedByAnotherModalComponent()))
  84. newComp = nullptr;
  85. if (newComp != lastComp)
  86. {
  87. lastComp = newComp;
  88. String newTip (findTip (newComp));
  89. if (newTip != lastTip)
  90. {
  91. lastTip = newTip;
  92. repaint();
  93. }
  94. }
  95. }
  96. static String findTip (Component* c)
  97. {
  98. while (c != nullptr)
  99. {
  100. if (TooltipClient* tc = dynamic_cast<TooltipClient*> (c))
  101. {
  102. const String tip (tc->getTooltip());
  103. if (tip.isNotEmpty())
  104. return tip;
  105. }
  106. c = c->getParentComponent();
  107. }
  108. return String();
  109. }
  110. };
  111. //==============================================================================
  112. class ConfigTreeItemBase : public JucerTreeViewBase,
  113. public ValueTree::Listener
  114. {
  115. public:
  116. ConfigTreeItemBase() {}
  117. void showSettingsPage (Component* content)
  118. {
  119. content->setComponentID (getUniqueName());
  120. ScopedPointer<Component> comp (content);
  121. if (ProjectContentComponent* pcc = getProjectContentComponent())
  122. pcc->setEditorComponent (new PropertyPanelViewport (comp.release()), nullptr);
  123. }
  124. void closeSettingsPage()
  125. {
  126. if (ProjectContentComponent* pcc = getProjectContentComponent())
  127. {
  128. if (PropertyPanelViewport* ppv = dynamic_cast<PropertyPanelViewport*> (pcc->getEditorComponent()))
  129. if (ppv->viewport.getViewedComponent()->getComponentID() == getUniqueName())
  130. pcc->hideEditor();
  131. }
  132. }
  133. void deleteAllSelectedItems() override
  134. {
  135. TreeView* const tree = getOwnerView();
  136. jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled
  137. if (ConfigTreeItemBase* s = dynamic_cast<ConfigTreeItemBase*> (tree->getSelectedItem (0)))
  138. s->deleteItem();
  139. }
  140. void itemOpennessChanged (bool isNowOpen) override
  141. {
  142. if (isNowOpen)
  143. refreshSubItems();
  144. }
  145. void valueTreePropertyChanged (ValueTree&, const Identifier&) override {}
  146. void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
  147. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
  148. void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
  149. void valueTreeParentChanged (ValueTree&) override {}
  150. virtual bool isProjectSettings() const { return false; }
  151. virtual bool isModulesList() const { return false; }
  152. static void updateSize (Component& comp, PropertyGroupComponent& group)
  153. {
  154. const int width = jmax (550, comp.getParentWidth() - 20);
  155. int y = 0;
  156. y += group.updateSize (12, y, width - 12);
  157. comp.setSize (width, y);
  158. }
  159. private:
  160. //==============================================================================
  161. struct PropertyPanelViewport : public Component
  162. {
  163. PropertyPanelViewport (Component* content)
  164. {
  165. addAndMakeVisible (viewport);
  166. addAndMakeVisible (rolloverHelp);
  167. viewport.setViewedComponent (content, true);
  168. }
  169. void paint (Graphics& g) override
  170. {
  171. ProjucerLookAndFeel::fillWithBackgroundTexture (*this, g);
  172. }
  173. void resized() override
  174. {
  175. Rectangle<int> r (getLocalBounds());
  176. rolloverHelp.setBounds (r.removeFromBottom (70).reduced (10, 0));
  177. viewport.setBounds (r);
  178. }
  179. Viewport viewport;
  180. RolloverHelpComp rolloverHelp;
  181. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyPanelViewport)
  182. };
  183. };
  184. //==============================================================================
  185. class RootItem : public ConfigTreeItemBase
  186. {
  187. public:
  188. RootItem (Project& p)
  189. : project (p), exportersTree (p.getExporters())
  190. {
  191. exportersTree.addListener (this);
  192. }
  193. bool isRoot() const override { return true; }
  194. bool isProjectSettings() const override { return true; }
  195. String getRenamingName() const override { return getDisplayName(); }
  196. String getDisplayName() const override { return project.getTitle(); }
  197. void setName (const String&) override {}
  198. bool isMissing() override { return false; }
  199. Icon getIcon() const override { return project.getMainGroup().getIcon().withContrastingColourTo (getBackgroundColour()); }
  200. void showDocument() override { showSettingsPage (new SettingsComp (project)); }
  201. bool canBeSelected() const override { return true; }
  202. bool mightContainSubItems() override { return project.getNumExporters() > 0; }
  203. String getUniqueName() const override { return "config_root"; }
  204. void addSubItems() override
  205. {
  206. addSubItem (new EnabledModulesItem (project));
  207. ProjucerApplication::getApp().addLiveBuildConfigItem (project, *this);
  208. int i = 0;
  209. for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
  210. addSubItem (new ExporterItem (project, exporter.exporter.release(), i));
  211. }
  212. void showPopupMenu() override
  213. {
  214. if (ProjectContentComponent* pcc = getProjectContentComponent())
  215. pcc->showNewExporterMenu();
  216. }
  217. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
  218. {
  219. return dragSourceDetails.description.toString().startsWith (getUniqueName());
  220. }
  221. void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
  222. {
  223. int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
  224. exportersTree.moveChild (oldIndex, jmax (0, insertIndex - 1), project.getUndoManagerFor (exportersTree));
  225. }
  226. //==============================================================================
  227. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
  228. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
  229. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
  230. void refreshIfNeeded (ValueTree& changedTree)
  231. {
  232. if (changedTree == exportersTree)
  233. refreshSubItems();
  234. }
  235. private:
  236. Project& project;
  237. ValueTree exportersTree;
  238. //==============================================================================
  239. class SettingsComp : public Component,
  240. private ChangeListener
  241. {
  242. public:
  243. SettingsComp (Project& p) : project (p)
  244. {
  245. addAndMakeVisible (group);
  246. updatePropertyList();
  247. project.addChangeListener (this);
  248. }
  249. ~SettingsComp()
  250. {
  251. project.removeChangeListener (this);
  252. }
  253. void parentSizeChanged() override
  254. {
  255. updateSize (*this, group);
  256. }
  257. void updatePropertyList()
  258. {
  259. PropertyListBuilder props;
  260. project.createPropertyEditors (props);
  261. group.setProperties (props);
  262. group.setName ("Project Settings");
  263. lastProjectType = project.getProjectTypeValue().getValue();
  264. parentSizeChanged();
  265. }
  266. void changeListenerCallback (ChangeBroadcaster*) override
  267. {
  268. if (lastProjectType != project.getProjectTypeValue().getValue())
  269. updatePropertyList();
  270. }
  271. private:
  272. Project& project;
  273. var lastProjectType;
  274. PropertyGroupComponent group;
  275. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  276. };
  277. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RootItem)
  278. };