/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-11 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #include "../../core/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE #include "juce_PluginListComponent.h" #include "juce_PluginDirectoryScanner.h" #include "../../gui/components/controls/juce_TextEditor.h" #include "../../gui/components/windows/juce_AlertWindow.h" #include "../../gui/graphics/fonts/juce_GlyphArrangement.h" #include "../../gui/components/filebrowser/juce_FileSearchPathListComponent.h" #include "../../text/juce_LocalisedStrings.h" #include "../../events/juce_MessageManager.h" //============================================================================== PluginListComponent::PluginListComponent (KnownPluginList& listToEdit, const File& deadMansPedalFile_, PropertiesFile* const propertiesToUse_) : list (listToEdit), deadMansPedalFile (deadMansPedalFile_), optionsButton ("Options..."), propertiesToUse (propertiesToUse_) { listBox.setModel (this); addAndMakeVisible (&listBox); addAndMakeVisible (&optionsButton); optionsButton.addListener (this); optionsButton.setTriggeredOnMouseDown (true); setSize (400, 600); list.addChangeListener (this); updateList(); } PluginListComponent::~PluginListComponent() { list.removeChangeListener (this); } void PluginListComponent::resized() { listBox.setBounds (0, 0, getWidth(), getHeight() - 30); optionsButton.changeWidthToFitText (24); optionsButton.setTopLeftPosition (8, getHeight() - 28); } void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) { updateList(); } void PluginListComponent::updateList() { listBox.updateContent(); listBox.repaint(); } int PluginListComponent::getNumRows() { return list.getNumTypes(); } void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) { if (rowIsSelected) g.fillAll (findColour (TextEditor::highlightColourId)); const PluginDescription* const pd = list.getType (row); if (pd != nullptr) { GlyphArrangement ga; ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), pd->name, 8.0f, height * 0.8f, width - 10.0f, true); g.setColour (Colours::black); ga.draw (g); const Rectangle bb (ga.getBoundingBox (0, -1, false)); String desc; desc << pd->pluginFormatName << (pd->isInstrument ? " instrument" : " effect") << " - " << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") << " / " << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); if (pd->manufacturerName.isNotEmpty()) desc << " - " << pd->manufacturerName; if (pd->version.isNotEmpty()) desc << " - " << pd->version; if (pd->category.isNotEmpty()) desc << " - category: '" << pd->category << '\''; g.setColour (Colours::grey); ga.clear(); ga.addCurtailedLineOfText (Font (height * 0.6f), desc, bb.getRight() + 10.0f, height * 0.8f, width - bb.getRight() - 12.0f, true); ga.draw (g); } } void PluginListComponent::deleteKeyPressed (int lastRowSelected) { list.removeType (lastRowSelected); } void PluginListComponent::optionsMenuCallback (int result) { switch (result) { case 1: list.clear(); break; case 2: list.sort (KnownPluginList::sortAlphabetically); break; case 3: list.sort (KnownPluginList::sortByCategory); break; case 4: list.sort (KnownPluginList::sortByManufacturer); break; case 5: { const SparseSet selected (listBox.getSelectedRows()); for (int i = list.getNumTypes(); --i >= 0;) if (selected.contains (i)) list.removeType (i); break; } case 6: { const PluginDescription* const desc = list.getType (listBox.getSelectedRow()); if (desc != nullptr) { if (File (desc->fileOrIdentifier).existsAsFile()) File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); } break; } case 7: for (int i = list.getNumTypes(); --i >= 0;) if (! AudioPluginFormatManager::getInstance()->doesPluginStillExist (*list.getType (i))) list.removeType (i); break; default: if (result != 0) { typeToScan = result - 10; startTimer (1); } break; } } void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList) { if (pluginList != nullptr) pluginList->optionsMenuCallback (result); } void PluginListComponent::buttonClicked (Button* button) { if (button == &optionsButton) { PopupMenu menu; menu.addItem (1, TRANS("Clear list")); menu.addItem (5, TRANS("Remove selected plugin from list"), listBox.getNumSelectedRows() > 0); menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox.getNumSelectedRows() > 0); menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); menu.addSeparator(); menu.addItem (2, TRANS("Sort alphabetically")); menu.addItem (3, TRANS("Sort by category")); menu.addItem (4, TRANS("Sort by manufacturer")); menu.addSeparator(); for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) { AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); if (format->getDefaultLocationsToSearch().getNumPaths() > 0) menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); } menu.showMenuAsync (PopupMenu::Options().withTargetComponent (&optionsButton), ModalCallbackFunction::forComponent (optionsMenuStaticCallback, this)); } } void PluginListComponent::timerCallback() { stopTimer(); scanFor (AudioPluginFormatManager::getInstance()->getFormat (typeToScan)); } bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) { return true; } void PluginListComponent::filesDropped (const StringArray& files, int, int) { OwnedArray typesFound; list.scanAndAddDragAndDroppedFiles (files, typesFound); } void PluginListComponent::scanFor (AudioPluginFormat* format) { #if JUCE_MODAL_LOOPS_PERMITTED if (format == nullptr) return; FileSearchPath path (format->getDefaultLocationsToSearch()); if (propertiesToUse != nullptr) path = propertiesToUse->getValue ("lastPluginScanPath_" + format->getName(), path.toString()); { AlertWindow aw (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon); FileSearchPathListComponent pathList; pathList.setSize (500, 300); pathList.setPath (path); aw.addCustomComponent (&pathList); aw.addButton (TRANS("Scan"), 1, KeyPress::returnKey); aw.addButton (TRANS("Cancel"), 0, KeyPress::escapeKey); if (aw.runModalLoop() == 0) return; path = pathList.getPath(); } if (propertiesToUse != nullptr) { propertiesToUse->setValue ("lastPluginScanPath_" + format->getName(), path.toString()); propertiesToUse->saveIfNeeded(); } double progress = 0.0; AlertWindow aw (TRANS("Scanning for plugins..."), TRANS("Searching for all possible plugin files..."), AlertWindow::NoIcon); aw.addButton (TRANS("Cancel"), 0, KeyPress::escapeKey); aw.addProgressBarComponent (progress); aw.enterModalState(); MessageManager::getInstance()->runDispatchLoopUntil (300); PluginDirectoryScanner scanner (list, *format, path, true, deadMansPedalFile); for (;;) { aw.setMessage (TRANS("Testing:\n\n") + scanner.getNextPluginFileThatWillBeScanned()); MessageManager::getInstance()->runDispatchLoopUntil (20); if (! scanner.scanNextFile (true)) break; if (! aw.isCurrentlyModal()) break; progress = scanner.getProgress(); } if (scanner.getFailedFiles().size() > 0) { StringArray shortNames; for (int i = 0; i < scanner.getFailedFiles().size(); ++i) shortNames.add (File (scanner.getFailedFiles()[i]).getFileName()); AlertWindow::showMessageBox (AlertWindow::InfoIcon, TRANS("Scan complete"), TRANS("Note that the following files appeared to be plugin files, but failed to load correctly:\n\n") + shortNames.joinIntoString (", ")); } #else jassertfalse; // this method needs refactoring to work without modal loops.. #endif } END_JUCE_NAMESPACE