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