/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #pragma once #include "jucer_ModuleDescription.h" #include //============================================================================== class AvailableModulesList final : private AsyncUpdater { public: using ModuleIDAndFolder = std::pair; using ModuleIDAndFolderList = std::vector; AvailableModulesList() = default; //============================================================================== void scanPaths (const Array& paths) { scanPathsAsync (paths); scanner = {}; } void scanPathsAsync (const Array& paths) { scanner = std::async (std::launch::async, [this, paths] { ModuleIDAndFolderList list; for (auto& p : paths) addAllModulesInFolder (p, list); std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1, const ModuleIDAndFolder& m2) { return m1.first.compareIgnoreCase (m2.first) < 0; }); { const ScopedLock swapLock (lock); if (list == modulesList) return; modulesList.swap (list); } triggerAsyncUpdate(); }); } //============================================================================== ModuleIDAndFolderList getAllModules() const { const ScopedLock readLock (lock); return modulesList; } ModuleIDAndFolder getModuleWithID (const String& id) const { const ScopedLock readLock (lock); for (auto& mod : modulesList) if (mod.first == id) return mod; return {}; } //============================================================================== void removeDuplicates (const ModuleIDAndFolderList& other) { const ScopedLock readLock (lock); const auto predicate = [&] (const ModuleIDAndFolder& entry) { return std::find (other.begin(), other.end(), entry) != other.end(); }; modulesList.erase (std::remove_if (modulesList.begin(), modulesList.end(), predicate), modulesList.end()); } //============================================================================== struct Listener { virtual ~Listener() = default; virtual void availableModulesChanged (AvailableModulesList* listThatHasChanged) = 0; }; void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); } void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); } private: //============================================================================== static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list) { ModuleDescription m (path); if (m.isValid() && std::none_of (list.begin(), list.end(), [&m] (const ModuleIDAndFolder& element) { return element.first == m.getID(); })) { list.push_back ({ m.getID(), path }); return true; } return false; } static void addAllModulesInFolder (const File& topLevelPath, ModuleIDAndFolderList& list) { struct FileAndDepth { File file; int depth; }; std::queue pathsToCheck; pathsToCheck.push ({ topLevelPath, 0 }); while (! pathsToCheck.empty()) { const auto path = pathsToCheck.front(); pathsToCheck.pop(); if (tryToAddModuleFromFolder (path.file, list) || path.depth == 3) continue; for (const auto& iter : RangedDirectoryIterator (path.file, false, "*", File::findDirectories)) { if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob()) if (job->shouldExit()) return; pathsToCheck.push ({ iter.getFile(), path.depth + 1 }); } } } //============================================================================== void handleAsyncUpdate() override { listeners.call ([this] (Listener& l) { l.availableModulesChanged (this); }); } //============================================================================== ModuleIDAndFolderList modulesList; ListenerList listeners; CriticalSection lock; std::future scanner; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModulesList) };