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.

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