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.

369 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. #pragma once
  14. //==============================================================================
  15. class ExporterItem : public ProjectTreeItemBase,
  16. private Value::Listener
  17. {
  18. public:
  19. ExporterItem (Project& p, ProjectExporter* e, int index)
  20. : project (p), exporter (e), configListTree (exporter->getConfigurations()),
  21. exporterIndex (index)
  22. {
  23. exporter->initialiseDependencyPathValues();
  24. configListTree.addListener (this);
  25. targetLocationValue.referTo (exporter->getTargetLocationValue());
  26. targetLocationValue.addListener (this);
  27. }
  28. int getItemHeight() const override { return 25; }
  29. bool canBeSelected() const override { return true; }
  30. bool mightContainSubItems() override { return exporter->getNumConfigurations() > 0; }
  31. String getUniqueName() const override { return "exporter_" + String (exporterIndex); }
  32. String getRenamingName() const override { return getDisplayName(); }
  33. String getDisplayName() const override { return exporter->getName(); }
  34. void setName (const String&) override {}
  35. bool isMissing() const override { return false; }
  36. String getTooltip() override { return getDisplayName(); }
  37. static Icon getIconForExporter (ProjectExporter* e)
  38. {
  39. if (e != nullptr)
  40. {
  41. if (e->isXcode()) return Icon (getIcons().xcode, Colours::transparentBlack);
  42. else if (e->isVisualStudio()) return Icon (getIcons().visualStudio, Colours::transparentBlack);
  43. else if (e->isAndroid()) return Icon (getIcons().android, Colours::transparentBlack);
  44. else if (e->isCodeBlocks()) return Icon (getIcons().codeBlocks, Colours::transparentBlack);
  45. else if (e->isMakefile()) return Icon (getIcons().linux, Colours::transparentBlack);
  46. else if (e->isCLion()) return Icon (getIcons().clion, Colours::transparentBlack);
  47. }
  48. return Icon();
  49. }
  50. Icon getIcon() const override
  51. {
  52. return getIconForExporter (exporter.get()).withColour (getContentColour (true));
  53. }
  54. void showDocument() override
  55. {
  56. showSettingsPage (new SettingsComp (*exporter));
  57. }
  58. void deleteItem() override
  59. {
  60. if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Delete Exporter",
  61. "Are you sure you want to delete this export target?"))
  62. {
  63. closeSettingsPage();
  64. ValueTree parent (exporter->settings.getParent());
  65. parent.removeChild (exporter->settings, project.getUndoManagerFor (parent));
  66. }
  67. }
  68. void addSubItems() override
  69. {
  70. for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
  71. addSubItem (new ConfigItem (config.config, *exporter));
  72. }
  73. void showPopupMenu() override
  74. {
  75. PopupMenu menu;
  76. menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
  77. menu.addItem (2, "Save this exporter");
  78. menu.addSeparator();
  79. menu.addItem (3, "Delete this exporter");
  80. launchPopupMenu (menu);
  81. }
  82. void showAddMenu() override
  83. {
  84. PopupMenu menu;
  85. menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
  86. launchPopupMenu (menu);
  87. }
  88. void handlePopupMenuResult (int resultCode) override
  89. {
  90. if (resultCode == 1)
  91. exporter->addNewConfiguration (false);
  92. else if (resultCode == 2)
  93. project.saveProject (exporter.get());
  94. else if (resultCode == 3)
  95. deleteAllSelectedItems();
  96. }
  97. var getDragSourceDescription() override
  98. {
  99. return getParentItem()->getUniqueName() + "/" + String (exporterIndex);
  100. }
  101. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
  102. {
  103. return dragSourceDetails.description.toString().startsWith (getUniqueName());
  104. }
  105. void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
  106. {
  107. auto oldIndex = indexOfConfig (dragSourceDetails.description.toString().fromLastOccurrenceOf ("||", false, false));
  108. if (oldIndex >= 0)
  109. configListTree.moveChild (oldIndex, insertIndex, project.getUndoManagerFor (configListTree));
  110. }
  111. int indexOfConfig (const String& configName)
  112. {
  113. int i = 0;
  114. for (ProjectExporter::ConfigIterator config (*exporter); config.next(); ++i)
  115. if (config->getName() == configName)
  116. return i;
  117. return -1;
  118. }
  119. //==============================================================================
  120. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
  121. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
  122. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
  123. void refreshIfNeeded (ValueTree& changedTree)
  124. {
  125. if (changedTree == configListTree)
  126. refreshSubItems();
  127. }
  128. private:
  129. Project& project;
  130. std::unique_ptr<ProjectExporter> exporter;
  131. ValueTree configListTree;
  132. int exporterIndex;
  133. Value targetLocationValue;
  134. void valueChanged (Value& value) override
  135. {
  136. if (value == exporter->getTargetLocationValue())
  137. refreshSubItems();
  138. }
  139. //==============================================================================
  140. struct SettingsComp : public Component
  141. {
  142. SettingsComp (ProjectExporter& exp)
  143. : group (exp.getName(),
  144. ExporterItem::getIconForExporter (&exp),
  145. exp.getDescription())
  146. {
  147. addAndMakeVisible (group);
  148. PropertyListBuilder props;
  149. exp.createPropertyEditors (props);
  150. group.setProperties (props);
  151. parentSizeChanged();
  152. }
  153. void parentSizeChanged() override { updateSize (*this, group); }
  154. void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
  155. PropertyGroupComponent group;
  156. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  157. };
  158. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
  159. };
  160. //==============================================================================
  161. class ConfigItem : public ProjectTreeItemBase
  162. {
  163. public:
  164. ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& conf, ProjectExporter& e)
  165. : config (conf), exporter (e), configTree (config->config)
  166. {
  167. jassert (config != nullptr);
  168. configTree.addListener (this);
  169. }
  170. bool isMissing() const override { return false; }
  171. bool canBeSelected() const override { return true; }
  172. bool mightContainSubItems() override { return false; }
  173. String getUniqueName() const override { return "config_" + config->getName(); }
  174. String getRenamingName() const override { return getDisplayName(); }
  175. String getDisplayName() const override { return config->getName(); }
  176. void setName (const String&) override {}
  177. Icon getIcon() const override { return Icon (getIcons().config, getContentColour (true)); }
  178. void itemOpennessChanged (bool) override {}
  179. void showDocument() override
  180. {
  181. showSettingsPage (new SettingsComp (*config));
  182. }
  183. void deleteItem() override
  184. {
  185. if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Delete Configuration",
  186. "Are you sure you want to delete this configuration?"))
  187. {
  188. closeSettingsPage();
  189. config->removeFromExporter();
  190. }
  191. }
  192. void showPopupMenu() override
  193. {
  194. bool enabled = exporter.supportsUserDefinedConfigurations();
  195. PopupMenu menu;
  196. menu.addItem (1, "Create a copy of this configuration", enabled);
  197. menu.addSeparator();
  198. menu.addItem (2, "Delete this configuration", enabled);
  199. launchPopupMenu (menu);
  200. }
  201. void handlePopupMenuResult (int resultCode) override
  202. {
  203. if (resultCode == 1)
  204. exporter.addNewConfigurationFromExisting (*config);
  205. else if (resultCode == 2)
  206. deleteAllSelectedItems();
  207. }
  208. var getDragSourceDescription() override
  209. {
  210. return getParentItem()->getUniqueName() + "||" + config->getName();
  211. }
  212. void valueTreePropertyChanged (ValueTree&, const Identifier&) override { repaintItem(); }
  213. private:
  214. ProjectExporter::BuildConfiguration::Ptr config;
  215. ProjectExporter& exporter;
  216. ValueTree configTree;
  217. //==============================================================================
  218. class SettingsComp : public Component
  219. {
  220. public:
  221. SettingsComp (ProjectExporter::BuildConfiguration& conf)
  222. : group (conf.exporter.getName() + " - " + conf.getName(), Icon (getIcons().config, Colours::transparentBlack))
  223. {
  224. addAndMakeVisible (group);
  225. PropertyListBuilder props;
  226. conf.createPropertyEditors (props);
  227. group.setProperties (props);
  228. parentSizeChanged();
  229. }
  230. void parentSizeChanged() override { updateSize (*this, group); }
  231. void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
  232. private:
  233. PropertyGroupComponent group;
  234. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  235. };
  236. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
  237. };
  238. //==============================================================================
  239. class ExportersTreeRoot : public ProjectTreeItemBase
  240. {
  241. public:
  242. ExportersTreeRoot (Project& p)
  243. : project (p),
  244. exportersTree (project.getExporters())
  245. {
  246. exportersTree.addListener (this);
  247. }
  248. bool isRoot() const override { return true; }
  249. bool canBeSelected() const override { return true; }
  250. bool isMissing() const override { return false; }
  251. bool mightContainSubItems() override { return project.getNumExporters() > 0; }
  252. String getUniqueName() const override { return "exporters"; }
  253. String getRenamingName() const override { return getDisplayName(); }
  254. String getDisplayName() const override { return "Exporters"; }
  255. void setName (const String&) override {}
  256. Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); }
  257. void showPopupMenu() override
  258. {
  259. if (auto* pcc = getProjectContentComponent())
  260. pcc->showNewExporterMenu();
  261. }
  262. void addSubItems() override
  263. {
  264. int i = 0;
  265. for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
  266. addSubItem (new TreeItemTypes::ExporterItem (project, exporter.exporter.release(), i));
  267. }
  268. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
  269. {
  270. return dragSourceDetails.description.toString().startsWith (getUniqueName());
  271. }
  272. void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
  273. {
  274. int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
  275. exportersTree.moveChild (oldIndex, jmax (0, insertIndex), project.getUndoManagerFor (exportersTree));
  276. }
  277. void itemOpennessChanged (bool isNowOpen) override
  278. {
  279. if (isNowOpen)
  280. refreshSubItems();
  281. }
  282. void removeExporter (int index)
  283. {
  284. if (auto* exporter = dynamic_cast<TreeItemTypes::ExporterItem*> (getSubItem (index)))
  285. exporter->deleteItem();
  286. }
  287. private:
  288. Project& project;
  289. ValueTree exportersTree;
  290. //==============================================================================
  291. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
  292. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
  293. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
  294. void refreshIfNeeded (ValueTree& changedTree)
  295. {
  296. if (changedTree == exportersTree)
  297. refreshSubItems();
  298. }
  299. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExportersTreeRoot)
  300. };