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.

395 lines
15KB

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