@@ -99,7 +99,7 @@ namespace | |||||
bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier) const | bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier) const | ||||
{ | { | ||||
if (getTypeForFile (fileOrIdentifier) == 0) | |||||
if (getTypeForFile (fileOrIdentifier) == nullptr) | |||||
return false; | return false; | ||||
for (int i = types.size(); --i >= 0;) | for (int i = types.size(); --i >= 0;) | ||||
@@ -121,8 +121,6 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, | |||||
OwnedArray <PluginDescription>& typesFound, | OwnedArray <PluginDescription>& typesFound, | ||||
AudioPluginFormat& format) | AudioPluginFormat& format) | ||||
{ | { | ||||
bool addedOne = false; | |||||
if (dontRescanIfAlreadyInList | if (dontRescanIfAlreadyInList | ||||
&& getTypeForFile (fileOrIdentifier) != nullptr) | && getTypeForFile (fileOrIdentifier) != nullptr) | ||||
{ | { | ||||
@@ -145,6 +143,9 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, | |||||
return false; | return false; | ||||
} | } | ||||
if (blacklist.contains (fileOrIdentifier)) | |||||
return false; | |||||
OwnedArray <PluginDescription> found; | OwnedArray <PluginDescription> found; | ||||
format.findAllTypesForFile (found, fileOrIdentifier); | format.findAllTypesForFile (found, fileOrIdentifier); | ||||
@@ -153,14 +154,11 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, | |||||
PluginDescription* const desc = found.getUnchecked(i); | PluginDescription* const desc = found.getUnchecked(i); | ||||
jassert (desc != nullptr); | jassert (desc != nullptr); | ||||
if (addType (*desc)) | |||||
{ | |||||
addedOne = true; | |||||
typesFound.add (new PluginDescription (*desc)); | |||||
} | |||||
addType (*desc); | |||||
typesFound.add (new PluginDescription (*desc)); | |||||
} | } | ||||
return addedOne; | |||||
return found.size() > 0; | |||||
} | } | ||||
void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager, | void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager, | ||||
@@ -196,10 +194,44 @@ void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& f | |||||
} | } | ||||
} | } | ||||
const StringArray& KnownPluginList::getBlacklistedFiles() const | |||||
{ | |||||
return blacklist; | |||||
} | |||||
void KnownPluginList::addToBlacklist (const String& pluginID) | |||||
{ | |||||
if (! blacklist.contains (pluginID)) | |||||
{ | |||||
blacklist.add (pluginID); | |||||
sendChangeMessage(); | |||||
} | |||||
} | |||||
void KnownPluginList::removeFromBlacklist (const String& pluginID) | |||||
{ | |||||
const int index = blacklist.indexOf (pluginID); | |||||
if (index >= 0) | |||||
{ | |||||
blacklist.remove (index); | |||||
sendChangeMessage(); | |||||
} | |||||
} | |||||
void KnownPluginList::clearBlacklistedFiles() | |||||
{ | |||||
if (blacklist.size() > 0) | |||||
{ | |||||
blacklist.clear(); | |||||
sendChangeMessage(); | |||||
} | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
struct PluginSorter | struct PluginSorter | ||||
{ | { | ||||
PluginSorter (KnownPluginList::SortMethod method_) noexcept : method (method_) {} | |||||
PluginSorter (KnownPluginList::SortMethod sortMethod) noexcept : method (sortMethod) {} | |||||
int compareElements (const PluginDescription* const first, | int compareElements (const PluginDescription* const first, | ||||
const PluginDescription* const second) const | const PluginDescription* const second) const | ||||
@@ -248,12 +280,16 @@ XmlElement* KnownPluginList::createXml() const | |||||
for (int i = 0; i < types.size(); ++i) | for (int i = 0; i < types.size(); ++i) | ||||
e->addChildElement (types.getUnchecked(i)->createXml()); | e->addChildElement (types.getUnchecked(i)->createXml()); | ||||
for (int i = 0; i < blacklist.size(); ++i) | |||||
e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", blacklist[i]); | |||||
return e; | return e; | ||||
} | } | ||||
void KnownPluginList::recreateFromXml (const XmlElement& xml) | void KnownPluginList::recreateFromXml (const XmlElement& xml) | ||||
{ | { | ||||
clear(); | clear(); | ||||
clearBlacklistedFiles(); | |||||
if (xml.hasTagName ("KNOWNPLUGINS")) | if (xml.hasTagName ("KNOWNPLUGINS")) | ||||
{ | { | ||||
@@ -261,7 +297,9 @@ void KnownPluginList::recreateFromXml (const XmlElement& xml) | |||||
{ | { | ||||
PluginDescription info; | PluginDescription info; | ||||
if (info.loadFromXml (*e)) | |||||
if (e->hasTagName ("BLACKLISTED")) | |||||
blacklist.add (e->getStringAttribute ("id")); | |||||
else if (info.loadFromXml (*e)) | |||||
addType (info); | addType (info); | ||||
} | } | ||||
} | } | ||||
@@ -113,6 +113,19 @@ public: | |||||
const StringArray& filenames, | const StringArray& filenames, | ||||
OwnedArray <PluginDescription>& typesFound); | OwnedArray <PluginDescription>& typesFound); | ||||
//============================================================================== | |||||
/** Returns the list of blacklisted files. */ | |||||
const StringArray& getBlacklistedFiles() const; | |||||
/** Adds a plugin ID to the black-list. */ | |||||
void addToBlacklist (const String& pluginID); | |||||
/** Removes a plugin ID from the black-list. */ | |||||
void removeFromBlacklist (const String& pluginID); | |||||
/** Clears all the blacklisted files. */ | |||||
void clearBlacklistedFiles(); | |||||
//============================================================================== | //============================================================================== | ||||
/** Sort methods used to change the order of the plugins in the list. | /** Sort methods used to change the order of the plugins in the list. | ||||
*/ | */ | ||||
@@ -169,6 +182,7 @@ public: | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
OwnedArray <PluginDescription> types; | OwnedArray <PluginDescription> types; | ||||
StringArray blacklist; | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList); | ||||
}; | }; | ||||
@@ -23,14 +23,22 @@ | |||||
============================================================================== | ============================================================================== | ||||
*/ | */ | ||||
static StringArray readDeadMansPedalFile (const File& file) | |||||
{ | |||||
StringArray lines; | |||||
file.readLines (lines); | |||||
lines.removeEmptyStrings(); | |||||
return lines; | |||||
} | |||||
PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, | PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, | ||||
AudioPluginFormat& formatToLookFor, | AudioPluginFormat& formatToLookFor, | ||||
FileSearchPath directoriesToSearch, | FileSearchPath directoriesToSearch, | ||||
const bool recursive, | const bool recursive, | ||||
const File& deadMansPedalFile_) | |||||
const File& deadMansPedal) | |||||
: list (listToAddTo), | : list (listToAddTo), | ||||
format (formatToLookFor), | format (formatToLookFor), | ||||
deadMansPedalFile (deadMansPedalFile_), | |||||
deadMansPedalFile (deadMansPedal), | |||||
nextIndex (0), | nextIndex (0), | ||||
progress (0) | progress (0) | ||||
{ | { | ||||
@@ -40,7 +48,7 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, | |||||
// If any plugins have crashed recently when being loaded, move them to the | // If any plugins have crashed recently when being loaded, move them to the | ||||
// end of the list to give the others a chance to load correctly.. | // end of the list to give the others a chance to load correctly.. | ||||
const StringArray crashedPlugins (getDeadMansPedalFile()); | |||||
const StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile)); | |||||
for (int i = 0; i < crashedPlugins.size(); ++i) | for (int i = 0; i < crashedPlugins.size(); ++i) | ||||
{ | { | ||||
@@ -50,6 +58,8 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, | |||||
if (f == filesOrIdentifiersToScan[j]) | if (f == filesOrIdentifiersToScan[j]) | ||||
filesOrIdentifiersToScan.move (j, -1); | filesOrIdentifiersToScan.move (j, -1); | ||||
} | } | ||||
applyBlacklistingsFromDeadMansPedal (listToAddTo, deadMansPedalFile); | |||||
} | } | ||||
PluginDirectoryScanner::~PluginDirectoryScanner() | PluginDirectoryScanner::~PluginDirectoryScanner() | ||||
@@ -71,21 +81,18 @@ bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList) | |||||
OwnedArray <PluginDescription> typesFound; | OwnedArray <PluginDescription> typesFound; | ||||
// Add this plugin to the end of the dead-man's pedal list in case it crashes... | // Add this plugin to the end of the dead-man's pedal list in case it crashes... | ||||
StringArray crashedPlugins (getDeadMansPedalFile()); | |||||
StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile)); | |||||
crashedPlugins.removeString (file); | crashedPlugins.removeString (file); | ||||
crashedPlugins.add (file); | crashedPlugins.add (file); | ||||
setDeadMansPedalFile (crashedPlugins); | setDeadMansPedalFile (crashedPlugins); | ||||
list.scanAndAddFile (file, | |||||
dontRescanIfAlreadyInList, | |||||
typesFound, | |||||
format); | |||||
list.scanAndAddFile (file, dontRescanIfAlreadyInList, typesFound, format); | |||||
// Managed to load without crashing, so remove it from the dead-man's-pedal.. | // Managed to load without crashing, so remove it from the dead-man's-pedal.. | ||||
crashedPlugins.removeString (file); | crashedPlugins.removeString (file); | ||||
setDeadMansPedalFile (crashedPlugins); | setDeadMansPedalFile (crashedPlugins); | ||||
if (typesFound.size() == 0) | |||||
if (typesFound.size() == 0 && ! list.getBlacklistedFiles().contains (file)) | |||||
failedFiles.add (file); | failedFiles.add (file); | ||||
} | } | ||||
@@ -101,21 +108,18 @@ bool PluginDirectoryScanner::skipNextFile() | |||||
return nextIndex < filesOrIdentifiersToScan.size(); | return nextIndex < filesOrIdentifiersToScan.size(); | ||||
} | } | ||||
StringArray PluginDirectoryScanner::getDeadMansPedalFile() | |||||
void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents) | |||||
{ | { | ||||
StringArray lines; | |||||
if (deadMansPedalFile != File::nonexistent) | if (deadMansPedalFile != File::nonexistent) | ||||
{ | |||||
deadMansPedalFile.readLines (lines); | |||||
lines.removeEmptyStrings(); | |||||
} | |||||
return lines; | |||||
deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true); | |||||
} | } | ||||
void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents) | |||||
void PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (KnownPluginList& list, const File& file) | |||||
{ | { | ||||
if (deadMansPedalFile != File::nonexistent) | |||||
deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true); | |||||
// If any plugins have crashed recently when being loaded, move them to the | |||||
// end of the list to give the others a chance to load correctly.. | |||||
const StringArray crashedPlugins (readDeadMansPedalFile (file)); | |||||
for (int i = 0; i < crashedPlugins.size(); ++i) | |||||
list.addToBlacklist (crashedPlugins[i]); | |||||
} | } |
@@ -105,6 +105,10 @@ public: | |||||
*/ | */ | ||||
const StringArray& getFailedFiles() const noexcept { return failedFiles; } | const StringArray& getFailedFiles() const noexcept { return failedFiles; } | ||||
/** Reads the given dead-mans-pedal file and applies its contents to the list. */ | |||||
static void applyBlacklistingsFromDeadMansPedal (KnownPluginList& listToApplyTo, | |||||
const File& deadMansPedalFile); | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
KnownPluginList& list; | KnownPluginList& list; | ||||
@@ -115,7 +119,6 @@ private: | |||||
int nextIndex; | int nextIndex; | ||||
float progress; | float progress; | ||||
StringArray getDeadMansPedalFile(); | |||||
void setDeadMansPedalFile (const StringArray& newContents); | void setDeadMansPedalFile (const StringArray& newContents); | ||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner); | ||||
@@ -43,6 +43,8 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, | |||||
setSize (400, 600); | setSize (400, 600); | ||||
list.addChangeListener (this); | list.addChangeListener (this); | ||||
updateList(); | updateList(); | ||||
PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (list, deadMansPedalFile); | |||||
} | } | ||||
PluginListComponent::~PluginListComponent() | PluginListComponent::~PluginListComponent() | ||||
@@ -60,7 +62,7 @@ void PluginListComponent::resized() | |||||
{ | { | ||||
listBox.setBounds (0, 0, getWidth(), getHeight() - 30); | listBox.setBounds (0, 0, getWidth(), getHeight() - 30); | ||||
optionsButton.changeWidthToFitText (24); | optionsButton.changeWidthToFitText (24); | ||||
optionsButton.setTopLeftPosition (8, getHeight() - 28); | |||||
optionsButton.setTopLeftPosition (0, getHeight() - 28); | |||||
} | } | ||||
void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) | void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) | ||||
@@ -76,7 +78,7 @@ void PluginListComponent::updateList() | |||||
int PluginListComponent::getNumRows() | int PluginListComponent::getNumRows() | ||||
{ | { | ||||
return list.getNumTypes(); | |||||
return list.getNumTypes() + list.getBlacklistedFiles().size(); | |||||
} | } | ||||
void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) | void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) | ||||
@@ -84,46 +86,60 @@ void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int | |||||
if (rowIsSelected) | if (rowIsSelected) | ||||
g.fillAll (findColour (TextEditor::highlightColourId)); | g.fillAll (findColour (TextEditor::highlightColourId)); | ||||
if (const PluginDescription* const pd = list.getType (row)) | |||||
{ | |||||
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); | |||||
String name, desc; | |||||
bool isBlacklisted = false; | |||||
const Rectangle<float> bb (ga.getBoundingBox (0, -1, false)); | |||||
if (row >= list.getNumTypes()) | |||||
{ | |||||
isBlacklisted = true; | |||||
name = list.getBlacklistedFiles() [row - list.getNumTypes()]; | |||||
desc = TRANS("Deactivated after failing to initialise correctly"); | |||||
} | |||||
else if (const PluginDescription* const pd = list.getType (row)) | |||||
{ | |||||
name = pd->name; | |||||
String desc; | |||||
desc << pd->pluginFormatName | desc << pd->pluginFormatName | ||||
<< (pd->isInstrument ? " instrument" : " effect") | << (pd->isInstrument ? " instrument" : " effect") | ||||
<< " - " | |||||
<< pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") | |||||
<< " / " | |||||
<< pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); | |||||
<< " - " << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") | |||||
<< " / " << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); | |||||
if (pd->manufacturerName.isNotEmpty()) | |||||
desc << " - " << pd->manufacturerName; | |||||
if (pd->manufacturerName.isNotEmpty()) desc << " - " << pd->manufacturerName; | |||||
if (pd->version.isNotEmpty()) desc << " - " << pd->version; | |||||
if (pd->category.isNotEmpty()) desc << " - category: '" << pd->category << '\''; | |||||
} | |||||
if (pd->version.isNotEmpty()) | |||||
desc << " - " << pd->version; | |||||
if (name.isNotEmpty()) | |||||
{ | |||||
GlyphArrangement ga; | |||||
ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), name, 8.0f, height * 0.8f, width - 10.0f, true); | |||||
if (pd->category.isNotEmpty()) | |||||
desc << " - category: '" << pd->category << '\''; | |||||
g.setColour (isBlacklisted ? Colours::red : Colours::black); | |||||
ga.draw (g); | |||||
g.setColour (Colours::grey); | |||||
const Rectangle<float> bb (ga.getBoundingBox (0, -1, false)); | |||||
ga.clear(); | ga.clear(); | ||||
ga.addCurtailedLineOfText (Font (height * 0.6f), desc, | ga.addCurtailedLineOfText (Font (height * 0.6f), desc, | ||||
bb.getRight() + 10.0f, height * 0.8f, | |||||
jmax (bb.getRight() + 10.0f, width / 3.0f), height * 0.8f, | |||||
width - bb.getRight() - 12.0f, true); | width - bb.getRight() - 12.0f, true); | ||||
g.setColour (isBlacklisted ? Colours::red : Colours::grey); | |||||
ga.draw (g); | ga.draw (g); | ||||
} | } | ||||
} | } | ||||
static void removePluginItem (KnownPluginList& list, int index) | |||||
{ | |||||
if (index < list.getNumTypes()) | |||||
list.removeType (index); | |||||
else | |||||
list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); | |||||
} | |||||
void PluginListComponent::deleteKeyPressed (int lastRowSelected) | void PluginListComponent::deleteKeyPressed (int lastRowSelected) | ||||
{ | { | ||||
list.removeType (lastRowSelected); | |||||
removePluginItem (list, lastRowSelected); | |||||
} | } | ||||
void PluginListComponent::removeSelected() | void PluginListComponent::removeSelected() | ||||
@@ -132,15 +148,22 @@ void PluginListComponent::removeSelected() | |||||
for (int i = list.getNumTypes(); --i >= 0;) | for (int i = list.getNumTypes(); --i >= 0;) | ||||
if (selected.contains (i)) | if (selected.contains (i)) | ||||
list.removeType (i); | |||||
removePluginItem (list, i); | |||||
} | } | ||||
void PluginListComponent::showSelectedFolder() | |||||
bool PluginListComponent::canShowSelectedFolder() const | |||||
{ | { | ||||
const PluginDescription* const desc = list.getType (listBox.getSelectedRow()); | |||||
if (const PluginDescription* const desc = list.getType (listBox.getSelectedRow())) | |||||
return File::createFileWithoutCheckingPath (desc->fileOrIdentifier).exists(); | |||||
return false; | |||||
} | |||||
if (desc != nullptr && File (desc->fileOrIdentifier).existsAsFile()) | |||||
File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); | |||||
void PluginListComponent::showSelectedFolder() | |||||
{ | |||||
if (canShowSelectedFolder()) | |||||
if (const PluginDescription* const desc = list.getType (listBox.getSelectedRow())) | |||||
File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); | |||||
} | } | ||||
void PluginListComponent::removeMissingPlugins() | void PluginListComponent::removeMissingPlugins() | ||||
@@ -180,7 +203,7 @@ void PluginListComponent::buttonClicked (Button* button) | |||||
PopupMenu menu; | PopupMenu menu; | ||||
menu.addItem (1, TRANS("Clear list")); | menu.addItem (1, TRANS("Clear list")); | ||||
menu.addItem (5, TRANS("Remove selected plugin from list"), listBox.getNumSelectedRows() > 0); | 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 (6, TRANS("Show folder containing selected plugin"), canShowSelectedFolder()); | |||||
menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); | menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); | ||||
menu.addSeparator(); | menu.addSeparator(); | ||||
menu.addItem (2, TRANS("Sort alphabetically")); | menu.addItem (2, TRANS("Sort alphabetically")); | ||||
@@ -292,7 +315,7 @@ void PluginListComponent::scanFinished (const StringArray& failedFiles) | |||||
StringArray shortNames; | StringArray shortNames; | ||||
for (int i = 0; i < failedFiles.size(); ++i) | for (int i = 0; i < failedFiles.size(); ++i) | ||||
shortNames.add (File (failedFiles[i]).getFileName()); | |||||
shortNames.add (File::createFileWithoutCheckingPath (failedFiles[i]).getFileName()); | |||||
currentScanner = nullptr; // mustn't delete this before using the failed files array | currentScanner = nullptr; // mustn't delete this before using the failed files array | ||||
@@ -97,6 +97,7 @@ private: | |||||
void updateList(); | void updateList(); | ||||
void removeSelected(); | void removeSelected(); | ||||
void showSelectedFolder(); | void showSelectedFolder(); | ||||
bool canShowSelectedFolder() const; | |||||
void removeMissingPlugins(); | void removeMissingPlugins(); | ||||
void buttonClicked (Button*); | void buttonClicked (Button*); | ||||