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.

333 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_PluginListComponent.h"
  21. #include "juce_PluginDirectoryScanner.h"
  22. #include "../../gui/components/controls/juce_TextEditor.h"
  23. #include "../../gui/components/windows/juce_AlertWindow.h"
  24. #include "../../gui/graphics/fonts/juce_GlyphArrangement.h"
  25. #include "../../gui/components/filebrowser/juce_FileSearchPathListComponent.h"
  26. #include "../../text/juce_LocalisedStrings.h"
  27. #include "../../events/juce_MessageManager.h"
  28. //==============================================================================
  29. PluginListComponent::PluginListComponent (KnownPluginList& listToEdit,
  30. const File& deadMansPedalFile_,
  31. PropertiesFile* const propertiesToUse_)
  32. : list (listToEdit),
  33. deadMansPedalFile (deadMansPedalFile_),
  34. optionsButton ("Options..."),
  35. propertiesToUse (propertiesToUse_)
  36. {
  37. listBox.setModel (this);
  38. addAndMakeVisible (&listBox);
  39. addAndMakeVisible (&optionsButton);
  40. optionsButton.addListener (this);
  41. optionsButton.setTriggeredOnMouseDown (true);
  42. setSize (400, 600);
  43. list.addChangeListener (this);
  44. updateList();
  45. }
  46. PluginListComponent::~PluginListComponent()
  47. {
  48. list.removeChangeListener (this);
  49. }
  50. void PluginListComponent::resized()
  51. {
  52. listBox.setBounds (0, 0, getWidth(), getHeight() - 30);
  53. optionsButton.changeWidthToFitText (24);
  54. optionsButton.setTopLeftPosition (8, getHeight() - 28);
  55. }
  56. void PluginListComponent::changeListenerCallback (ChangeBroadcaster*)
  57. {
  58. updateList();
  59. }
  60. void PluginListComponent::updateList()
  61. {
  62. listBox.updateContent();
  63. listBox.repaint();
  64. }
  65. int PluginListComponent::getNumRows()
  66. {
  67. return list.getNumTypes();
  68. }
  69. void PluginListComponent::paintListBoxItem (int row,
  70. Graphics& g,
  71. int width, int height,
  72. bool rowIsSelected)
  73. {
  74. if (rowIsSelected)
  75. g.fillAll (findColour (TextEditor::highlightColourId));
  76. const PluginDescription* const pd = list.getType (row);
  77. if (pd != nullptr)
  78. {
  79. GlyphArrangement ga;
  80. ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), pd->name, 8.0f, height * 0.8f, width - 10.0f, true);
  81. g.setColour (Colours::black);
  82. ga.draw (g);
  83. const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));
  84. String desc;
  85. desc << pd->pluginFormatName
  86. << (pd->isInstrument ? " instrument" : " effect")
  87. << " - "
  88. << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins")
  89. << " / "
  90. << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs");
  91. if (pd->manufacturerName.isNotEmpty())
  92. desc << " - " << pd->manufacturerName;
  93. if (pd->version.isNotEmpty())
  94. desc << " - " << pd->version;
  95. if (pd->category.isNotEmpty())
  96. desc << " - category: '" << pd->category << '\'';
  97. g.setColour (Colours::grey);
  98. ga.clear();
  99. ga.addCurtailedLineOfText (Font (height * 0.6f), desc, bb.getRight() + 10.0f, height * 0.8f, width - bb.getRight() - 12.0f, true);
  100. ga.draw (g);
  101. }
  102. }
  103. void PluginListComponent::deleteKeyPressed (int lastRowSelected)
  104. {
  105. list.removeType (lastRowSelected);
  106. }
  107. void PluginListComponent::optionsMenuCallback (int result)
  108. {
  109. switch (result)
  110. {
  111. case 1:
  112. list.clear();
  113. break;
  114. case 2:
  115. list.sort (KnownPluginList::sortAlphabetically);
  116. break;
  117. case 3:
  118. list.sort (KnownPluginList::sortByCategory);
  119. break;
  120. case 4:
  121. list.sort (KnownPluginList::sortByManufacturer);
  122. break;
  123. case 5:
  124. {
  125. const SparseSet <int> selected (listBox.getSelectedRows());
  126. for (int i = list.getNumTypes(); --i >= 0;)
  127. if (selected.contains (i))
  128. list.removeType (i);
  129. break;
  130. }
  131. case 6:
  132. {
  133. const PluginDescription* const desc = list.getType (listBox.getSelectedRow());
  134. if (desc != nullptr)
  135. {
  136. if (File (desc->fileOrIdentifier).existsAsFile())
  137. File (desc->fileOrIdentifier).getParentDirectory().startAsProcess();
  138. }
  139. break;
  140. }
  141. case 7:
  142. for (int i = list.getNumTypes(); --i >= 0;)
  143. if (! AudioPluginFormatManager::getInstance()->doesPluginStillExist (*list.getType (i)))
  144. list.removeType (i);
  145. break;
  146. default:
  147. if (result != 0)
  148. {
  149. typeToScan = result - 10;
  150. startTimer (1);
  151. }
  152. break;
  153. }
  154. }
  155. void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList)
  156. {
  157. if (pluginList != nullptr)
  158. pluginList->optionsMenuCallback (result);
  159. }
  160. void PluginListComponent::buttonClicked (Button* button)
  161. {
  162. if (button == &optionsButton)
  163. {
  164. PopupMenu menu;
  165. menu.addItem (1, TRANS("Clear list"));
  166. menu.addItem (5, TRANS("Remove selected plugin from list"), listBox.getNumSelectedRows() > 0);
  167. menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox.getNumSelectedRows() > 0);
  168. menu.addItem (7, TRANS("Remove any plugins whose files no longer exist"));
  169. menu.addSeparator();
  170. menu.addItem (2, TRANS("Sort alphabetically"));
  171. menu.addItem (3, TRANS("Sort by category"));
  172. menu.addItem (4, TRANS("Sort by manufacturer"));
  173. menu.addSeparator();
  174. for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i)
  175. {
  176. AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i);
  177. if (format->getDefaultLocationsToSearch().getNumPaths() > 0)
  178. menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins...");
  179. }
  180. menu.showMenuAsync (PopupMenu::Options().withTargetComponent (&optionsButton),
  181. ModalCallbackFunction::forComponent (optionsMenuStaticCallback, this));
  182. }
  183. }
  184. void PluginListComponent::timerCallback()
  185. {
  186. stopTimer();
  187. scanFor (AudioPluginFormatManager::getInstance()->getFormat (typeToScan));
  188. }
  189. bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/)
  190. {
  191. return true;
  192. }
  193. void PluginListComponent::filesDropped (const StringArray& files, int, int)
  194. {
  195. OwnedArray <PluginDescription> typesFound;
  196. list.scanAndAddDragAndDroppedFiles (files, typesFound);
  197. }
  198. void PluginListComponent::scanFor (AudioPluginFormat* format)
  199. {
  200. #if JUCE_MODAL_LOOPS_PERMITTED
  201. if (format == nullptr)
  202. return;
  203. FileSearchPath path (format->getDefaultLocationsToSearch());
  204. if (propertiesToUse != nullptr)
  205. path = propertiesToUse->getValue ("lastPluginScanPath_" + format->getName(), path.toString());
  206. {
  207. AlertWindow aw (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon);
  208. FileSearchPathListComponent pathList;
  209. pathList.setSize (500, 300);
  210. pathList.setPath (path);
  211. aw.addCustomComponent (&pathList);
  212. aw.addButton (TRANS("Scan"), 1, KeyPress::returnKey);
  213. aw.addButton (TRANS("Cancel"), 0, KeyPress::escapeKey);
  214. if (aw.runModalLoop() == 0)
  215. return;
  216. path = pathList.getPath();
  217. }
  218. if (propertiesToUse != nullptr)
  219. {
  220. propertiesToUse->setValue ("lastPluginScanPath_" + format->getName(), path.toString());
  221. propertiesToUse->saveIfNeeded();
  222. }
  223. double progress = 0.0;
  224. AlertWindow aw (TRANS("Scanning for plugins..."),
  225. TRANS("Searching for all possible plugin files..."), AlertWindow::NoIcon);
  226. aw.addButton (TRANS("Cancel"), 0, KeyPress::escapeKey);
  227. aw.addProgressBarComponent (progress);
  228. aw.enterModalState();
  229. MessageManager::getInstance()->runDispatchLoopUntil (300);
  230. PluginDirectoryScanner scanner (list, *format, path, true, deadMansPedalFile);
  231. for (;;)
  232. {
  233. aw.setMessage (TRANS("Testing:\n\n")
  234. + scanner.getNextPluginFileThatWillBeScanned());
  235. MessageManager::getInstance()->runDispatchLoopUntil (20);
  236. if (! scanner.scanNextFile (true))
  237. break;
  238. if (! aw.isCurrentlyModal())
  239. break;
  240. progress = scanner.getProgress();
  241. }
  242. if (scanner.getFailedFiles().size() > 0)
  243. {
  244. StringArray shortNames;
  245. for (int i = 0; i < scanner.getFailedFiles().size(); ++i)
  246. shortNames.add (File (scanner.getFailedFiles()[i]).getFileName());
  247. AlertWindow::showMessageBox (AlertWindow::InfoIcon,
  248. TRANS("Scan complete"),
  249. TRANS("Note that the following files appeared to be plugin files, but failed to load correctly:\n\n")
  250. + shortNames.joinIntoString (", "));
  251. }
  252. #else
  253. jassertfalse; // this method needs refactoring to work without modal loops..
  254. #endif
  255. }
  256. END_JUCE_NAMESPACE