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.

331 lines
12KB

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