diff --git a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 6af4db7640..008c74753b 100644 --- a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -472,9 +472,9 @@ private: { for (int i = 0; i < pathList.getPath().getNumPaths(); ++i) { - auto f = pathList.getPath()[i]; + auto f = pathList.getPath().getRawString (i); - if (isStupidPath (f)) + if (File::isAbsolutePath (f) && isStupidPath (File (f))) { AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon, TRANS("Plugin Scanning"), @@ -483,7 +483,7 @@ private: "attempting to load unsuitable files.") + newLine + TRANS ("Are you sure you want to scan the folder \"XYZ\"?") - .replace ("XYZ", f.getFullPathName()), + .replace ("XYZ", f), TRANS ("Scan"), String(), nullptr, diff --git a/modules/juce_core/files/juce_FileSearchPath.cpp b/modules/juce_core/files/juce_FileSearchPath.cpp index 6b4ad3981b..9b786ac05e 100644 --- a/modules/juce_core/files/juce_FileSearchPath.cpp +++ b/modules/juce_core/files/juce_FileSearchPath.cpp @@ -63,7 +63,12 @@ int FileSearchPath::getNumPaths() const File FileSearchPath::operator[] (int index) const { - return File (directories[index]); + return File (getRawString (index)); +} + +String FileSearchPath::getRawString (int index) const +{ + return directories[index]; } String FileSearchPath::toString() const @@ -110,21 +115,30 @@ void FileSearchPath::addPath (const FileSearchPath& other) void FileSearchPath::removeRedundantPaths() { - for (int i = directories.size(); --i >= 0;) + std::vector reduced; + + for (const auto& directory : directories) { - const File d1 (directories[i]); - - for (int j = directories.size(); --j >= 0;) + const auto checkedIsChildOf = [&] (const auto& a, const auto& b) { - const File d2 (directories[j]); + return File::isAbsolutePath (a) && File::isAbsolutePath (b) && File (a).isAChildOf (b); + }; - if (i != j && (d1.isAChildOf (d2) || d1 == d2)) - { - directories.remove (i); - break; - } - } + const auto fContainsDirectory = [&] (const auto& f) + { + return f == directory || checkedIsChildOf (directory, f); + }; + + if (std::find_if (reduced.begin(), reduced.end(), fContainsDirectory) != reduced.end()) + continue; + + const auto directoryContainsF = [&] (const auto& f) { return checkedIsChildOf (f, directory); }; + + reduced.erase (std::remove_if (reduced.begin(), reduced.end(), directoryContainsF), reduced.end()); + reduced.push_back (directory); } + + directories = StringArray (reduced.data(), (int) reduced.size()); } void FileSearchPath::removeNonExistentPaths() @@ -172,4 +186,54 @@ bool FileSearchPath::isFileInPath (const File& fileToCheck, return false; } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class FileSearchPathTests : public UnitTest +{ +public: + FileSearchPathTests() : UnitTest ("FileSearchPath", UnitTestCategories::files) {} + + void runTest() override + { + beginTest ("removeRedundantPaths"); + { + #if JUCE_WINDOWS + const String prefix = "C:"; + #else + const String prefix = ""; + #endif + + { + FileSearchPath fsp { prefix + "/a/b/c/d;" + prefix + "/a/b/c/e;" + prefix + "/a/b/c" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), prefix + "/a/b/c"); + } + + { + FileSearchPath fsp { prefix + "/a/b/c;" + prefix + "/a/b/c/d;" + prefix + "/a/b/c/e" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), prefix + "/a/b/c"); + } + + { + FileSearchPath fsp { prefix + "/a/b/c/d;" + prefix + "/a/b/c;" + prefix + "/a/b/c/e" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), prefix + "/a/b/c"); + } + + { + FileSearchPath fsp { "%FOO%;" + prefix + "/a/b/c;%FOO%;" + prefix + "/a/b/c/d" }; + fsp.removeRedundantPaths(); + expectEquals (fsp.toString(), "%FOO%;" + prefix + "/a/b/c"); + } + } + } +}; + +static FileSearchPathTests fileSearchPathTests; + +#endif + } // namespace juce diff --git a/modules/juce_core/files/juce_FileSearchPath.h b/modules/juce_core/files/juce_FileSearchPath.h index 8b48162ba5..d789cf4133 100644 --- a/modules/juce_core/files/juce_FileSearchPath.h +++ b/modules/juce_core/files/juce_FileSearchPath.h @@ -71,10 +71,21 @@ public: /** Returns one of the folders in this search path. The file returned isn't guaranteed to actually be a valid directory. - @see getNumPaths + @see getNumPaths, getRawString */ File operator[] (int index) const; + /** Returns the unaltered text of the folder at the specified index. + + Unlike operator[], this function returns the exact text that was entered. It does not + attempt to convert the path into an absolute path. + + This may be useful if the directory string is expected to understand environment variables + or other placeholders that the File constructor doesn't necessarily understand. + @see operator[] + */ + String getRawString (int index) const; + /** Returns the search path as a semicolon-separated list of directories. */ String toString() const; diff --git a/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp b/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp index 65ceb66a39..1799e7382b 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp @@ -129,7 +129,7 @@ void FileSearchPathListComponent::paintListBoxItem (int rowNumber, Graphics& g, f.setHorizontalScale (0.9f); g.setFont (f); - g.drawText (path[rowNumber].getFullPathName(), + g.drawText (path.getRawString (rowNumber), 4, 0, width - 6, height, Justification::centredLeft, true); } @@ -145,7 +145,7 @@ void FileSearchPathListComponent::deleteKeyPressed (int row) void FileSearchPathListComponent::returnKeyPressed (int row) { - chooser = std::make_unique (TRANS("Change folder..."), path[row], "*"); + chooser = std::make_unique (TRANS("Change folder..."), path.getRawString (row), "*"); auto chooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories; chooser->launchAsync (chooserFlags, [this, row] (const FileChooser& fc) @@ -258,7 +258,7 @@ void FileSearchPathListComponent::moveSelection (int delta) if (currentRow != newRow) { - auto f = path[currentRow]; + const auto f = File::createFileWithoutCheckingPath (path.getRawString (currentRow)); path.remove (currentRow); path.add (f, newRow); listBox.selectRow (newRow);