| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE 6 technical preview.
 -    Copyright (c) 2020 - Raw Material Software Limited
 - 
 -    You may use this code under the terms of the GPL v3
 -    (see www.gnu.org/licenses).
 - 
 -    For this technical preview, this file is not subject to commercial licensing.
 - 
 -    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"
 - 
 - //==============================================================================
 - class AvailableModulesList  : private AsyncUpdater
 - {
 - public:
 -     using ModuleIDAndFolder     = std::pair<String, File>;
 -     using ModuleIDAndFolderList = std::vector<ModuleIDAndFolder>;
 - 
 -     AvailableModulesList() = default;
 - 
 -     //==============================================================================
 -     void scanPaths (const Array<File>& paths)
 -     {
 -         auto job = createScannerJob (paths);
 -         auto& ref = *job;
 - 
 -         removePendingAndAddJob (std::move (job));
 -         scanPool.waitForJobToFinish (&ref, -1);
 -     }
 - 
 -     void scanPathsAsync (const Array<File>& paths)
 -     {
 -         removePendingAndAddJob (createScannerJob (paths));
 -     }
 - 
 -     //==============================================================================
 -     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:
 -     //==============================================================================
 -     struct ModuleScannerJob  : public ThreadPoolJob
 -     {
 -         ModuleScannerJob (const Array<File>& paths,
 -                           std::function<void (const ModuleIDAndFolderList&)>&& callback)
 -             : ThreadPoolJob ("ModuleScannerJob"),
 -               pathsToScan (paths),
 -               completionCallback (std::move (callback))
 -         {
 -         }
 - 
 -         JobStatus runJob() override
 -         {
 -             ModuleIDAndFolderList list;
 - 
 -             for (auto& p : pathsToScan)
 -                 addAllModulesInFolder (p, list);
 - 
 -             if (! shouldExit())
 -             {
 -                 std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1,
 -                                                          const ModuleIDAndFolder& m2)
 -                 {
 -                     return m1.first.compareIgnoreCase (m2.first) < 0;
 -                 });
 - 
 -                 completionCallback (list);
 -             }
 - 
 -             return jobHasFinished;
 -         }
 - 
 -         static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list)
 -         {
 -             ModuleDescription m (path);
 - 
 -             if (m.isValid())
 -             {
 -                 list.push_back ({ m.getID(), path });
 -                 return true;
 -             }
 - 
 -             return false;
 -         }
 - 
 -         static void addAllModulesInSubfoldersRecursively (const File& path, int depth, ModuleIDAndFolderList& list)
 -         {
 -             if (depth > 0)
 -             {
 -                 for (const auto& iter : RangedDirectoryIterator (path, false, "*", File::findDirectories))
 -                 {
 -                     if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob())
 -                         if (job->shouldExit())
 -                             return;
 - 
 -                     auto childPath = iter.getFile();
 - 
 -                     if (! tryToAddModuleFromFolder (childPath, list))
 -                         addAllModulesInSubfoldersRecursively (childPath, depth - 1, list);
 -                 }
 -             }
 -         }
 - 
 -         static void addAllModulesInFolder (const File& path, ModuleIDAndFolderList& list)
 -         {
 -             if (! tryToAddModuleFromFolder (path, list))
 -             {
 -                 constexpr int subfolders = 3;
 -                 addAllModulesInSubfoldersRecursively (path, subfolders, list);
 -             }
 -         }
 - 
 -         Array<File> pathsToScan;
 -         std::function<void (const ModuleIDAndFolderList&)> completionCallback;
 -     };
 - 
 -     //==============================================================================
 -     void handleAsyncUpdate() override
 -     {
 -         listeners.call ([this] (Listener& l) { l.availableModulesChanged (this); });
 -     }
 - 
 -     std::unique_ptr<ThreadPoolJob> createScannerJob (const Array<File>& paths)
 -     {
 -         return std::make_unique<ModuleScannerJob> (paths, [this] (ModuleIDAndFolderList scannedModulesList)
 -         {
 -             {
 -                 const ScopedLock swapLock (lock);
 -                 modulesList.swap (scannedModulesList);
 -             }
 - 
 -             triggerAsyncUpdate();
 -         });
 -     }
 - 
 -     void removePendingAndAddJob (std::unique_ptr<ThreadPoolJob> jobToAdd)
 -     {
 -         scanPool.removeAllJobs (false, 100);
 -         scanPool.addJob (jobToAdd.release(), true);
 -     }
 - 
 -     //==============================================================================
 -     ThreadPool scanPool { 1 };
 - 
 -     ModuleIDAndFolderList modulesList;
 -     ListenerList<Listener> listeners;
 -     CriticalSection lock;
 - 
 -     //==============================================================================
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModulesList)
 - };
 
 
  |