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.

376 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2017 - ROLI Ltd.
  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. {
  92. exporter->addNewConfiguration (false);
  93. }
  94. else if (resultCode == 2)
  95. {
  96. const ScopedValueSetter<String> valueSetter (project.specifiedExporterToSave, exporter->getName(), {});
  97. project.save (true, true);
  98. }
  99. else if (resultCode == 3)
  100. {
  101. deleteAllSelectedItems();
  102. }
  103. }
  104. var getDragSourceDescription() override
  105. {
  106. return getParentItem()->getUniqueName() + "/" + String (exporterIndex);
  107. }
  108. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
  109. {
  110. return dragSourceDetails.description.toString().startsWith (getUniqueName());
  111. }
  112. void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
  113. {
  114. auto oldIndex = indexOfConfig (dragSourceDetails.description.toString().fromLastOccurrenceOf ("||", false, false));
  115. if (oldIndex >= 0)
  116. configListTree.moveChild (oldIndex, insertIndex, project.getUndoManagerFor (configListTree));
  117. }
  118. int indexOfConfig (const String& configName)
  119. {
  120. int i = 0;
  121. for (ProjectExporter::ConfigIterator config (*exporter); config.next(); ++i)
  122. if (config->getName() == configName)
  123. return i;
  124. return -1;
  125. }
  126. //==============================================================================
  127. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
  128. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
  129. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
  130. void refreshIfNeeded (ValueTree& changedTree)
  131. {
  132. if (changedTree == configListTree)
  133. refreshSubItems();
  134. }
  135. private:
  136. Project& project;
  137. std::unique_ptr<ProjectExporter> exporter;
  138. ValueTree configListTree;
  139. int exporterIndex;
  140. Value targetLocationValue;
  141. void valueChanged (Value& value) override
  142. {
  143. if (value == exporter->getTargetLocationValue())
  144. refreshSubItems();
  145. }
  146. //==============================================================================
  147. struct SettingsComp : public Component
  148. {
  149. SettingsComp (ProjectExporter& exp)
  150. : group (exp.getName(),
  151. ExporterItem::getIconForExporter (&exp),
  152. exp.getDescription())
  153. {
  154. addAndMakeVisible (group);
  155. PropertyListBuilder props;
  156. exp.createPropertyEditors (props);
  157. group.setProperties (props);
  158. parentSizeChanged();
  159. }
  160. void parentSizeChanged() override { updateSize (*this, group); }
  161. void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
  162. PropertyGroupComponent group;
  163. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  164. };
  165. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
  166. };
  167. //==============================================================================
  168. class ConfigItem : public ProjectTreeItemBase
  169. {
  170. public:
  171. ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& conf, ProjectExporter& e)
  172. : config (conf), exporter (e), configTree (config->config)
  173. {
  174. jassert (config != nullptr);
  175. configTree.addListener (this);
  176. }
  177. bool isMissing() const override { return false; }
  178. bool canBeSelected() const override { return true; }
  179. bool mightContainSubItems() override { return false; }
  180. String getUniqueName() const override { return "config_" + config->getName(); }
  181. String getRenamingName() const override { return getDisplayName(); }
  182. String getDisplayName() const override { return config->getName(); }
  183. void setName (const String&) override {}
  184. Icon getIcon() const override { return Icon (getIcons().config, getContentColour (true)); }
  185. void itemOpennessChanged (bool) override {}
  186. void showDocument() override
  187. {
  188. showSettingsPage (new SettingsComp (*config));
  189. }
  190. void deleteItem() override
  191. {
  192. if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Delete Configuration",
  193. "Are you sure you want to delete this configuration?"))
  194. {
  195. closeSettingsPage();
  196. config->removeFromExporter();
  197. }
  198. }
  199. void showPopupMenu() override
  200. {
  201. bool enabled = exporter.supportsUserDefinedConfigurations();
  202. PopupMenu menu;
  203. menu.addItem (1, "Create a copy of this configuration", enabled);
  204. menu.addSeparator();
  205. menu.addItem (2, "Delete this configuration", enabled);
  206. launchPopupMenu (menu);
  207. }
  208. void handlePopupMenuResult (int resultCode) override
  209. {
  210. if (resultCode == 1)
  211. exporter.addNewConfigurationFromExisting (*config);
  212. else if (resultCode == 2)
  213. deleteAllSelectedItems();
  214. }
  215. var getDragSourceDescription() override
  216. {
  217. return getParentItem()->getUniqueName() + "||" + config->getName();
  218. }
  219. void valueTreePropertyChanged (ValueTree&, const Identifier&) override { repaintItem(); }
  220. private:
  221. ProjectExporter::BuildConfiguration::Ptr config;
  222. ProjectExporter& exporter;
  223. ValueTree configTree;
  224. //==============================================================================
  225. class SettingsComp : public Component
  226. {
  227. public:
  228. SettingsComp (ProjectExporter::BuildConfiguration& conf)
  229. : group (conf.exporter.getName() + " - " + conf.getName(), Icon (getIcons().config, Colours::transparentBlack))
  230. {
  231. addAndMakeVisible (group);
  232. PropertyListBuilder props;
  233. conf.createPropertyEditors (props);
  234. group.setProperties (props);
  235. parentSizeChanged();
  236. }
  237. void parentSizeChanged() override { updateSize (*this, group); }
  238. void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
  239. private:
  240. PropertyGroupComponent group;
  241. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
  242. };
  243. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
  244. };
  245. //==============================================================================
  246. class ExportersTreeRoot : public ProjectTreeItemBase
  247. {
  248. public:
  249. ExportersTreeRoot (Project& p)
  250. : project (p),
  251. exportersTree (project.getExporters())
  252. {
  253. exportersTree.addListener (this);
  254. }
  255. bool isRoot() const override { return true; }
  256. bool canBeSelected() const override { return true; }
  257. bool isMissing() const override { return false; }
  258. bool mightContainSubItems() override { return project.getNumExporters() > 0; }
  259. String getUniqueName() const override { return "exporters"; }
  260. String getRenamingName() const override { return getDisplayName(); }
  261. String getDisplayName() const override { return "Exporters"; }
  262. void setName (const String&) override {}
  263. Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); }
  264. void showPopupMenu() override
  265. {
  266. if (auto* pcc = getProjectContentComponent())
  267. pcc->showNewExporterMenu();
  268. }
  269. void addSubItems() override
  270. {
  271. int i = 0;
  272. for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
  273. addSubItem (new TreeItemTypes::ExporterItem (project, exporter.exporter.release(), i));
  274. }
  275. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
  276. {
  277. return dragSourceDetails.description.toString().startsWith (getUniqueName());
  278. }
  279. void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
  280. {
  281. int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
  282. exportersTree.moveChild (oldIndex, jmax (0, insertIndex), project.getUndoManagerFor (exportersTree));
  283. }
  284. void itemOpennessChanged (bool isNowOpen) override
  285. {
  286. if (isNowOpen)
  287. refreshSubItems();
  288. }
  289. void removeExporter (int index)
  290. {
  291. if (auto* exporter = dynamic_cast<TreeItemTypes::ExporterItem*> (getSubItem (index)))
  292. exporter->deleteItem();
  293. }
  294. private:
  295. Project& project;
  296. ValueTree exportersTree;
  297. //==============================================================================
  298. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
  299. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
  300. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
  301. void refreshIfNeeded (ValueTree& changedTree)
  302. {
  303. if (changedTree == exportersTree)
  304. refreshSubItems();
  305. }
  306. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExportersTreeRoot)
  307. };