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.

345 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - 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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-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 ModulesInformationComponent final : public Component,
  21. private ListBoxModel,
  22. private ValueTree::Listener
  23. {
  24. public:
  25. ModulesInformationComponent (Project& p)
  26. : project (p),
  27. modulesValueTree (project.getEnabledModules().getState())
  28. {
  29. auto tempHeader = std::make_unique<ListBoxHeader> (Array<String> { "Module", "Version", "Make Local Copy", "Paths" },
  30. Array<float> { 0.25f, 0.2f, 0.2f, 0.35f });
  31. listHeader = tempHeader.get();
  32. list.setHeaderComponent (std::move (tempHeader));
  33. list.setModel (this);
  34. list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
  35. addAndMakeVisible (list);
  36. list.updateContent();
  37. list.setRowHeight (30);
  38. list.setMultipleSelectionEnabled (true);
  39. addAndMakeVisible (header);
  40. addAndMakeVisible (setCopyModeButton);
  41. setCopyModeButton.setTriggeredOnMouseDown (true);
  42. setCopyModeButton.onClick = [this] { showCopyModeMenu(); };
  43. addAndMakeVisible (copyPathButton);
  44. copyPathButton.setTriggeredOnMouseDown (true);
  45. copyPathButton.onClick = [this] { showSetPathsMenu(); };
  46. addAndMakeVisible (globalPathsButton);
  47. globalPathsButton.onClick = [this] { showGlobalPathsMenu(); };
  48. modulesValueTree.addListener (this);
  49. lookAndFeelChanged();
  50. }
  51. void paint (Graphics& g) override
  52. {
  53. g.setColour (findColour (secondaryBackgroundColourId));
  54. g.fillRect (getLocalBounds().reduced (12, 0));
  55. }
  56. void resized() override
  57. {
  58. auto bounds = getLocalBounds().reduced (12, 0);
  59. header.setBounds (bounds.removeFromTop (40));
  60. bounds.reduce (10, 0);
  61. list.setBounds (bounds.removeFromTop (list.getRowPosition (getNumRows() - 1, true).getBottom() + 20));
  62. if (bounds.getHeight() < 35)
  63. {
  64. parentSizeChanged();
  65. }
  66. else
  67. {
  68. auto buttonRow = bounds.removeFromTop (35);
  69. setCopyModeButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
  70. buttonRow.removeFromLeft (8);
  71. copyPathButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
  72. buttonRow.removeFromLeft (8);
  73. globalPathsButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
  74. }
  75. }
  76. void parentSizeChanged() override
  77. {
  78. auto width = jmax (550, getParentWidth());
  79. auto y = list.getRowPosition (getNumRows() - 1, true).getBottom() + 200;
  80. y = jmax (getParentHeight(), y);
  81. setSize (width, y);
  82. }
  83. int getNumRows() override
  84. {
  85. return project.getEnabledModules().getNumModules();
  86. }
  87. void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
  88. {
  89. ignoreUnused (height);
  90. Rectangle<int> bounds (0, 0, width, height);
  91. g.setColour (rowIsSelected ? findColour (defaultHighlightColourId) : findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
  92. : secondaryWidgetBackgroundColourId));
  93. g.fillRect (bounds.withTrimmedBottom (1));
  94. bounds.removeFromLeft (5);
  95. g.setColour (rowIsSelected ? findColour (defaultHighlightedTextColourId) : findColour (widgetTextColourId));
  96. //==============================================================================
  97. auto moduleID = project.getEnabledModules().getModuleID (rowNumber);
  98. g.drawFittedText (moduleID, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (0) * (float) width)), Justification::centredLeft, 1);
  99. //==============================================================================
  100. auto version = project.getEnabledModules().getModuleInfo (moduleID).getVersion();
  101. if (version.isEmpty())
  102. version = "?";
  103. g.drawFittedText (version, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (1) * (float) width)), Justification::centredLeft, 1);
  104. //==============================================================================
  105. g.drawFittedText (String (project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID) ? "Yes" : "No"),
  106. bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (2) * (float) width)), Justification::centredLeft, 1);
  107. //==============================================================================
  108. String pathText;
  109. if (project.getEnabledModules().shouldUseGlobalPath (moduleID))
  110. {
  111. pathText = "Global";
  112. }
  113. else
  114. {
  115. StringArray paths;
  116. for (Project::ExporterIterator exporter (project); exporter.next();)
  117. paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim());
  118. paths.removeEmptyStrings();
  119. paths.removeDuplicates (true);
  120. pathText = paths.joinIntoString (", ");
  121. }
  122. g.drawFittedText (pathText, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (3) * (float) width)), Justification::centredLeft, 1);
  123. }
  124. void listBoxItemDoubleClicked (int row, const MouseEvent&) override
  125. {
  126. auto moduleID = project.getEnabledModules().getModuleID (row);
  127. if (moduleID.isNotEmpty())
  128. if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
  129. pcc->showModule (moduleID);
  130. }
  131. void deleteKeyPressed (int row) override
  132. {
  133. project.getEnabledModules().removeModule (project.getEnabledModules().getModuleID (row));
  134. }
  135. void lookAndFeelChanged() override
  136. {
  137. setCopyModeButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
  138. copyPathButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
  139. globalPathsButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
  140. }
  141. private:
  142. enum
  143. {
  144. nameCol = 1,
  145. versionCol,
  146. copyCol,
  147. pathCol
  148. };
  149. Project& project;
  150. ValueTree modulesValueTree;
  151. ContentViewHeader header { "Modules", { getIcons().modules, Colours::transparentBlack } };
  152. ListBox list;
  153. ListBoxHeader* listHeader;
  154. TextButton setCopyModeButton { "Set copy-mode for all modules..." };
  155. TextButton copyPathButton { "Set paths for all modules..." };
  156. TextButton globalPathsButton { "Enable/disable global path for modules..." };
  157. std::map<String, var> modulePathClipboard;
  158. void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
  159. void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
  160. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
  161. void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
  162. void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
  163. void itemChanged()
  164. {
  165. list.updateContent();
  166. resized();
  167. repaint();
  168. }
  169. static void setLocalCopyModeForAllModules (Project& project, bool copyLocally)
  170. {
  171. auto& modules = project.getEnabledModules();
  172. for (auto i = modules.getNumModules(); --i >= 0;)
  173. modules.shouldCopyModuleFilesLocallyValue (modules.getModuleID (i)) = copyLocally;
  174. }
  175. void showCopyModeMenu()
  176. {
  177. PopupMenu m;
  178. m.addItem (PopupMenu::Item ("Set all modules to copy locally")
  179. .setAction ([&] { setLocalCopyModeForAllModules (project, true); }));
  180. m.addItem (PopupMenu::Item ("Set all modules to not copy locally")
  181. .setAction ([&] { setLocalCopyModeForAllModules (project, false); }));
  182. m.showMenuAsync (PopupMenu::Options().withTargetComponent (setCopyModeButton));
  183. }
  184. static void setAllModulesToUseGlobalPaths (Project& project, bool useGlobal)
  185. {
  186. auto& modules = project.getEnabledModules();
  187. for (auto moduleID : modules.getAllModules())
  188. modules.shouldUseGlobalPathValue (moduleID) = useGlobal;
  189. }
  190. static void setSelectedModulesToUseGlobalPaths (Project& project, SparseSet<int> selected, bool useGlobal)
  191. {
  192. auto& modules = project.getEnabledModules();
  193. for (int i = 0; i < selected.size(); ++i)
  194. modules.shouldUseGlobalPathValue (modules.getModuleID (selected[i])) = useGlobal;
  195. }
  196. void showGlobalPathsMenu()
  197. {
  198. PopupMenu m;
  199. m.addItem (PopupMenu::Item ("Set all modules to use global paths")
  200. .setAction ([&] { setAllModulesToUseGlobalPaths (project, true); }));
  201. m.addItem (PopupMenu::Item ("Set all modules to not use global paths")
  202. .setAction ([&] { setAllModulesToUseGlobalPaths (project, false); }));
  203. m.addItem (PopupMenu::Item ("Set selected modules to use global paths")
  204. .setEnabled (list.getNumSelectedRows() > 0)
  205. .setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), true); }));
  206. m.addItem (PopupMenu::Item ("Set selected modules to not use global paths")
  207. .setEnabled (list.getNumSelectedRows() > 0)
  208. .setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), false); }));
  209. m.showMenuAsync (PopupMenu::Options().withTargetComponent (globalPathsButton));
  210. }
  211. void showSetPathsMenu()
  212. {
  213. PopupMenu m;
  214. auto moduleToCopy = project.getEnabledModules().getModuleID (list.getSelectedRow());
  215. if (moduleToCopy.isNotEmpty())
  216. {
  217. m.addItem (PopupMenu::Item ("Copy the paths from the module '" + moduleToCopy + "' to all other modules")
  218. .setAction ([this, moduleToCopy]
  219. {
  220. auto& modulesList = project.getEnabledModules();
  221. for (Project::ExporterIterator exporter (project); exporter.next();)
  222. {
  223. for (int i = 0; i < modulesList.getNumModules(); ++i)
  224. {
  225. auto modID = modulesList.getModuleID (i);
  226. if (modID != moduleToCopy)
  227. exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).get();
  228. }
  229. }
  230. list.repaint();
  231. }));
  232. m.addItem (PopupMenu::Item ("Copy paths from selected module")
  233. .setEnabled (list.getNumSelectedRows() == 1)
  234. .setAction ([this, moduleToCopy]
  235. {
  236. modulePathClipboard.clear();
  237. for (Project::ExporterIterator exporter (project); exporter.next();)
  238. modulePathClipboard[exporter->getUniqueName()] = exporter->getPathForModuleValue (moduleToCopy).get();
  239. list.repaint();
  240. }));
  241. m.addItem (PopupMenu::Item ("Paste paths to selected modules")
  242. .setEnabled (! modulePathClipboard.empty())
  243. .setAction ([this]
  244. {
  245. for (int selectionId = 0; selectionId < list.getNumSelectedRows(); ++selectionId)
  246. {
  247. auto rowNumber = list.getSelectedRow (selectionId);
  248. auto modID = project.getEnabledModules().getModuleID (rowNumber);
  249. for (Project::ExporterIterator exporter (project); exporter.next();)
  250. exporter->getPathForModuleValue (modID) = modulePathClipboard[exporter->getUniqueName()];
  251. }
  252. list.repaint();
  253. }));
  254. }
  255. else
  256. {
  257. m.addItem (PopupMenu::Item ("(Select a module in the list above to use this option)")
  258. .setEnabled (false));
  259. }
  260. m.showMenuAsync (PopupMenu::Options()
  261. .withDeletionCheck (*this)
  262. .withTargetComponent (copyPathButton));
  263. }
  264. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModulesInformationComponent)
  265. };