Add TAL-Reverb-II plugin to test
[juce-lv2.git] / juce / source / src / audio / plugin_host / juce_KnownPluginList.cpp
blobeae96a20e059385df0b040eaa859f3584e696854
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../core/juce_StandardHeader.h"
28 BEGIN_JUCE_NAMESPACE
30 #include "juce_KnownPluginList.h"
31 #include "juce_AudioPluginFormatManager.h"
34 //==============================================================================
35 KnownPluginList::KnownPluginList()
39 KnownPluginList::~KnownPluginList()
43 void KnownPluginList::clear()
45 if (types.size() > 0)
47 types.clear();
48 sendChangeMessage();
52 PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifier) const
54 for (int i = 0; i < types.size(); ++i)
55 if (types.getUnchecked(i)->fileOrIdentifier == fileOrIdentifier)
56 return types.getUnchecked(i);
58 return nullptr;
61 PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const
63 for (int i = 0; i < types.size(); ++i)
64 if (types.getUnchecked(i)->createIdentifierString() == identifierString)
65 return types.getUnchecked(i);
67 return nullptr;
70 bool KnownPluginList::addType (const PluginDescription& type)
72 for (int i = types.size(); --i >= 0;)
74 if (types.getUnchecked(i)->isDuplicateOf (type))
76 // strange - found a duplicate plugin with different info..
77 jassert (types.getUnchecked(i)->name == type.name);
78 jassert (types.getUnchecked(i)->isInstrument == type.isInstrument);
80 *types.getUnchecked(i) = type;
81 return false;
85 types.add (new PluginDescription (type));
86 sendChangeMessage();
87 return true;
90 void KnownPluginList::removeType (const int index)
92 types.remove (index);
93 sendChangeMessage();
96 namespace
98 Time getPluginFileModTime (const String& fileOrIdentifier)
100 if (fileOrIdentifier.startsWithChar ('/') || fileOrIdentifier[1] == ':')
101 return File (fileOrIdentifier).getLastModificationTime();
103 return Time();
106 bool timesAreDifferent (const Time& t1, const Time& t2) noexcept
108 return t1 != t2 || t1 == Time();
112 bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier) const
114 if (getTypeForFile (fileOrIdentifier) == 0)
115 return false;
117 for (int i = types.size(); --i >= 0;)
119 const PluginDescription* const d = types.getUnchecked(i);
121 if (d->fileOrIdentifier == fileOrIdentifier
122 && timesAreDifferent (d->lastFileModTime, getPluginFileModTime (fileOrIdentifier)))
124 return false;
128 return true;
131 bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
132 const bool dontRescanIfAlreadyInList,
133 OwnedArray <PluginDescription>& typesFound,
134 AudioPluginFormat& format)
136 bool addedOne = false;
138 if (dontRescanIfAlreadyInList
139 && getTypeForFile (fileOrIdentifier) != nullptr)
141 bool needsRescanning = false;
143 for (int i = types.size(); --i >= 0;)
145 const PluginDescription* const d = types.getUnchecked(i);
147 if (d->fileOrIdentifier == fileOrIdentifier)
149 if (timesAreDifferent (d->lastFileModTime, getPluginFileModTime (fileOrIdentifier)))
150 needsRescanning = true;
151 else
152 typesFound.add (new PluginDescription (*d));
156 if (! needsRescanning)
157 return false;
160 OwnedArray <PluginDescription> found;
161 format.findAllTypesForFile (found, fileOrIdentifier);
163 for (int i = 0; i < found.size(); ++i)
165 PluginDescription* const desc = found.getUnchecked(i);
166 jassert (desc != nullptr);
168 if (addType (*desc))
169 addedOne = true;
171 typesFound.add (new PluginDescription (*desc));
174 return addedOne;
177 void KnownPluginList::scanAndAddDragAndDroppedFiles (const StringArray& files,
178 OwnedArray <PluginDescription>& typesFound)
180 for (int i = 0; i < files.size(); ++i)
182 for (int j = 0; j < AudioPluginFormatManager::getInstance()->getNumFormats(); ++j)
184 AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (j);
186 if (scanAndAddFile (files[i], true, typesFound, *format))
187 return;
190 const File f (files[i]);
192 if (f.isDirectory())
194 StringArray s;
197 Array<File> subFiles;
198 f.findChildFiles (subFiles, File::findFilesAndDirectories, false);
200 for (int j = 0; j < subFiles.size(); ++j)
201 s.add (subFiles.getReference(j).getFullPathName());
204 scanAndAddDragAndDroppedFiles (s, typesFound);
209 //==============================================================================
210 class PluginSorter
212 public:
213 KnownPluginList::SortMethod method;
215 PluginSorter() noexcept {}
217 int compareElements (const PluginDescription* const first,
218 const PluginDescription* const second) const
220 int diff = 0;
222 if (method == KnownPluginList::sortByCategory)
223 diff = first->category.compareLexicographically (second->category);
224 else if (method == KnownPluginList::sortByManufacturer)
225 diff = first->manufacturerName.compareLexicographically (second->manufacturerName);
226 else if (method == KnownPluginList::sortByFileSystemLocation)
227 diff = first->fileOrIdentifier.replaceCharacter ('\\', '/')
228 .upToLastOccurrenceOf ("/", false, false)
229 .compare (second->fileOrIdentifier.replaceCharacter ('\\', '/')
230 .upToLastOccurrenceOf ("/", false, false));
232 if (diff == 0)
233 diff = first->name.compareLexicographically (second->name);
235 return diff;
239 void KnownPluginList::sort (const SortMethod method)
241 if (method != defaultOrder)
243 PluginSorter sorter;
244 sorter.method = method;
245 types.sort (sorter, true);
247 sendChangeMessage();
251 //==============================================================================
252 XmlElement* KnownPluginList::createXml() const
254 XmlElement* const e = new XmlElement ("KNOWNPLUGINS");
256 for (int i = 0; i < types.size(); ++i)
257 e->addChildElement (types.getUnchecked(i)->createXml());
259 return e;
262 void KnownPluginList::recreateFromXml (const XmlElement& xml)
264 clear();
266 if (xml.hasTagName ("KNOWNPLUGINS"))
268 forEachXmlChildElement (xml, e)
270 PluginDescription info;
272 if (info.loadFromXml (*e))
273 addType (info);
278 //==============================================================================
279 const int menuIdBase = 0x324503f4;
281 // This is used to turn a bunch of paths into a nested menu structure.
282 struct PluginFilesystemTree
284 private:
285 String folder;
286 OwnedArray <PluginFilesystemTree> subFolders;
287 Array <PluginDescription*> plugins;
289 void addPlugin (PluginDescription* const pd, const String& path)
291 if (path.isEmpty())
293 plugins.add (pd);
295 else
297 const String firstSubFolder (path.upToFirstOccurrenceOf ("/", false, false));
298 const String remainingPath (path.fromFirstOccurrenceOf ("/", false, false));
300 for (int i = subFolders.size(); --i >= 0;)
302 if (subFolders.getUnchecked(i)->folder.equalsIgnoreCase (firstSubFolder))
304 subFolders.getUnchecked(i)->addPlugin (pd, remainingPath);
305 return;
309 PluginFilesystemTree* const newFolder = new PluginFilesystemTree();
310 newFolder->folder = firstSubFolder;
311 subFolders.add (newFolder);
313 newFolder->addPlugin (pd, remainingPath);
317 // removes any deeply nested folders that don't contain any actual plugins
318 void optimise()
320 for (int i = subFolders.size(); --i >= 0;)
322 PluginFilesystemTree* const sub = subFolders.getUnchecked(i);
324 sub->optimise();
326 if (sub->plugins.size() == 0)
328 for (int j = 0; j < sub->subFolders.size(); ++j)
329 subFolders.add (sub->subFolders.getUnchecked(j));
331 sub->subFolders.clear (false);
332 subFolders.remove (i);
337 public:
338 void buildTree (const Array <PluginDescription*>& allPlugins)
340 for (int i = 0; i < allPlugins.size(); ++i)
342 String path (allPlugins.getUnchecked(i)
343 ->fileOrIdentifier.replaceCharacter ('\\', '/')
344 .upToLastOccurrenceOf ("/", false, false));
346 if (path.substring (1, 2) == ":")
347 path = path.substring (2);
349 addPlugin (allPlugins.getUnchecked(i), path);
352 optimise();
355 void addToMenu (PopupMenu& m, const OwnedArray <PluginDescription>& allPlugins) const
357 int i;
358 for (i = 0; i < subFolders.size(); ++i)
360 const PluginFilesystemTree* const sub = subFolders.getUnchecked(i);
362 PopupMenu subMenu;
363 sub->addToMenu (subMenu, allPlugins);
365 #if JUCE_MAC
366 // avoid the special AU formatting nonsense on Mac..
367 m.addSubMenu (sub->folder.fromFirstOccurrenceOf (":", false, false), subMenu);
368 #else
369 m.addSubMenu (sub->folder, subMenu);
370 #endif
373 for (i = 0; i < plugins.size(); ++i)
375 PluginDescription* const plugin = plugins.getUnchecked(i);
377 m.addItem (allPlugins.indexOf (plugin) + menuIdBase,
378 plugin->name, true, false);
383 //==============================================================================
384 void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const
386 Array <PluginDescription*> sorted;
389 PluginSorter sorter;
390 sorter.method = sortMethod;
392 for (int i = 0; i < types.size(); ++i)
393 sorted.addSorted (sorter, types.getUnchecked(i));
396 if (sortMethod == sortByCategory
397 || sortMethod == sortByManufacturer)
399 String lastSubMenuName;
400 PopupMenu sub;
402 for (int i = 0; i < sorted.size(); ++i)
404 const PluginDescription* const pd = sorted.getUnchecked(i);
405 String thisSubMenuName (sortMethod == sortByCategory ? pd->category
406 : pd->manufacturerName);
408 if (! thisSubMenuName.containsNonWhitespaceChars())
409 thisSubMenuName = "Other";
411 if (thisSubMenuName != lastSubMenuName)
413 if (sub.getNumItems() > 0)
415 menu.addSubMenu (lastSubMenuName, sub);
416 sub.clear();
419 lastSubMenuName = thisSubMenuName;
422 sub.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false);
425 if (sub.getNumItems() > 0)
426 menu.addSubMenu (lastSubMenuName, sub);
428 else if (sortMethod == sortByFileSystemLocation)
430 PluginFilesystemTree root;
431 root.buildTree (sorted);
432 root.addToMenu (menu, types);
434 else
436 for (int i = 0; i < sorted.size(); ++i)
438 const PluginDescription* const pd = sorted.getUnchecked(i);
439 menu.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false);
444 int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const
446 const int i = menuResultCode - menuIdBase;
448 return isPositiveAndBelow (i, types.size()) ? i : -1;
451 END_JUCE_NAMESPACE