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.

469 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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. #include "../Project Saving/jucer_ProjectExporter.h"
  18. #include "../Application/jucer_Application.h"
  19. #include "jucer_ConfigPage.h"
  20. #include "jucer_ModulesPanel.h"
  21. //==============================================================================
  22. void SettingsTreeViewItemBase::showSettingsPage (Component* content)
  23. {
  24. content->setComponentID (getUniqueName());
  25. ScopedPointer<Component> comp (content);
  26. if (ProjectContentComponent* pcc = getProjectContentComponent())
  27. pcc->setEditorComponent (new PropertyPanelViewport (comp.release()), nullptr);
  28. }
  29. void SettingsTreeViewItemBase::closeSettingsPage()
  30. {
  31. if (ProjectContentComponent* pcc = getProjectContentComponent())
  32. {
  33. if (PropertyPanelViewport* ppv = dynamic_cast<PropertyPanelViewport*> (pcc->getEditorComponent()))
  34. if (ppv->viewport.getViewedComponent()->getComponentID() == getUniqueName())
  35. pcc->hideEditor();
  36. }
  37. }
  38. //==============================================================================
  39. namespace ProjectSettingsTreeClasses
  40. {
  41. class ConfigItem : public SettingsTreeViewItemBase
  42. {
  43. public:
  44. ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& config_, const String& exporterName_)
  45. : config (config_), exporterName (exporterName_), configTree (config->config)
  46. {
  47. jassert (config != nullptr);
  48. configTree.addListener (this);
  49. }
  50. bool isMissing() { return false; }
  51. bool canBeSelected() const { return true; }
  52. bool mightContainSubItems() { return false; }
  53. String getUniqueName() const { return "config_" + config->getName(); }
  54. String getRenamingName() const { return getDisplayName(); }
  55. String getDisplayName() const { return config->getName(); }
  56. void setName (const String&) {}
  57. Icon getIcon() const { return Icon (getIcons().config, getContrastingColour (Colours::green, 0.5f)); }
  58. void showDocument() { showSettingsPage (new SettingsComp (config, exporterName)); }
  59. void itemOpennessChanged (bool) {}
  60. void deleteItem()
  61. {
  62. if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Delete Configuration",
  63. "Are you sure you want to delete this configuration?"))
  64. {
  65. closeSettingsPage();
  66. config->removeFromExporter();
  67. }
  68. }
  69. void showPopupMenu()
  70. {
  71. PopupMenu menu;
  72. menu.addItem (1, "Create a copy of this configuration");
  73. menu.addSeparator();
  74. menu.addItem (2, "Delete this configuration");
  75. launchPopupMenu (menu);
  76. }
  77. void handlePopupMenuResult (int resultCode)
  78. {
  79. if (resultCode == 2)
  80. {
  81. deleteAllSelectedItems();
  82. }
  83. else if (resultCode == 1)
  84. {
  85. for (Project::ExporterIterator exporter (config->project); exporter.next();)
  86. {
  87. if (config->config.isAChildOf (exporter.exporter->settings))
  88. {
  89. exporter.exporter->addNewConfiguration (config);
  90. break;
  91. }
  92. }
  93. }
  94. }
  95. var getDragSourceDescription()
  96. {
  97. return getParentItem()->getUniqueName() + "||" + config->getName();
  98. }
  99. void valueTreePropertyChanged (ValueTree&, const Identifier&) { repaintItem(); }
  100. private:
  101. ProjectExporter::BuildConfiguration::Ptr config;
  102. String exporterName;
  103. ValueTree configTree;
  104. //==============================================================================
  105. class SettingsComp : public Component
  106. {
  107. public:
  108. SettingsComp (ProjectExporter::BuildConfiguration* conf, const String& expName)
  109. {
  110. addAndMakeVisible (&group);
  111. PropertyListBuilder props;
  112. conf->createPropertyEditors (props);
  113. group.setProperties (props);
  114. group.setName (expName + " / " + conf->getName());
  115. parentSizeChanged();
  116. }
  117. void parentSizeChanged() { updateSize (*this, group); }
  118. private:
  119. PropertyGroup group;
  120. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  121. };
  122. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
  123. };
  124. //==============================================================================
  125. class ExporterItem : public SettingsTreeViewItemBase
  126. {
  127. public:
  128. ExporterItem (Project& project_, ProjectExporter* exporter_, int exporterIndex_)
  129. : project (project_), exporter (exporter_), configListTree (exporter->getConfigurations()),
  130. exporterIndex (exporterIndex_)
  131. {
  132. configListTree.addListener (this);
  133. jassert (exporter != nullptr);
  134. }
  135. bool canBeSelected() const { return true; }
  136. bool mightContainSubItems() { return exporter->getNumConfigurations() > 0; }
  137. String getUniqueName() const { return "exporter_" + String (exporterIndex); }
  138. String getRenamingName() const { return getDisplayName(); }
  139. String getDisplayName() const { return exporter->getName(); }
  140. void setName (const String&) {}
  141. bool isMissing() { return false; }
  142. Icon getIcon() const { return Icon (getIcons().exporter, getContrastingColour (0.5f)); }
  143. void showDocument() { showSettingsPage (new SettingsComp (exporter)); }
  144. void deleteItem()
  145. {
  146. if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Delete Exporter",
  147. "Are you sure you want to delete this export target?"))
  148. {
  149. closeSettingsPage();
  150. ValueTree parent (exporter->settings.getParent());
  151. parent.removeChild (exporter->settings, project.getUndoManagerFor (parent));
  152. }
  153. }
  154. void addSubItems()
  155. {
  156. for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
  157. addSubItem (new ConfigItem (config.config, exporter->getName()));
  158. }
  159. void showPopupMenu()
  160. {
  161. PopupMenu menu;
  162. menu.addItem (1, "Add a new configuration");
  163. menu.addSeparator();
  164. menu.addItem (2, "Delete this exporter");
  165. launchPopupMenu (menu);
  166. }
  167. void handlePopupMenuResult (int resultCode)
  168. {
  169. if (resultCode == 2)
  170. deleteAllSelectedItems();
  171. else if (resultCode == 1)
  172. exporter->addNewConfiguration (nullptr);
  173. }
  174. var getDragSourceDescription()
  175. {
  176. return getParentItem()->getUniqueName() + "/" + String (exporterIndex);
  177. }
  178. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails)
  179. {
  180. return dragSourceDetails.description.toString().startsWith (getUniqueName());
  181. }
  182. void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex)
  183. {
  184. const int oldIndex = indexOfConfig (dragSourceDetails.description.toString().fromLastOccurrenceOf ("||", false, false));
  185. if (oldIndex >= 0)
  186. configListTree.moveChild (oldIndex, insertIndex, project.getUndoManagerFor (configListTree));
  187. }
  188. int indexOfConfig (const String& configName)
  189. {
  190. int i = 0;
  191. for (ProjectExporter::ConfigIterator config (*exporter); config.next(); ++i)
  192. if (config->getName() == configName)
  193. return i;
  194. return -1;
  195. }
  196. //==============================================================================
  197. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) { refreshIfNeeded (parentTree); }
  198. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) { refreshIfNeeded (parentTree); }
  199. void valueTreeChildOrderChanged (ValueTree& parentTree) { refreshIfNeeded (parentTree); }
  200. void refreshIfNeeded (ValueTree& changedTree)
  201. {
  202. if (changedTree == configListTree)
  203. refreshSubItems();
  204. }
  205. private:
  206. Project& project;
  207. ScopedPointer<ProjectExporter> exporter;
  208. ValueTree configListTree;
  209. int exporterIndex;
  210. //==============================================================================
  211. class SettingsComp : public Component
  212. {
  213. public:
  214. SettingsComp (ProjectExporter* exp)
  215. {
  216. addAndMakeVisible (&group);
  217. PropertyListBuilder props;
  218. exp->createPropertyEditors (props);
  219. group.setProperties (props);
  220. group.setName ("Export target: " + exp->getName());
  221. parentSizeChanged();
  222. }
  223. void parentSizeChanged() { updateSize (*this, group); }
  224. private:
  225. PropertyGroup group;
  226. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  227. };
  228. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
  229. };
  230. //==============================================================================
  231. class ModulesItem : public SettingsTreeViewItemBase
  232. {
  233. public:
  234. ModulesItem (Project& project_) : project (project_) {}
  235. bool canBeSelected() const { return true; }
  236. bool mightContainSubItems() { return false; }
  237. String getUniqueName() const { return "modules"; }
  238. String getRenamingName() const { return getDisplayName(); }
  239. String getDisplayName() const { return "Modules"; }
  240. void setName (const String&) {}
  241. bool isMissing() { return false; }
  242. Icon getIcon() const { return Icon (getIcons().graph, getContrastingColour (Colours::red, 0.5f)); }
  243. void showDocument() { showSettingsPage (new SettingsComp (project)); }
  244. private:
  245. Project& project;
  246. //==============================================================================
  247. class SettingsComp : public Component
  248. {
  249. public:
  250. SettingsComp (Project& project_) : project (project_)
  251. {
  252. addAndMakeVisible (&group);
  253. PropertyListBuilder props;
  254. props.add (new ModulesPanel (project));
  255. group.setProperties (props);
  256. group.setName ("Modules");
  257. parentSizeChanged();
  258. }
  259. void parentSizeChanged()
  260. {
  261. updateSize (*this, group);
  262. }
  263. private:
  264. Project& project;
  265. var lastProjectType;
  266. PropertyGroup group;
  267. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  268. };
  269. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModulesItem)
  270. };
  271. //==============================================================================
  272. class RootItem : public SettingsTreeViewItemBase
  273. {
  274. public:
  275. RootItem (Project& project_)
  276. : project (project_), exportersTree (project_.getExporters())
  277. {
  278. exportersTree.addListener (this);
  279. }
  280. String getRenamingName() const { return getDisplayName(); }
  281. String getDisplayName() const { return project.getTitle(); }
  282. void setName (const String&) {}
  283. bool isMissing() { return false; }
  284. Icon getIcon() const { return project.getMainGroup().getIcon().withContrastingColourTo (getBackgroundColour()); }
  285. void showDocument() { showSettingsPage (new SettingsComp (project)); }
  286. bool canBeSelected() const { return true; }
  287. bool mightContainSubItems() { return project.getNumExporters() > 0; }
  288. String getUniqueName() const { return "config_root"; }
  289. void addSubItems()
  290. {
  291. addSubItem (new ModulesItem (project));
  292. IntrojucerApp::getApp().addExtraConfigItems (project, *this);
  293. int i = 0;
  294. for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
  295. addSubItem (new ExporterItem (project, exporter.exporter.release(), i));
  296. }
  297. void showPopupMenu()
  298. {
  299. PopupMenu menu;
  300. const StringArray exporters (ProjectExporter::getExporterNames());
  301. for (int i = 0; i < exporters.size(); ++i)
  302. menu.addItem (i + 1, "Create a new " + exporters[i] + " target");
  303. launchPopupMenu (menu);
  304. }
  305. void handlePopupMenuResult (int resultCode)
  306. {
  307. if (resultCode > 0)
  308. {
  309. String exporterName (ProjectExporter::getExporterNames() [resultCode - 1]);
  310. if (exporterName.isNotEmpty())
  311. project.addNewExporter (exporterName);
  312. }
  313. }
  314. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails)
  315. {
  316. return dragSourceDetails.description.toString().startsWith (getUniqueName());
  317. }
  318. void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex)
  319. {
  320. int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
  321. exportersTree.moveChild (oldIndex, jmax (0, insertIndex - 1), project.getUndoManagerFor (exportersTree));
  322. }
  323. //==============================================================================
  324. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) { refreshIfNeeded (parentTree); }
  325. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) { refreshIfNeeded (parentTree); }
  326. void valueTreeChildOrderChanged (ValueTree& parentTree) { refreshIfNeeded (parentTree); }
  327. void refreshIfNeeded (ValueTree& changedTree)
  328. {
  329. if (changedTree == exportersTree)
  330. refreshSubItems();
  331. }
  332. private:
  333. Project& project;
  334. ValueTree exportersTree;
  335. //==============================================================================
  336. class SettingsComp : public Component,
  337. private ChangeListener
  338. {
  339. public:
  340. SettingsComp (Project& project_)
  341. : project (project_)
  342. {
  343. addAndMakeVisible (&group);
  344. updatePropertyList();
  345. project.addChangeListener (this);
  346. }
  347. ~SettingsComp()
  348. {
  349. project.removeChangeListener (this);
  350. }
  351. void parentSizeChanged()
  352. {
  353. updateSize (*this, group);
  354. }
  355. void updatePropertyList()
  356. {
  357. PropertyListBuilder props;
  358. project.createPropertyEditors (props);
  359. group.setProperties (props);
  360. group.setName ("Project Settings");
  361. lastProjectType = project.getProjectTypeValue().getValue();
  362. parentSizeChanged();
  363. }
  364. void changeListenerCallback (ChangeBroadcaster*)
  365. {
  366. if (lastProjectType != project.getProjectTypeValue().getValue())
  367. updatePropertyList();
  368. }
  369. private:
  370. Project& project;
  371. var lastProjectType;
  372. PropertyGroup group;
  373. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  374. };
  375. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RootItem)
  376. };
  377. }
  378. JucerTreeViewBase* createProjectConfigTreeViewRoot (Project& project)
  379. {
  380. return new ProjectSettingsTreeClasses::RootItem (project);
  381. }