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.

403 lines
15KB

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