diff --git a/gui/manualtest/projectfiledialog.md b/gui/manualtest/projectfiledialog.md index f11b030f246..ce7d39b1df0 100644 --- a/gui/manualtest/projectfiledialog.md +++ b/gui/manualtest/projectfiledialog.md @@ -4,6 +4,20 @@ Some manual testing in the project file dialog interface +## Test: Relative paths + +Ticket: #14983 + +1. Configure files/paths in project folder: + * import a projectfile + * add include paths in project folder + * exclude file/folder + +2. Save project + +EXPECTED: Relative paths should be used in the XML + + ## Test: Platform file pic8.xml Ticket: #14489 diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 9144f5a6117..716288d9695 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -1193,6 +1193,15 @@ QStringList ProjectFile::getSearchPaths(const QString& projectPath, const QStrin return ret; } +QString ProjectFile::getRelativePath(const QString &absolutePath) const +{ + const QDir dir(QFileInfo(mFilename).absolutePath()); + const QString relativePath(dir.relativeFilePath(absolutePath)); + if (relativePath.startsWith("../../..") || absolutePath.length() < relativePath.length()) + return absolutePath; + return relativePath; +} + QStringList ProjectFile::getSearchPaths(const QString& dir) const { const QFileInfo inf(mFilename); const QString applicationFilePath = QCoreApplication::applicationFilePath(); diff --git a/gui/projectfile.h b/gui/projectfile.h index 5a5e0ccb89b..e0b85582c99 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -447,6 +447,14 @@ class ProjectFile : public QObject { static QStringList getSearchPaths(const QString& projectPath, const QString& appPath, const QString& datadir, const QString& dir); + /** + * @brief Convert an absolute path to a path relative to this project's directory. + * If the relative path would need to walk up more than 2 parent folders + * (i.e. "../../...") the absolute path is returned unchanged instead. + * @param absolutePath Absolute path to convert. + */ + QString getRelativePath(const QString &absolutePath) const; + /** Set user includes in settings if non-empty */ void setSettingsUserIncludes(Settings &settings) const; diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 72e3d651471..abb7b343ec1 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -571,10 +571,7 @@ QString ProjectFileDialog::getExistingDirectory(const QString &caption, bool tra // Check if the path is relative to project file's path and if so // make it a relative path instead of absolute path. - const QDir dir(projectPath); - const QString relpath(dir.relativeFilePath(selectedDir)); - if (!relpath.startsWith("../..")) - selectedDir = relpath; + selectedDir = mProjectFile->getRelativePath(selectedDir); // Trailing slash.. if (trailingSlash && !selectedDir.endsWith('/')) @@ -631,7 +628,7 @@ void ProjectFileDialog::browseImportProject() dir.canonicalPath(), toFilterString(filters)); if (!fileName.isEmpty()) { - mUI->mEditImportProject->setText(dir.relativeFilePath(fileName)); + mUI->mEditImportProject->setText(mProjectFile->getRelativePath(fileName)); updatePathsAndDefines(); setProjectConfigurations(getProjectConfigs(fileName)); for (int row = 0; row < mUI->mListVsConfigs->count(); ++row) { @@ -652,7 +649,7 @@ void ProjectFileDialog::browseUserInclude() dir.canonicalPath(), toFilterString(filters)); if (!fileName.isEmpty()) { - mUI->mEditUserInclude->setText(dir.relativeFilePath(fileName)); + mUI->mEditUserInclude->setText(mProjectFile->getRelativePath(fileName)); } } @@ -891,7 +888,9 @@ void ProjectFileDialog::addExcludeFile() QMap filters; filters[tr("Source files")] = "*.c *.cpp"; filters[tr("All files")] = "*.*"; - addExcludePath(QFileDialog::getOpenFileName(this, tr("Exclude file"), dir.canonicalPath(), toFilterString(filters))); + QString fileName = QFileDialog::getOpenFileName(this, tr("Exclude file"), dir.canonicalPath(), toFilterString(filters)); + if (!fileName.isEmpty()) + addExcludePath(mProjectFile->getRelativePath(fileName)); } void ProjectFileDialog::editExcludePath() diff --git a/gui/test/projectfile/testprojectfile.cpp b/gui/test/projectfile/testprojectfile.cpp index efc1dce6c4f..1a66bd8de23 100644 --- a/gui/test/projectfile/testprojectfile.cpp +++ b/gui/test/projectfile/testprojectfile.cpp @@ -214,5 +214,45 @@ void TestProjectFile::emptyUserInclude() const QCOMPARE(settings.userIncludes.size(), 0); } +// Absolute path is made relative when it does not require walking up more than 3 parent folders +void TestProjectFile::getRelativePathRelative() const +{ + ProjectFile projectFile; + projectFile.setFilename("/some/path/sub/123.cppcheck"); + QCOMPARE(projectFile.getRelativePath("/some/path/externals/foo.cpp"), QString("../externals/foo.cpp")); +} + +// Absolute path is made relative even when it requires walking up 2 parent folders +void TestProjectFile::getRelativePathTwoUp() const +{ + ProjectFile projectFile; + projectFile.setFilename("/some/path/sub/123.cppcheck"); + QCOMPARE(projectFile.getRelativePath("/some/externals/foo.cpp"), QString("../../externals/foo.cpp")); +} + +// Absolute path is kept as-is when making it relative would require walking up more than 3 parent folders +void TestProjectFile::getRelativePathTooFarUp() const +{ + ProjectFile projectFile; + projectFile.setFilename("/some/path/sub/123.cppcheck"); + QCOMPARE(projectFile.getRelativePath("/other/deep/foo.cpp"), QString("/other/deep/foo.cpp")); +} + +// Absolute path in a subfolder of the project path is made relative without walking up at all +void TestProjectFile::getRelativePathSubfolder() const +{ + ProjectFile projectFile; + projectFile.setFilename("/some/path/sub/123.cppcheck"); + QCOMPARE(projectFile.getRelativePath("/some/path/sub/src/file1.c"), QString("src/file1.c")); +} + +// Absolute path is kept as-is when it is shorter than the relative path, even if it does not require walking up 3 or more parent folders +void TestProjectFile::getRelativePathAbsoluteShorter() const +{ + ProjectFile projectFile; + projectFile.setFilename("/ab/path/sub/123.cppcheck"); + QCOMPARE(projectFile.getRelativePath("/ab/foo.cpp"), QString("/ab/foo.cpp")); +} + QTEST_MAIN(TestProjectFile) diff --git a/gui/test/projectfile/testprojectfile.h b/gui/test/projectfile/testprojectfile.h index ceda2ff6b96..7203980eb7c 100644 --- a/gui/test/projectfile/testprojectfile.h +++ b/gui/test/projectfile/testprojectfile.h @@ -38,4 +38,10 @@ private slots: void getCheckingSuppressionsStar() const; void emptyUserInclude() const; + + void getRelativePathRelative() const; + void getRelativePathTwoUp() const; + void getRelativePathTooFarUp() const; + void getRelativePathSubfolder() const; + void getRelativePathAbsoluteShorter() const; };