diff options
author | Marcus Tillmanns <marcus.tillmanns@qt.io> | 2022-05-31 11:16:44 +0200 |
---|---|---|
committer | Marcus Tillmanns <marcus.tillmanns@qt.io> | 2022-07-21 13:14:01 +0000 |
commit | 3462bc67be64d8caf59ed2f9a0ba6f5edddba3b9 (patch) | |
tree | 3c1b78f5a1709e754b1567be5bf5edd73f5288b0 | |
parent | a5d44fb32b7d66448d3b1a4f871957eff02107f5 (diff) |
filesystem: Add QFSEngine for filepaths
Change-Id: Ibd0c88c69863c0877138d8cc45541530c359bd9c
Reviewed-by: hjk <hjk@qt.io>
87 files changed, 2194 insertions, 192 deletions
diff --git a/src/app/main.cpp b/src/app/main.cpp index ca18cfcfc7e..0ba1ea6a05a 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -35,6 +35,7 @@ #include <utils/algorithm.h> #include <utils/environment.h> #include <utils/fileutils.h> +#include <utils/fsengine/fsengine.h> #include <utils/hostosinfo.h> #include <utils/optional.h> #include <utils/qtcsettings.h> @@ -451,6 +452,8 @@ int main(int argc, char **argv) Restarter restarter(argc, argv); Utils::Environment::systemEnvironment(); // cache system environment before we do any changes + Utils::FSEngine fileSystemEngine; + // Manually determine various command line options // We can't use the regular way of the plugin manager, // because settings can change the way plugin manager behaves diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index ae1c91364a8..9ad21c03bbf 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -57,6 +57,8 @@ add_qtc_library(Utils filewizardpage.cpp filewizardpage.h fixedsizeclicklabel.cpp fixedsizeclicklabel.h flowlayout.cpp flowlayout.h + fsengine/fsengine.cpp fsengine/fsengine.h + fsengine/fileiconprovider.cpp fsengine/fileiconprovider.h functiontraits.h futuresynchronizer.cpp futuresynchronizer.h fuzzymatcher.cpp fuzzymatcher.h @@ -247,6 +249,20 @@ extend_qtc_library(Utils CONDITION UNIX AND NOT APPLE touchbar/touchbar.cpp ) +extend_qtc_library(Utils + CONDITION TARGET Qt5::CorePrivate + DEPENDS Qt5::CorePrivate + DEFINES QTC_UTILS_WITH_FSENGINE + SOURCES fsengine/fsengine_impl.cpp + fsengine/fsengine_impl.h + fsengine/diriterator.h + fsengine/fileiteratordevicesappender.h + fsengine/rootinjectfsengine.h + fsengine/fixedlistfsengine.h + fsengine/fsenginehandler.cpp + fsengine/fsenginehandler.h +) + if (WIN32) add_qtc_executable(qtcreator_process_stub SOURCES process_stub_win.c @@ -263,3 +279,4 @@ if (WIN32) else() add_qtc_executable(qtcreator_process_stub SOURCES process_stub_unix.c) endif() + diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index cb7981b9f29..d1171c371ef 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -39,6 +39,7 @@ #include <QRegularExpression> #include <QStorageInfo> #include <QUrl> +#include <QStringView> #ifdef Q_OS_WIN #ifdef QTCREATOR_PCH_H @@ -480,9 +481,20 @@ QString FilePath::toString() const { if (m_scheme.isEmpty()) return m_data; + + if (m_data.startsWith('/')) + return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + hostEncoded(m_host) + m_data; + return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + hostEncoded(m_host) + "/./" + m_data; +} + +QString FilePath::toFSPathString() const +{ + if (m_scheme.isEmpty()) + return m_data; + if (m_data.startsWith('/')) - return m_scheme + "://" + hostEncoded(m_host) + m_data; - return m_scheme + "://" + hostEncoded(m_host) + "/./" + m_data; + return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + hostEncoded(m_host) + m_data; + return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + hostEncoded(m_host) + "/./" + m_data; } QUrl FilePath::toUrl() const @@ -504,6 +516,12 @@ void FileUtils::setDeviceFileHooks(const DeviceFileHooks &hooks) /// this path belongs to. QString FilePath::toUserOutput() const { + if (needsDevice()) { + if (m_data.startsWith('/')) + return m_scheme + "://" + hostEncoded(m_host) + m_data; + return m_scheme + "://" + hostEncoded(m_host) + "/./" + m_data; + } + FilePath tmp = *this; if (osType() == OsTypeWindows) tmp.m_data.replace('/', '\\'); @@ -941,6 +959,27 @@ FilePath FilePath::absoluteFilePath() const return result; } +QString FilePath::specialPath(SpecialPathComponent component) +{ + switch (component) { + case SpecialPathComponent::RootName: + return QLatin1String("__qtc_devices__"); + case SpecialPathComponent::RootPath: + return (QDir::rootPath() + "__qtc_devices__"); + case SpecialPathComponent::DeviceRootName: + return QLatin1String("device"); + case SpecialPathComponent::DeviceRootPath: + return QDir::rootPath() + "__qtc_devices__/device"; + } + + QTC_ASSERT(false, return {}); +} + +FilePath FilePath::specialFilePath(SpecialPathComponent component) +{ + return FilePath::fromString(specialPath(component)); +} + FilePath FilePath::normalizedPathName() const { FilePath result = *this; @@ -1005,28 +1044,88 @@ FilePath FilePath::fromString(const QString &filepath) void FilePath::setFromString(const QString &filename) { - if (filename.startsWith('/')) { - m_data = filename; // fast track: absolute local paths - } else { - int pos1 = filename.indexOf("://"); - if (pos1 >= 0) { - m_scheme = filename.left(pos1); - pos1 += 3; - int pos2 = filename.indexOf('/', pos1); - if (pos2 == -1) { - m_data = filename.mid(pos1); - } else { - m_host = filename.mid(pos1, pos2 - pos1); - m_host.replace("%2f", "/"); - m_host.replace("%25", "%"); - m_data = filename.mid(pos2); +#ifndef UTILS_FILEPATH_USE_REGEXP + static const QLatin1String qtcDevSlash("__qtc_devices__/"); + + const QStringView fileNameView(filename); + const QString rootPath = QDir::rootPath(); + + if (fileNameView.startsWith(rootPath, Qt::CaseInsensitive)) { // Absolute path ... + const QStringView withoutRootPath = fileNameView.mid(rootPath.size()); + if (withoutRootPath.startsWith(qtcDevSlash)) { // Starts with "/__qtc_devices__/" ... + const QStringView withoutQtcDeviceRoot = withoutRootPath.mid(qtcDevSlash.size()); + + const auto firstSlash = withoutQtcDeviceRoot.indexOf('/'); + + if (firstSlash != -1) { + m_scheme = withoutQtcDeviceRoot.left(firstSlash).toString(); + const auto secondSlash = withoutQtcDeviceRoot.indexOf('/', firstSlash + 1); + m_host = withoutQtcDeviceRoot.mid(firstSlash + 1, secondSlash - firstSlash - 1) + .toString(); + if (secondSlash != -1) { + const QStringView path = withoutQtcDeviceRoot.mid(secondSlash); + m_data = path.startsWith(QLatin1String("/./")) ? path.mid(3).toString() + : path.toString(); + return; + } + m_data = "/"; + + return; } - if (m_data.startsWith("/./")) - m_data = m_data.mid(3); - } else { - m_data = filename; // treat everything else as local, too. + m_scheme = ""; + m_host = ""; + m_data = filename; + + return; } } + + const auto firstSlash = filename.indexOf('/'); + const auto schemeEnd = filename.indexOf("://"); + if (schemeEnd != -1 && schemeEnd < firstSlash) { + // This is a pseudo Url, we can't use QUrl here sadly. + m_scheme = filename.left(schemeEnd); + const auto hostEnd = filename.indexOf('/', schemeEnd + 3); + m_host = filename.mid(schemeEnd + 3, hostEnd - schemeEnd - 3); + m_data = filename.mid(hostEnd); + + return; + } + + m_data = filename; + + return; +#else + // Convert the root path ( "/" or e.g. "c:/") to a regex match string ( e.g. ^[(?i)c(?-i)]:\/ ) + // (?i) will turn on case insensitivity, (?-i) will turn it off again. + static const QString rootPart = '^' + + QDir::rootPath() + .replace('/', "\\/") + .replace(QRegularExpression("([a-zA-Z])"), + R"((?i)[\1](?-i))"); + + static const QString pathPattern = rootPart + specialPath(SpecialPathComponent::RootName) + + QString(R"(\/([^\/]+)\/([^\/]+)(\/.*)?)"); + + static const QRegularExpression rePath(pathPattern); + static const QRegularExpression reUrl(R"(^\/?([^:]+):\/{2}([^\/]*)(\/.*))"); + + const auto m = filename.startsWith(specialPath(SpecialPathComponent::RootPath), + Qt::CaseInsensitive) + ? rePath.match(filename) + : reUrl.match(filename); + + if (m.hasMatch()) { + m_scheme = m.captured(1); + m_host = m.captured(2); + m_host.replace("%2f", "/"); + m_host.replace("%25", "%"); + + m_data = m.captured(3).isEmpty() ? "/" : m.captured(3); + } else { + m_data = filename; + } +#endif } /// Constructs a FilePath from \a filePath. The \a defaultExtension is appended diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index ad8ff1811d9..f6479685af2 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -78,6 +78,7 @@ public: QString toUserOutput() const; QString toString() const; + QString toFSPathString() const; QVariant toVariant() const; QUrl toUrl() const; @@ -210,6 +211,16 @@ public: [[nodiscard]] FilePath absolutePath() const; // Avoid. Use resolvePath(...)[.parent()] with proper base. [[nodiscard]] FilePath absoluteFilePath() const; // Avoid. Use resolvePath(...) with proper base. + enum class SpecialPathComponent { + RootName, + RootPath, + DeviceRootName, + DeviceRootPath, + }; + + [[nodiscard]] static QString specialPath(SpecialPathComponent component); + [[nodiscard]] static FilePath specialFilePath(SpecialPathComponent component); + private: friend class ::tst_fileutils; static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath); diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index bdd67a6609e..4f2f49445e6 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -29,6 +29,9 @@ #include "algorithm.h" #include "qtcassert.h" +#include "fsengine/fileiconprovider.h" +#include "fsengine/fsengine.h" + #include <QDataStream> #include <QDebug> #include <QOperatingSystemVersion> @@ -41,6 +44,7 @@ #ifdef QT_GUI_LIB #include <QMessageBox> #include <QRegularExpression> +#include <QGuiApplication> #endif #ifdef Q_OS_WIN @@ -438,8 +442,15 @@ FilePath FileUtils::getOpenFilePath(QWidget *parent, const FilePath &dir, const QString &filter, QString *selectedFilter, - QFileDialog::Options options) + QFileDialog::Options options, + bool fromDeviceIfShiftIsPressed) { +#ifdef QT_GUI_LIB + if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) { + return getOpenFilePathFromDevice(parent, caption, dir, filter, selectedFilter, options); + } +#endif + const QString result = QFileDialog::getOpenFileName(dialogParent(parent), caption, dir.toString(), @@ -493,6 +504,47 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent, return transform(result, &FilePath::fromString); } +FilePath FileUtils::getOpenFilePathFromDevice(QWidget *parent, + const QString &caption, + const FilePath &dir, + const QString &filter, + QString *selectedFilter, + QFileDialog::Options options) +{ + QFileDialog dialog; + dialog.setOptions(options | QFileDialog::DontUseNativeDialog); + dialog.setWindowTitle(caption); + dialog.setDirectory(dir.toString()); + dialog.setNameFilter(filter); + + QList<QUrl> sideBarUrls = Utils::transform(Utils::filtered(FSEngine::registeredDeviceRoots(), + [](const auto &filePath) { + return filePath.exists(); + }), + [](const auto &filePath) { + return QUrl::fromLocalFile( + filePath.toFSPathString()); + }); + dialog.setSidebarUrls(sideBarUrls); + dialog.setFileMode(QFileDialog::AnyFile); + + dialog.setIconProvider(Utils::FileIconProvider::iconProvider()); + + if (dialog.exec()) { + FilePaths filePaths = Utils::transform(dialog.selectedFiles(), [](const auto &path) { + return FilePath::fromString(path); + }); + + if (selectedFilter) { + *selectedFilter = dialog.selectedNameFilter(); + } + + return filePaths.first(); + } + + return {}; +} + // Used on 'ls' output on unix-like systems. void FileUtils::iterateLsOutput(const FilePath &base, const QStringList &entries, diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 995ebfb78b0..dd6a01bac80 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -152,7 +152,8 @@ public: const FilePath &dir = {}, const QString &filter = {}, QString *selectedFilter = nullptr, - QFileDialog::Options options = {}); + QFileDialog::Options options = {}, + bool fromDeviceIfShiftIsPressed = false); static FilePath getSaveFilePath(QWidget *parent, const QString &caption, @@ -172,6 +173,13 @@ public: const QString &filter = {}, QString *selectedFilter = nullptr, QFileDialog::Options options = {}); + + static FilePath getOpenFilePathFromDevice(QWidget *parent, + const QString &caption, + const FilePath &dir = {}, + const QString &filter = {}, + QString *selectedFilter = nullptr, + QFileDialog::Options options = {}); #endif }; diff --git a/src/libs/utils/fsengine/diriterator.h b/src/libs/utils/fsengine/diriterator.h new file mode 100644 index 00000000000..9439e820db7 --- /dev/null +++ b/src/libs/utils/fsengine/diriterator.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "../filepath.h" +#include "../stringutils.h" + +#include <QFileInfo> +#include <QString> + +#include <QtCore/private/qabstractfileengine_p.h> + +namespace Utils { +namespace Internal { + +class DirIterator : public QAbstractFileEngineIterator +{ +public: + DirIterator(FilePaths paths) + : QAbstractFileEngineIterator({}, {}) + , m_filePaths(std::move(paths)) + , it(m_filePaths.begin()) + {} + + // QAbstractFileEngineIterator interface +public: + QString next() override + { + if (it == m_filePaths.end()) + return QString(); + const QString r = currentFilePath(); + ++it; + return r; + } + bool hasNext() const override { return !m_filePaths.empty() && m_filePaths.end() != it + 1; } + QString currentFileName() const override { return chopIfEndsWith(it->toString(), '/'); } + + QFileInfo currentFileInfo() const override + { + return QFileInfo(chopIfEndsWith(it->toString(), '/')); + } + +private: + const FilePaths m_filePaths; + FilePaths::const_iterator it; +}; + +} // namespace Internal + +} // namespace Utils diff --git a/src/plugins/coreplugin/fileiconprovider.cpp b/src/libs/utils/fsengine/fileiconprovider.cpp index 217e2df8b90..0addee53861 100644 --- a/src/plugins/coreplugin/fileiconprovider.cpp +++ b/src/libs/utils/fsengine/fileiconprovider.cpp @@ -41,11 +41,14 @@ #include <QFileIconProvider> #include <QIcon> +#include <QLoggingCategory> using namespace Utils; +Q_LOGGING_CATEGORY(fileIconProvider, "qtc.core.fileiconprovider", QtWarningMsg) + /*! - \namespace Core::FileIconProvider + \namespace Utils::FileIconProvider \inmodule QtCreator \brief Provides functions for registering custom overlay icons for system icons. @@ -64,11 +67,9 @@ using namespace Utils; using Item = Utils::variant<QIcon, QString>; // icon or filename for the icon -namespace Core { +namespace Utils { namespace FileIconProvider { -enum { debug = 0 }; - static Utils::optional<QIcon> getIcon(QHash<QString, Item> &cache, const QString &key) { auto it = cache.constFind(key); @@ -128,8 +129,44 @@ public: // Mapping of file suffix to icon. mutable QHash<QString, Item> m_suffixCache; mutable QHash<QString, Item> m_filenameCache; + + // QAbstractFileIconProvider interface +public: + QIcon icon(IconType) const override; + QIcon icon(const QFileInfo &) const override; + QString type(const QFileInfo &) const override; }; +QIcon FileIconProviderImplementation::icon(IconType type) const +{ + return QFileIconProvider::icon(type); +} + +QIcon FileIconProviderImplementation::icon(const QFileInfo &fi) const +{ + return icon(FilePath::fromString(fi.filePath())); +} + +QString FileIconProviderImplementation::type(const QFileInfo &fi) const +{ + const FilePath fPath = FilePath::fromString(fi.filePath()); + if (fPath.needsDevice()) { + if (fPath.isDir()) { +#ifdef Q_OS_WIN + return QGuiApplication::translate("QAbstractFileIconProvider", "File Folder", "Match Windows Explorer"); +#else + return QGuiApplication::translate("QAbstractFileIconProvider", "Folder", "All other platforms"); +#endif + } + if (fPath.isExecutableFile()) { + return "Program"; + } + + return QFileIconProvider::type(fi); + } + return QFileIconProvider::type(fi); +} + FileIconProviderImplementation *instance() { static FileIconProviderImplementation theInstance; @@ -155,11 +192,20 @@ static const QIcon &dirIcon() QIcon FileIconProviderImplementation::icon(const FilePath &filePath) const { - if (debug) - qDebug() << "FileIconProvider::icon" << filePath.absoluteFilePath(); + qCDebug(fileIconProvider) << "FileIconProvider::icon" << filePath.absoluteFilePath(); + + if (filePath.isEmpty()) + return unknownFileIcon(); + + // Check if its one of the virtual devices directories + if (filePath.path().startsWith(FilePath::specialPath(FilePath::SpecialPathComponent::RootPath))) { + // If the filepath does not need a device, it is a virtual device directory + if (!filePath.needsDevice()) + return dirIcon(); + } + bool isDir = filePath.isDir(); - if (filePath.needsDevice()) - return isDir ? dirIcon() : unknownFileIcon(); + // Check for cached overlay icons by file suffix. const QString filename = !isDir ? filePath.fileName() : QString(); if (!filename.isEmpty()) { @@ -167,6 +213,7 @@ QIcon FileIconProviderImplementation::icon(const FilePath &filePath) const if (icon) return *icon; } + const QString suffix = !isDir ? filePath.suffix() : QString(); if (!suffix.isEmpty()) { const Utils::optional<QIcon> icon = getIcon(m_suffixCache, suffix); @@ -174,6 +221,9 @@ QIcon FileIconProviderImplementation::icon(const FilePath &filePath) const return *icon; } + if (filePath.needsDevice()) + return isDir ? dirIcon() : unknownFileIcon(); + // Get icon from OS (and cache it based on suffix!) QIcon icon; if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost()) @@ -263,7 +313,7 @@ QIcon directoryIcon(const QString &overlay) const QPixmap dirPixmap = QApplication::style()->standardIcon(QStyle::SP_DirIcon).pixmap(desiredSize); const QIcon overlayIcon(overlay); QIcon result; - result.addPixmap(Core::FileIconProvider::overlayIcon(dirPixmap, overlayIcon)); + result.addPixmap(FileIconProvider::overlayIcon(dirPixmap, overlayIcon)); return result; } diff --git a/src/plugins/coreplugin/fileiconprovider.h b/src/libs/utils/fsengine/fileiconprovider.h index 8089f36a1e5..44f54df4cf7 100644 --- a/src/plugins/coreplugin/fileiconprovider.h +++ b/src/libs/utils/fsengine/fileiconprovider.h @@ -25,33 +25,41 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../utils_global.h" -#include <QStyle> -#include <QFileIconProvider> +#ifdef QT_GUI_LIB -namespace Utils { class FilePath; } +#include <QFileIconProvider> +#include <QStyle> -namespace Core { +namespace Utils { +class FilePath; namespace FileIconProvider { // Access to the single instance -CORE_EXPORT QFileIconProvider *iconProvider(); +QTCREATOR_UTILS_EXPORT QFileIconProvider *iconProvider(); // Access to individual items -CORE_EXPORT QIcon icon(const Utils::FilePath &filePath); -CORE_EXPORT QIcon icon(QFileIconProvider::IconType type); +QTCREATOR_UTILS_EXPORT QIcon icon(const Utils::FilePath &filePath); +QTCREATOR_UTILS_EXPORT QIcon icon(QFileIconProvider::IconType type); // Register additional overlay icons -CORE_EXPORT QPixmap overlayIcon(const QPixmap &baseIcon, const QIcon &overlayIcon); -CORE_EXPORT QPixmap overlayIcon(QStyle::StandardPixmap baseIcon, const QIcon &overlayIcon, const QSize &size); -CORE_EXPORT void registerIconOverlayForSuffix(const QString &path, const QString &suffix); -CORE_EXPORT void registerIconOverlayForFilename(const QString &path, const QString &filename); -CORE_EXPORT void registerIconOverlayForMimeType(const QString &path, const QString &mimeType); -CORE_EXPORT void registerIconOverlayForMimeType(const QIcon &icon, const QString &mimeType); +QTCREATOR_UTILS_EXPORT QPixmap overlayIcon(const QPixmap &baseIcon, const QIcon &overlayIcon); +QTCREATOR_UTILS_EXPORT QPixmap overlayIcon(QStyle::StandardPixmap baseIcon, + const QIcon &overlayIcon, + const QSize &size); +QTCREATOR_UTILS_EXPORT void registerIconOverlayForSuffix(const QString &path, const QString &suffix); +QTCREATOR_UTILS_EXPORT void registerIconOverlayForFilename(const QString &path, + const QString &filename); +QTCREATOR_UTILS_EXPORT void registerIconOverlayForMimeType(const QString &path, + const QString &mimeType); +QTCREATOR_UTILS_EXPORT void registerIconOverlayForMimeType(const QIcon &icon, + const QString &mimeType); -CORE_EXPORT QIcon directoryIcon(const QString &overlay); +QTCREATOR_UTILS_EXPORT QIcon directoryIcon(const QString &overlay); } // namespace FileIconProvider -} // namespace Core +} // namespace Utils + +#endif diff --git a/src/libs/utils/fsengine/fileiteratordevicesappender.h b/src/libs/utils/fsengine/fileiteratordevicesappender.h new file mode 100644 index 00000000000..713ae247297 --- /dev/null +++ b/src/libs/utils/fsengine/fileiteratordevicesappender.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "../filepath.h" + +#include <QtCore/private/qabstractfileengine_p.h> + +namespace Utils { +namespace Internal { + +// Based on http://bloglitb.blogspot.com/2011/12/access-to-private-members-safer.htm +template<typename Tag, typename Tag::type M> +struct PrivateAccess +{ + friend typename Tag::type get(Tag) { return M; } +}; + +struct QAFEITag +{ + using type = void (QAbstractFileEngineIterator::*)(const QString &); + friend type get(QAFEITag); +}; + +template struct PrivateAccess<QAFEITag, &QAbstractFileEngineIterator::setPath>; + +class FileIteratorWrapper : public QAbstractFileEngineIterator +{ + enum class State { + NotIteratingRoot, + IteratingRoot, + BaseIteratorEnd, + Ended, + }; + +public: + FileIteratorWrapper(std::unique_ptr<QAbstractFileEngineIterator> &&baseIterator, + QDir::Filters filters, + const QStringList &filterNames) + : QAbstractFileEngineIterator(filters, filterNames) + , m_baseIterator(std::move(baseIterator)) + {} + +public: + QString next() override + { + if (m_status == State::Ended) + return QString(); + + setPath(); + checkStatus(); + + if (m_status == State::BaseIteratorEnd) { + m_status = State::Ended; + return "__qtc__devices__"; + } + + return m_baseIterator->next(); + } + bool hasNext() const override + { + if (m_status == State::Ended) + return false; + + setPath(); + checkStatus(); + + if (m_status == State::BaseIteratorEnd) + return true; + + return m_baseIterator->hasNext(); + } + QString currentFileName() const override + { + if (m_status == State::Ended) + return FilePath::specialPath(FilePath::SpecialPathComponent::RootPath); + + setPath(); + checkStatus(); + return m_baseIterator->currentFileName(); + } + QFileInfo currentFileInfo() const override + { + if (m_status == State::Ended) + return QFileInfo(FilePath::specialPath(FilePath::SpecialPathComponent::RootPath)); + setPath(); + checkStatus(); + return m_baseIterator->currentFileInfo(); + } + +private: + void setPath() const + { + if (!m_hasSetPath) { + const QString p = path(); + if (p.toLower() == QDir::rootPath()) + m_status = State::IteratingRoot; + + ((*m_baseIterator).*get(QAFEITag()))(p); + m_hasSetPath = true; + } + } + + void checkStatus() const + { + if (m_status == State::NotIteratingRoot) { + return; + } + if (m_status == State::IteratingRoot) { + if (m_baseIterator->hasNext() == false) { + m_status = State::BaseIteratorEnd; + } + } + } + +private: + std::unique_ptr<QAbstractFileEngineIterator> m_baseIterator; + mutable bool m_hasSetPath{false}; + mutable State m_status{State::NotIteratingRoot}; +}; + +} // namespace Internal +} // namespace Utils diff --git a/src/libs/utils/fsengine/fixedlistfsengine.h b/src/libs/utils/fsengine/fixedlistfsengine.h new file mode 100644 index 00000000000..69460a649c0 --- /dev/null +++ b/src/libs/utils/fsengine/fixedlistfsengine.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "diriterator.h" + +#include "../filepath.h" +#include "../stringutils.h" + +#include <QtCore/private/qabstractfileengine_p.h> + +namespace Utils { +namespace Internal { + +class FixedListFSEngine : public QAbstractFileEngine +{ + const FilePath m_filePath; + FilePaths m_children; + +public: + FixedListFSEngine(FilePath path, const FilePaths children) + : m_filePath(std::move(path)) + , m_children(std::move(children)) + { + m_children.prepend(m_filePath.pathAppended(".")); + } + + // QAbstractFileEngine interface +public: + bool isRelativePath() const override { return false; } + FileFlags fileFlags(FileFlags /*type*/) const override + { + return FileFlag::DirectoryType | FileFlag::ExistsFlag | FileFlag::ReadGroupPerm + | FileFlag::ReadUserPerm | FileFlag::ReadOwnerPerm | FileFlag::ReadOtherPerm; + } + QString fileName(FileName file) const override + { + switch (file) { + case QAbstractFileEngine::AbsoluteName: + case QAbstractFileEngine::DefaultName: + case QAbstractFileEngine::CanonicalName: + return chopIfEndsWith(m_filePath.toString(), '/'); + break; + case QAbstractFileEngine::BaseName: + return m_filePath.baseName(); + break; + case QAbstractFileEngine::PathName: + case QAbstractFileEngine::AbsolutePathName: + case QAbstractFileEngine::CanonicalPathName: + return chopIfEndsWith(m_filePath.parentDir().toString(), '/'); + break; + + default: + // case QAbstractFileEngine::LinkName: + // case QAbstractFileEngine::BundleName: + // case QAbstractFileEngine::JunctionName: + return {}; + break; + } + + return QAbstractFileEngine::fileName(file); + } + Iterator *beginEntryList(QDir::Filters /*filters*/, const QStringList & /*filterNames*/) override + { + return new DirIterator(m_children); + } +}; + +} // namespace Internal +} // namespace Utils diff --git a/src/libs/utils/fsengine/fsengine.cpp b/src/libs/utils/fsengine/fsengine.cpp new file mode 100644 index 00000000000..cbfc2821793 --- /dev/null +++ b/src/libs/utils/fsengine/fsengine.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "fsengine.h" + +#ifdef QTC_UTILS_WITH_FSENGINE +#include "fsenginehandler.h" +#else +class Utils::Internal::FSEngineHandler +{}; +#endif + +#include <memory> + +namespace Utils { + +FSEngine::FSEngine() + : m_engineHandler(std::make_unique<Internal::FSEngineHandler>()) +{} + +FSEngine::~FSEngine() {} + +bool FSEngine::isAvailable() +{ +#ifdef QTC_UTILS_WITH_FSENGINE + return true; +#else + return false; +#endif +} + +FilePaths FSEngine::registeredDeviceRoots() +{ + return FSEngine::deviceRoots(); +} + +void FSEngine::addDevice(const FilePath &deviceRoot) +{ + deviceRoots().append(deviceRoot); +} + +void FSEngine::removeDevice(const FilePath &deviceRoot) +{ + deviceRoots().removeAll(deviceRoot); +} + +FilePaths &FSEngine::deviceRoots() +{ + static FilePaths g_deviceRoots; + return g_deviceRoots; +} + +QStringList &FSEngine::deviceSchemes() +{ + static QStringList g_deviceSchemes {"device"}; + return g_deviceSchemes; +} + +void FSEngine::registerDeviceScheme(const QString &scheme) +{ + deviceSchemes().append(scheme); +} + +void FSEngine::unregisterDeviceScheme(const QString &scheme) +{ + deviceSchemes().removeAll(scheme); +} + +QStringList FSEngine::registeredDeviceSchemes() +{ + return FSEngine::deviceSchemes(); +} + +} // namespace Utils diff --git a/src/libs/utils/fsengine/fsengine.h b/src/libs/utils/fsengine/fsengine.h new file mode 100644 index 00000000000..4c6326038e3 --- /dev/null +++ b/src/libs/utils/fsengine/fsengine.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "../filepath.h" +#include "../utils_global.h" + +#include <memory> + +namespace Utils { + +namespace Internal { +class FSEngineHandler; +} + +class QTCREATOR_UTILS_EXPORT FSEngine +{ + friend class Internal::FSEngineHandler; + +public: + FSEngine(); + ~FSEngine(); + +public: + static bool isAvailable(); + + static Utils::FilePaths registeredDeviceRoots(); + static void addDevice(const Utils::FilePath &deviceRoot); + static void removeDevice(const Utils::FilePath &deviceRoot); + + static void registerDeviceScheme(const QString &scheme); + static void unregisterDeviceScheme(const QString &scheme); + static QStringList registeredDeviceSchemes(); + +private: + static Utils::FilePaths &deviceRoots(); + static QStringList &deviceSchemes(); + +private: + std::unique_ptr<Internal::FSEngineHandler> m_engineHandler; +}; + +} // namespace Utils diff --git a/src/libs/utils/fsengine/fsengine_impl.cpp b/src/libs/utils/fsengine/fsengine_impl.cpp new file mode 100644 index 00000000000..f7129ad8886 --- /dev/null +++ b/src/libs/utils/fsengine/fsengine_impl.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "fsengine_impl.h" + +#include "diriterator.h" + +#include "../filepath.h" +#include "../qtcassert.h" + +#include <QIODevice> +#include <QDateTime> + +namespace Utils { + +namespace Internal { + +FSEngineImpl::FSEngineImpl(FilePath filePath) + : m_filePath(std::move(filePath)) +{} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) +bool FSEngineImpl::open(QIODeviceBase::OpenMode openMode, std::optional<QFile::Permissions>) +#else +bool FSEngineImpl::open(QIODevice::OpenMode openMode) +#endif +{ + QTC_ASSERT(m_tempStorage.open(), return false); + + bool read = openMode & QIODevice::ReadOnly; + bool write = openMode & QIODevice::WriteOnly; + bool append = openMode & QIODevice::Append; + + if (!write && !m_filePath.exists()) + return false; + + if (openMode & QIODevice::NewOnly && m_filePath.exists()) + return false; + + if (read || append) { + QTC_ASSERT(m_tempStorage.write(m_filePath.fileContents()) >= 0, return false); + + if (!append) + m_tempStorage.seek(0); + } + + if (write && !append) + m_hasChangedContent = true; + + return true; +} + +bool FSEngineImpl::close() +{ + QTC_ASSERT(flush(), return false); + m_tempStorage.close(); + return true; +} + +bool FSEngineImpl::flush() +{ + return syncToDisk(); +} + +bool FSEngineImpl::syncToDisk() +{ + if (m_hasChangedContent) { + const qint64 oldPos = m_tempStorage.pos(); + QTC_ASSERT(m_tempStorage.seek(0), return false); + QTC_ASSERT(m_filePath.writeFileContents(m_tempStorage.readAll()), return false); + m_tempStorage.seek(oldPos); + m_hasChangedContent = false; + return true; + } + + return true; +} + +qint64 FSEngineImpl::size() const +{ + return m_filePath.fileSize(); +} + +qint64 FSEngineImpl::pos() const +{ + return m_tempStorage.pos(); +} + +bool FSEngineImpl::seek(qint64 pos) +{ + return m_tempStorage.seek(pos); +} + +bool FSEngineImpl::isSequential() const +{ + return false; +} + +bool FSEngineImpl::remove() +{ + return m_filePath.removeRecursively(); +} + +bool FSEngineImpl::copy(const QString &newName) +{ + return m_filePath.copyFile(FilePath::fromString(newName)); +} + +bool FSEngineImpl::rename(const QString &newName) +{ + return m_filePath.renameFile(FilePath::fromString(newName)); +} + +bool FSEngineImpl::renameOverwrite(const QString &newName) +{ + Q_UNUSED(newName) + return false; +} + +bool FSEngineImpl::link(const QString &newName) +{ + Q_UNUSED(newName) + return false; +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) +bool FSEngineImpl::mkdir(const QString &dirName, bool createParentDirectories, + std::optional<QFile::Permissions>) const +#else +bool FSEngineImpl::mkdir(const QString &dirName, bool createParentDirectories) const +#endif +{ + Q_UNUSED(createParentDirectories) + return FilePath::fromString(dirName).createDir(); +} + +bool FSEngineImpl::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + if (recurseParentDirectories) + return false; + + return m_filePath.pathAppended(dirName).removeRecursively(); +} + +bool FSEngineImpl::setSize(qint64 size) +{ + return m_tempStorage.resize(size); +} + +bool FSEngineImpl::caseSensitive() const +{ + // TODO? + return true; +} + +bool FSEngineImpl::isRelativePath() const +{ + return false; +} + +QStringList FSEngineImpl::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + QStringList result; + m_filePath.iterateDirectory( + [&result](const FilePath &p) { + result.append(p.toFSPathString()); + return true; + }, + {filterNames, filters}); + return result; +} + +QAbstractFileEngine::FileFlags FSEngineImpl::fileFlags(FileFlags type) const +{ + FileFlags result{0}; + + if (type & FileInfoAll && m_filePath.exists()) { + result |= QAbstractFileEngine::ExistsFlag; + + if (type & DirectoryType && m_filePath.isDir()) + result |= QAbstractFileEngine::DirectoryType; + if (type & FileType && m_filePath.isFile()) + result |= QAbstractFileEngine::FileType; + + if (type & PermsMask) { + result |= FileFlags::fromInt(m_filePath.permissions().toInt()); + } + } + + return result; +} + +bool FSEngineImpl::setPermissions(uint /*perms*/) +{ + return false; +} + +QByteArray FSEngineImpl::id() const +{ + return QAbstractFileEngine::id(); +} + +QString FSEngineImpl::fileName(FileName file) const +{ + switch (file) { + case QAbstractFileEngine::AbsoluteName: + case QAbstractFileEngine::DefaultName: + return m_filePath.toFSPathString(); + break; + case QAbstractFileEngine::BaseName: + return m_filePath.baseName(); + break; + case QAbstractFileEngine::PathName: + case QAbstractFileEngine::AbsolutePathName: + return m_filePath.parentDir().toFSPathString(); + break; + case QAbstractFileEngine::CanonicalName: + return m_filePath.canonicalPath().toFSPathString(); + break; + case QAbstractFileEngine::CanonicalPathName: + return m_filePath.canonicalPath().parentDir().toFSPathString(); + break; + default: + // case QAbstractFileEngine::LinkName: + // case QAbstractFileEngine::BundleName: + // case QAbstractFileEngine::JunctionName: + return {}; + break; + + } + + return QAbstractFileEngine::fileName(file); +} + +uint FSEngineImpl::ownerId(FileOwner) const +{ + return 1; +} + +QString FSEngineImpl::owner(FileOwner) const +{ + return "<unknown>"; +} + +bool FSEngineImpl::setFileTime(const QDateTime &newDate, FileTime time) +{ + Q_UNUSED(newDate) + Q_UNUSED(time) + return false; +} + +QDateTime FSEngineImpl::fileTime(FileTime time) const +{ + Q_UNUSED(time) + return m_filePath.lastModified(); +} + +void FSEngineImpl::setFileName(const QString &file) +{ + close(); + m_filePath = FilePath::fromString(file); +} + +int FSEngineImpl::handle() const +{ + return 0; +} + +bool FSEngineImpl::cloneTo(QAbstractFileEngine *target) +{ + return QAbstractFileEngine::cloneTo(target); +} + +QAbstractFileEngine::Iterator *FSEngineImpl::beginEntryList(QDir::Filters filters, + const QStringList &filterNames) +{ + FilePaths paths{m_filePath.pathAppended(".")}; + m_filePath.iterateDirectory( + [&paths](const FilePath &p) { + paths.append(p); + return true; + }, + {filterNames, filters}); + + return new DirIterator(paths); +} + +QAbstractFileEngine::Iterator *FSEngineImpl::endEntryList() +{ + return nullptr; +} + +qint64 FSEngineImpl::read(char *data, qint64 maxlen) +{ + return m_tempStorage.read(data, maxlen); +} + +qint64 FSEngineImpl::readLine(char *data, qint64 maxlen) +{ + return m_tempStorage.readLine(data, maxlen); +} + +qint64 FSEngineImpl::write(const char *data, qint64 len) +{ + qint64 bytesWritten = m_tempStorage.write(data, len); + + if (bytesWritten > 0) + m_hasChangedContent = true; + + return bytesWritten; +} + +bool FSEngineImpl::extension(Extension extension, + const ExtensionOption *option, + ExtensionReturn *output) +{ + Q_UNUSED(extension) + Q_UNUSED(option) + Q_UNUSED(output) + return false; +} + +bool FSEngineImpl::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension) + return false; +} + +} // namespace Internal +} // namespace Utils diff --git a/src/libs/utils/fsengine/fsengine_impl.h b/src/libs/utils/fsengine/fsengine_impl.h new file mode 100644 index 00000000000..cf84edd72b1 --- /dev/null +++ b/src/libs/utils/fsengine/fsengine_impl.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "../filepath.h" + +#include <QtCore/private/qabstractfileengine_p.h> + +#include <QTemporaryFile> + +namespace Utils { +namespace Internal { + +class FSEngineImpl : public QAbstractFileEngine +{ +public: + FSEngineImpl(FilePath filePath); + +public: +#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) + bool open(QIODeviceBase::OpenMode openMode, + std::optional<QFile::Permissions> permissions = std::nullopt) override; + bool mkdir(const QString &dirName, bool createParentDirectories, + std::optional<QFile::Permissions> permissions = std::nullopt) const override; +#else + bool open(QIODevice::OpenMode openMode) override; + bool mkdir(const QString &dirName, bool createParentDirectories) const override; +#endif + bool close() override; + bool flush() override; + bool syncToDisk() override; + qint64 size() const override; + qint64 pos() const override; + bool seek(qint64 pos) override; + bool isSequential() const override; + bool remove() override; + bool copy(const QString &newName) override; + bool rename(const QString &newName) override; + bool renameOverwrite(const QString &newName) override; + bool link(const QString &newName) override; + bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; + bool setSize(qint64 size) override; + bool caseSensitive() const override; + bool isRelativePath() const override; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override; + FileFlags fileFlags(FileFlags type) const override; + bool setPermissions(uint perms) override; + QByteArray id() const override; + QString fileName(FileName file) const override; + uint ownerId(FileOwner) const override; + QString owner(FileOwner) const override; + bool setFileTime(const QDateTime &newDate, FileTime time) override; + QDateTime fileTime(FileTime time) const override; + void setFileName(const QString &file) override; + int handle() const override; + bool cloneTo(QAbstractFileEngine *target) override; + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; + Iterator *endEntryList() override; + qint64 read(char *data, qint64 maxlen) override; + qint64 readLine(char *data, qint64 maxlen) override; + qint64 write(const char *data, qint64 len) override; + bool extension(Extension extension, + const ExtensionOption *option, + ExtensionReturn *output) override; + bool supportsExtension(Extension extension) const override; + +private: + FilePath m_filePath; + QTemporaryFile m_tempStorage; + + bool m_hasChangedContent{false}; +}; + +} // namespace Internal +} // namespace Utils diff --git a/src/libs/utils/fsengine/fsenginehandler.cpp b/src/libs/utils/fsengine/fsenginehandler.cpp new file mode 100644 index 00000000000..b66314bd3cd --- /dev/null +++ b/src/libs/utils/fsengine/fsenginehandler.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "fsenginehandler.h" + +#include "fixedlistfsengine.h" +#include "fsengine_impl.h" +#include "rootinjectfsengine.h" + +#include "fsengine.h" + +#include "../algorithm.h" + +namespace Utils { + +namespace Internal { + +QAbstractFileEngine *FSEngineHandler::create(const QString &fileName) const +{ + if (fileName.startsWith(':')) + return nullptr; + + QString fixedFileName = fileName; + + if (fileName.startsWith("//")) { + fixedFileName = fixedFileName.mid(1); + } + if (fixedFileName == FilePath::specialPath(FilePath::SpecialPathComponent::RootPath)) { + const FilePaths paths + = Utils::transform(FSEngine::registeredDeviceSchemes(), [](const QString &scheme) { + return FilePath::specialFilePath(FilePath::SpecialPathComponent::RootPath) + .pathAppended(scheme); + }); + + return new FixedListFSEngine(FilePath::specialFilePath( + FilePath::SpecialPathComponent::RootPath), + paths); + } + + if (fixedFileName.startsWith(FilePath::specialPath(FilePath::SpecialPathComponent::RootPath))) { + const QStringList deviceSchemes = FSEngine::registeredDeviceSchemes(); + for (const QString &scheme : deviceSchemes) { + if (fixedFileName + == FilePath::specialFilePath(FilePath::SpecialPathComponent::RootPath) + .pathAppended(scheme) + .toString()) { + const FilePaths filteredRoots = Utils::filtered(FSEngine::deviceRoots(), + [scheme](const FilePath &root) { + return root.scheme() == scheme; + }); + + return new FixedListFSEngine(FilePath::specialFilePath( + FilePath::SpecialPathComponent::RootPath) + .pathAppended(scheme), + filteredRoots); + } + } + } + + FilePath filePath = FilePath::fromString(fixedFileName); + if (filePath.needsDevice()) { + return new FSEngineImpl(filePath); + } + + if (fixedFileName.compare(QDir::rootPath(), Qt::CaseInsensitive) == 0) { + return new RootInjectFSEngine(fixedFileName); + } + + return nullptr; +} + +} // namespace Internal + +} // namespace Utils diff --git a/src/libs/utils/fsengine/fsenginehandler.h b/src/libs/utils/fsengine/fsenginehandler.h new file mode 100644 index 00000000000..d65e1de2fed --- /dev/null +++ b/src/libs/utils/fsengine/fsenginehandler.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QtCore/private/qabstractfileengine_p.h> + +namespace Utils { + +namespace Internal { + +class FSEngineHandler : public QAbstractFileEngineHandler +{ +public: + QAbstractFileEngine *create(const QString &fileName) const override; +}; + +} // namespace Internal + +} // namespace Utils diff --git a/src/libs/utils/fsengine/rootinjectfsengine.h b/src/libs/utils/fsengine/rootinjectfsengine.h new file mode 100644 index 00000000000..4a5d48fc6aa --- /dev/null +++ b/src/libs/utils/fsengine/rootinjectfsengine.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "fileiteratordevicesappender.h" + +#include <QtCore/private/qfsfileengine_p.h> + +namespace Utils { +namespace Internal { + +class RootInjectFSEngine : public QFSFileEngine +{ +public: + using QFSFileEngine::QFSFileEngine; + +public: + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override + { + std::unique_ptr<QAbstractFileEngineIterator> baseIterator( + QFSFileEngine::beginEntryList(filters, filterNames)); + return new FileIteratorWrapper(std::move(baseIterator), filters, filterNames); + } +}; + +} // namespace Internal +} // namespace Utils diff --git a/src/libs/utils/mimetypes/mimedatabase.cpp b/src/libs/utils/mimetypes/mimedatabase.cpp index 7bf0a4c8fc3..54f8030c48e 100644 --- a/src/libs/utils/mimetypes/mimedatabase.cpp +++ b/src/libs/utils/mimetypes/mimedatabase.cpp @@ -49,14 +49,14 @@ #include <utils/fileutils.h> -#include <QtCore/QCoreApplication> -#include <QtCore/QFile> -#include <QtCore/QFileInfo> -#include <QtCore/QSet> -#include <QtCore/QBuffer> -#include <QtCore/QUrl> -#include <QtCore/QStack> -#include <QtCore/QDebug> +#include <QCoreApplication> +#include <QFile> +#include <QFileInfo> +#include <QSet> +#include <QBuffer> +#include <QUrl> +#include <QStack> +#include <QDebug> #include <algorithm> #include <functional> diff --git a/src/libs/utils/mimetypes2/mimedatabase.cpp b/src/libs/utils/mimetypes2/mimedatabase.cpp index 5a0ced57d96..4e1cf979e93 100644 --- a/src/libs/utils/mimetypes2/mimedatabase.cpp +++ b/src/libs/utils/mimetypes2/mimedatabase.cpp @@ -49,12 +49,12 @@ #include "algorithm.h" -#include <QtCore/QFile> -#include <QtCore/QFileInfo> -#include <QtCore/QStandardPaths> -#include <QtCore/QBuffer> -#include <QtCore/QUrl> -#include <QtCore/QDebug> +#include <QFile> +#include <QFileInfo> +#include <QStandardPaths> +#include <QBuffer> +#include <QUrl> +#include <QDebug> #include <algorithm> #include <functional> diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index 7a31cf8318d..3e304496c34 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -33,6 +33,7 @@ #include "qtcassert.h" #include "qtcprocess.h" +#include <QGuiApplication> #include <QFileDialog> #include <QHBoxLayout> #include <QMenu> @@ -194,6 +195,7 @@ public: QList<QAbstractButton *> m_buttons; const MacroExpander *m_macroExpander = globalMacroExpander(); std::function<void()> m_openTerminal; + bool m_allowPathFromDevice = false; }; PathChooserPrivate::PathChooserPrivate() @@ -420,40 +422,46 @@ void PathChooser::slotBrowse() case PathChooser::Directory: case PathChooser::ExistingDirectory: newPath = FileUtils::getExistingDirectory(this, - makeDialogTitle(tr("Choose Directory")), predefined); + makeDialogTitle(tr("Choose Directory")), + predefined); break; case PathChooser::ExistingCommand: case PathChooser::Command: newPath = FileUtils::getOpenFilePath(this, - makeDialogTitle(tr("Choose Executable")), predefined, d->m_dialogFilter); + makeDialogTitle(tr("Choose Executable")), + predefined, + d->m_dialogFilter, + nullptr, + {}, + d->m_allowPathFromDevice); newPath = appBundleExpandedPath(newPath); break; case PathChooser::File: // fall through newPath = FileUtils::getOpenFilePath(this, - makeDialogTitle(tr("Choose File")), predefined, d->m_dialogFilter); + makeDialogTitle(tr("Choose File")), + predefined, + d->m_dialogFilter, + nullptr, + {}, + d->m_allowPathFromDevice); newPath = appBundleExpandedPath(newPath); break; case PathChooser::SaveFile: newPath = FileUtils::getSaveFilePath(this, - makeDialogTitle(tr("Choose File")), predefined, d->m_dialogFilter); + makeDialogTitle(tr("Choose File")), + predefined, + d->m_dialogFilter); break; case PathChooser::Any: { - QFileDialog dialog(this); - dialog.setFileMode(QFileDialog::AnyFile); - dialog.setWindowTitle(makeDialogTitle(tr("Choose File"))); - if (predefined.exists()) - dialog.setDirectory(predefined.absolutePath().toDir()); - // FIXME: fix QFileDialog so that it filters properly: lib*.a - dialog.setNameFilter(d->m_dialogFilter); - if (dialog.exec() == QDialog::Accepted) { - // probably loop here until the *.framework dir match - QStringList paths = dialog.selectedFiles(); - if (!paths.isEmpty()) - newPath = FilePath::fromString(paths.at(0)); - } + newPath = FileUtils::getOpenFilePath(this, + makeDialogTitle(tr("Choose File")), + predefined, + d->m_dialogFilter, + nullptr, + {}, + d->m_allowPathFromDevice); break; - } - + } default: break; } @@ -758,4 +766,14 @@ void PathChooser::setCommandVersionArguments(const QStringList &arguments) } } +void PathChooser::setAllowPathFromDevice(bool allow) +{ + d->m_allowPathFromDevice = allow; +} + +bool PathChooser::allowPathFromDevice() const +{ + return d->m_allowPathFromDevice; +} + } // namespace Utils diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index 855a594ff94..0bf9d1316ad 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -158,6 +158,9 @@ public: // setting an empty QString will disable this and clear the placeHolderText void setDefaultValue(const QString &defaultValue); + void setAllowPathFromDevice(bool allow); + bool allowPathFromDevice() const; + private: QString rawPath() const; // The raw unexpanded input. bool validatePath(FancyLineEdit *edit, QString *errorMessage) const; diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index 30f5c805aa8..fac6c6b16ce 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -28,6 +28,7 @@ #include "algorithm.h" #include "hostosinfo.h" #include "qtcassert.h" +#include "filepath.h" #ifdef QT_WIDGETS_LIB #include <QApplication> @@ -119,6 +120,9 @@ QTCREATOR_UTILS_EXPORT QString withTildeHomePath(const QString &path) if (HostOsInfo::isWindowsHost()) return path; + if (FilePath::fromString(path).needsDevice()) + return path; + static const QString homePath = QDir::homePath(); QFileInfo fi(QDir::cleanPath(path)); @@ -475,7 +479,6 @@ QTCREATOR_UTILS_EXPORT QString languageNameFromLanguageCode(const QString &langu } #ifdef QT_WIDGETS_LIB - QTCREATOR_UTILS_EXPORT void setClipboardAndSelection(const QString &text) { QClipboard *clipboard = QApplication::clipboard(); @@ -483,7 +486,14 @@ QTCREATOR_UTILS_EXPORT void setClipboardAndSelection(const QString &text) if (clipboard->supportsSelection()) clipboard->setText(text, QClipboard::Selection); } - #endif +QTCREATOR_UTILS_EXPORT QString chopIfEndsWith(QString str, QChar c) +{ + if (str.endsWith(c)) + str.chop(1); + + return str; +} + } // namespace Utils diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index fd29223967b..e88a6adbd2c 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -130,4 +130,6 @@ QTCREATOR_UTILS_EXPORT void setClipboardAndSelection(const QString &text); #endif +QTCREATOR_UTILS_EXPORT QString chopIfEndsWith(QString str, QChar c); + } // namespace Utils diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 71fadc6bcf3..d878c6f7d3b 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -33,7 +33,7 @@ Project { cpp.frameworks: ["Foundation", "AppKit"] } - Depends { name: "Qt"; submodules: ["concurrent", "network", "qml", "widgets", "xml"] } + Depends { name: "Qt"; submodules: ["concurrent", "core-private", "network", "qml", "widgets", "xml"] } Depends { name: "Qt.macextras"; condition: Qt.core.versionMajor < 6 && qbs.targetOS.contains("macos") } Depends { name: "app_version_header" } @@ -133,6 +133,11 @@ Project { "fixedsizeclicklabel.h", "flowlayout.cpp", "flowlayout.h", + "fsengine/fileiconprovider.cpp", + "fsengine/fileiconprovider.h", + "fsengine/qtcfsengine.cpp", + "fsengine/qtcfsengine.h", + "fsengine/qtcfsengine_p.h", "functiontraits.h", "futuresynchronizer.cpp", "futuresynchronizer.h", diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index f56a60cdb69..f8d305d752c 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp @@ -30,10 +30,11 @@ #include "clangtoolsutils.h" #include "diagnosticmark.h" -#include <coreplugin/fileiconprovider.h> #include <projectexplorer/project.h> #include <projectexplorer/session.h> #include <texteditor/textmark.h> + +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> @@ -58,7 +59,7 @@ QVariant FilePathItem::data(int column, int role) const case Qt::DisplayRole: return m_filePath.toUserOutput(); case Qt::DecorationRole: - return Core::FileIconProvider::icon(m_filePath); + return Utils::FileIconProvider::icon(m_filePath); case Debugger::DetailedErrorView::FullTextRole: return m_filePath.toUserOutput(); default: diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp index 23d1f9703b8..9f8dc48902e 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp @@ -29,7 +29,6 @@ #include "cmakeprojectconstants.h" #include <android/androidconstants.h> -#include <coreplugin/fileiconprovider.h> #include <ios/iosconstants.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp index 0e4f7b4c1b5..aaeabd24f85 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp @@ -41,13 +41,13 @@ #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/projecttree.h> #include <texteditor/snippets/snippetprovider.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/parameteraction.h> using namespace Core; diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp index 0f070095f24..9a7aa784ef3 100644 --- a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp +++ b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp @@ -25,10 +25,10 @@ #include "projecttreehelper.h" -#include <coreplugin/fileiconprovider.h> #include <projectexplorer/projectexplorerconstants.h> #include <utils/algorithm.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> using namespace ProjectExplorer; diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp index fa7f425432a..db04ea89dcd 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseprojectmanagerplugin.cpp @@ -32,13 +32,13 @@ #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> -#include <coreplugin/fileiconprovider.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/projecttree.h> #include <projectexplorer/session.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/parameteraction.h> #include <utils/utilsicons.h> @@ -71,9 +71,9 @@ bool CompilationDatabaseProjectManagerPlugin::initialize(const QStringList &argu d = new CompilationDatabaseProjectManagerPluginPrivate; - FileIconProvider::registerIconOverlayForFilename(Utils::Icons::PROJECT.imageFilePath().toString(), + Utils::FileIconProvider::registerIconOverlayForFilename(Utils::Icons::PROJECT.imageFilePath().toString(), COMPILE_COMMANDS_JSON); - FileIconProvider::registerIconOverlayForFilename( + Utils::FileIconProvider::registerIconOverlayForFilename( Utils::Icons::PROJECT.imageFilePath().toString(), QString(COMPILE_COMMANDS_JSON) + Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX); diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index 28469c43807..d97b40493b8 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -59,7 +59,6 @@ add_qtc_plugin(Core fancyactionbar.qrc fancytabwidget.cpp fancytabwidget.h featureprovider.cpp featureprovider.h - fileiconprovider.cpp fileiconprovider.h fileutils.cpp fileutils.h find/basetextfind.cpp find/basetextfind.h find/currentdocumentfind.cpp find/currentdocumentfind.h diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h index fd5e21963a7..372fc3b3c52 100644 --- a/src/plugins/coreplugin/coreconstants.h +++ b/src/plugins/coreplugin/coreconstants.h @@ -91,6 +91,7 @@ const char NEW[] = "QtCreator.New"; const char NEW_FILE[] = "QtCreator.NewFile"; const char OPEN[] = "QtCreator.Open"; const char OPEN_WITH[] = "QtCreator.OpenWith"; +const char OPEN_FROM_DEVICE[] = "QtCreator.OpenFromDevice"; const char REVERTTOSAVED[] = "QtCreator.RevertToSaved"; const char SAVE[] = "QtCreator.Save"; const char SAVEAS[] = "QtCreator.SaveAs"; diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index ad8e49b64c0..7bbe3010501 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -67,8 +67,6 @@ Project { "fancytabwidget.h", "featureprovider.cpp", "featureprovider.h", - "fileiconprovider.cpp", - "fileiconprovider.h", "fileutils.cpp", "fileutils.h", "findplaceholder.cpp", diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp index 5f8475f35a0..d54541d3b65 100644 --- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp +++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.cpp @@ -27,7 +27,6 @@ #include "ui_readonlyfilesdialog.h" #include <coreplugin/editormanager/editormanager_p.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <coreplugin/idocument.h> #include <coreplugin/iversioncontrol.h> @@ -35,6 +34,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <utils/stringutils.h> diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp index d110beac0a4..7e20a9ac8c9 100644 --- a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp @@ -26,10 +26,10 @@ #include "saveitemsdialog.h" #include <coreplugin/diffservice.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/idocument.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <extensionsystem/pluginmanager.h> @@ -78,7 +78,7 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.treeWidget, QStringList() << visibleName << QDir::toNativeSeparators(directory)); if (!filePath.isEmpty()) - item->setIcon(0, FileIconProvider::icon(filePath)); + item->setIcon(0, Utils::FileIconProvider::icon(filePath)); item->setData(0, Qt::UserRole, QVariant::fromValue(document)); } diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp index ee027e11c1a..68459bc9f2d 100644 --- a/src/plugins/coreplugin/editortoolbar.cpp +++ b/src/plugins/coreplugin/editortoolbar.cpp @@ -31,10 +31,10 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager_p.h> #include <coreplugin/editormanager/ieditor.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> @@ -415,7 +415,7 @@ void EditorToolBar::updateDocumentStatus(IDocument *document) if (document->filePath().isEmpty()) d->m_dragHandle->setIcon(QIcon()); else - d->m_dragHandle->setIcon(FileIconProvider::icon(document->filePath())); + d->m_dragHandle->setIcon(Utils::FileIconProvider::icon(document->filePath())); d->m_editorList->setToolTip(document->filePath().isEmpty() ? document->displayName() diff --git a/src/plugins/coreplugin/foldernavigationwidget.cpp b/src/plugins/coreplugin/foldernavigationwidget.cpp index 4c927e0e457..0ffa8e2e347 100644 --- a/src/plugins/coreplugin/foldernavigationwidget.cpp +++ b/src/plugins/coreplugin/foldernavigationwidget.cpp @@ -32,7 +32,6 @@ #include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/fileutils.h> #include <coreplugin/icontext.h> #include <coreplugin/icore.h> @@ -46,6 +45,7 @@ #include <utils/algorithm.h> #include <utils/filecrumblabel.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <utils/navigationtreeview.h> #include <utils/qtcassert.h> @@ -158,8 +158,26 @@ public: protected: bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; }; +bool FolderSortProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + if (static_cast<QFileSystemModel *>(sourceModel())->rootPath().isEmpty()) { + QModelIndex sourceIndex = sourceModel()->index(source_row, 0, source_parent); + while (sourceIndex.isValid()) { + if (sourceIndex.data().toString() + == FilePath::specialPath(FilePath::SpecialPathComponent::RootName)) { + return false; + } + + sourceIndex = sourceIndex.parent(); + } + } + + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + FolderSortProxyModel::FolderSortProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { @@ -281,8 +299,9 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent m_sortProxyModel->setSourceModel(m_fileSystemModel); m_sortProxyModel->setSortRole(FolderNavigationModel::IsFolderRole); m_sortProxyModel->sort(0); + m_fileSystemModel->setResolveSymlinks(false); - m_fileSystemModel->setIconProvider(Core::FileIconProvider::iconProvider()); + m_fileSystemModel->setIconProvider(Utils::FileIconProvider::iconProvider()); QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot; if (Utils::HostOsInfo::isWindowsHost()) // Symlinked directories can cause file watcher warnings on Win32. filters |= QDir::NoSymLinks; @@ -924,6 +943,17 @@ static FolderNavigationWidget *currentFolderNavigationWidget() return qobject_cast<FolderNavigationWidget *>(Core::ICore::currentContextWidget()); } +void FolderNavigationWidgetFactory::addRootPath(Utils::Id id, const QString &displayName, const QIcon &icon, const Utils::FilePath &path) +{ + if (path.isDir()) + insertRootDirectory({id.toString(), 0, displayName, path, icon}); +} + +void FolderNavigationWidgetFactory::removeRootPath(Utils::Id id) +{ + removeRootDirectory(id.toString()); +} + void FolderNavigationWidgetFactory::registerActions() { Core::Context context(C_FOLDERNAVIGATIONWIDGET); diff --git a/src/plugins/coreplugin/foldernavigationwidget.h b/src/plugins/coreplugin/foldernavigationwidget.h index 4cee261369a..e5b2242c912 100644 --- a/src/plugins/coreplugin/foldernavigationwidget.h +++ b/src/plugins/coreplugin/foldernavigationwidget.h @@ -80,6 +80,9 @@ public: void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; void restoreSettings(QSettings *settings, int position, QWidget *widget) override; + void addRootPath(Utils::Id id, const QString &displayName, const QIcon &icon, const Utils::FilePath &path) override; + void removeRootPath(Utils::Id path) override; + static void insertRootDirectory(const RootDirectory &directory); static void removeRootDirectory(const QString &id); diff --git a/src/plugins/coreplugin/inavigationwidgetfactory.cpp b/src/plugins/coreplugin/inavigationwidgetfactory.cpp index 63ed236e11f..c9ccfa5f7c2 100644 --- a/src/plugins/coreplugin/inavigationwidgetfactory.cpp +++ b/src/plugins/coreplugin/inavigationwidgetfactory.cpp @@ -28,6 +28,7 @@ #include "inavigationwidgetfactory.h" +#include <QIcon> #include <QKeySequence> /*! @@ -171,3 +172,15 @@ void INavigationWidgetFactory::saveSettings(Utils::QtcSettings * /* settings */, void INavigationWidgetFactory::restoreSettings(QSettings * /* settings */, int /* position */, QWidget * /* widget */) { } + +// Registers a new root path in the factory +void INavigationWidgetFactory::addRootPath(Utils::Id /*id*/, const QString & /*displayName*/, const QIcon & /*icon*/, const Utils::FilePath & /*path*/) +{ + +} + +// Removes a root path from the factory +void INavigationWidgetFactory::removeRootPath(Utils::Id /*path*/) +{ + +} diff --git a/src/plugins/coreplugin/inavigationwidgetfactory.h b/src/plugins/coreplugin/inavigationwidgetfactory.h index 5b3b38455e6..2573bd6961c 100644 --- a/src/plugins/coreplugin/inavigationwidgetfactory.h +++ b/src/plugins/coreplugin/inavigationwidgetfactory.h @@ -27,6 +27,7 @@ #include "core_global.h" +#include <utils/filepath.h> #include <utils/id.h> #include <QObject> @@ -80,6 +81,9 @@ public: virtual void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget); virtual void restoreSettings(QSettings *settings, int position, QWidget *widget); + virtual void addRootPath(Utils::Id id, const QString &displayName, const QIcon &icon, const Utils::FilePath &path); + virtual void removeRootPath(Utils::Id id); + private: QString m_displayName; int m_priority = 0; diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp index 73821c5d7a2..2b05025bc39 100644 --- a/src/plugins/coreplugin/locator/locatorwidget.cpp +++ b/src/plugins/coreplugin/locator/locatorwidget.cpp @@ -33,12 +33,12 @@ #include <coreplugin/icore.h> #include <coreplugin/modemanager.h> #include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icontext.h> #include <coreplugin/mainwindow.h> #include <utils/algorithm.h> #include <utils/appmainwindow.h> #include <utils/fancylineedit.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/highlightingitemdelegate.h> #include <utils/hostosinfo.h> #include <utils/itemviews.h> diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index 7c1641ee789..44b7233d5fe 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -72,6 +72,8 @@ #include <coreplugin/settingsdatabase.h> #include <extensionsystem/pluginmanager.h> #include <utils/algorithm.h> +#include <utils/fsengine/fileiconprovider.h> +#include <utils/fsengine/fsengine.h> #include <utils/historycompleter.h> #include <utils/hostosinfo.h> #include <utils/mimeutils.h> @@ -83,6 +85,7 @@ #include <utils/touchbar/touchbar.h> #include <utils/utilsicons.h> +#include <QAbstractProxyModel> #include <QActionGroup> #include <QApplication> #include <QBrush> @@ -93,11 +96,13 @@ #include <QDialogButtonBox> #include <QDir> #include <QFileInfo> +#include <QFileSystemModel> #include <QMenu> #include <QMenuBar> #include <QMessageBox> #include <QPrinter> #include <QSettings> +#include <QSortFilterProxyModel> #include <QStatusBar> #include <QStyleFactory> #include <QSyntaxHighlighter> @@ -114,6 +119,17 @@ using namespace Utils; namespace Core { namespace Internal { +static const char settingsGroup[] = "MainWindow"; +static const char colorKey[] = "Color"; +static const char askBeforeExitKey[] = "AskBeforeExit"; +static const char windowGeometryKey[] = "WindowGeometry"; +static const char windowStateKey[] = "WindowState"; +static const char modeSelectorLayoutKey[] = "ModeSelectorLayout"; +static const char openFromDeviceDialogKey[] = "OpenFromDeviceDialog"; + +static const bool askBeforeExitDefault = false; + + enum { debugMainWindow = 0 }; MainWindow::MainWindow() @@ -583,6 +599,14 @@ void MainWindow::registerDefaultActions() mfile->addAction(cmd, Constants::G_FILE_OPEN); connect(m_openWithAction, &QAction::triggered, this, &MainWindow::openFileWith); + if (FSEngine::isAvailable()) { + // Open From Device Action + m_openFromDeviceAction = new QAction(Tr::tr("Open From Device..."), this); + cmd = ActionManager::registerAction(m_openFromDeviceAction, Constants::OPEN_FROM_DEVICE); + mfile->addAction(cmd, Constants::G_FILE_OPEN); + connect(m_openFromDeviceAction, &QAction::triggered, this, &MainWindow::openFileFromDevice); + } + // File->Recent Files Menu ActionContainer *ac = ActionManager::createMenu(Constants::M_FILE_RECENTFILES); mfile->addMenu(ac, Constants::G_FILE_OPEN); @@ -1043,6 +1067,41 @@ void MainWindow::openFileWith() } } +void MainWindow::openFileFromDevice() +{ + QSettings *settings = PluginManager::settings(); + settings->beginGroup(QLatin1String(settingsGroup)); + QVariant dialogSettings = settings->value(QLatin1String(openFromDeviceDialogKey)); + + QFileDialog dialog; + dialog.setOption(QFileDialog::DontUseNativeDialog); + if (!dialogSettings.isNull()) { + dialog.restoreState(dialogSettings.toByteArray()); + } + QList<QUrl> sideBarUrls = Utils::transform(Utils::filtered(FSEngine::registeredDeviceRoots(), + [](const auto &filePath) { + return filePath.exists(); + }), + [](const auto &filePath) { + return QUrl::fromLocalFile(filePath.toFSPathString()); + }); + dialog.setSidebarUrls(sideBarUrls); + dialog.setFileMode(QFileDialog::AnyFile); + + dialog.setIconProvider(FileIconProvider::iconProvider()); + + if (dialog.exec()) { + FilePaths filePaths = Utils::transform(dialog.selectedFiles(), [](const auto &path) { + return FilePath::fromString(path); + }); + + openFiles(filePaths, ICore::SwitchMode); + } + + settings->setValue(QLatin1String(openFromDeviceDialogKey), dialog.saveState()); + settings->endGroup(); +} + IContext *MainWindow::contextObject(QWidget *widget) const { const auto it = m_contextWidgets.find(widget); @@ -1126,15 +1185,6 @@ void MainWindow::aboutToShutdown() hide(); } -static const char settingsGroup[] = "MainWindow"; -static const char colorKey[] = "Color"; -static const char askBeforeExitKey[] = "AskBeforeExit"; -static const char windowGeometryKey[] = "WindowGeometry"; -static const char windowStateKey[] = "WindowState"; -static const char modeSelectorLayoutKey[] = "ModeSelectorLayout"; - -static const bool askBeforeExitDefault = false; - void MainWindow::readSettings() { QSettings *settings = PluginManager::settings(); diff --git a/src/plugins/coreplugin/mainwindow.h b/src/plugins/coreplugin/mainwindow.h index 9765373ab37..674bebf5719 100644 --- a/src/plugins/coreplugin/mainwindow.h +++ b/src/plugins/coreplugin/mainwindow.h @@ -116,6 +116,8 @@ public: void restart(); + void openFileFromDevice(); + public slots: static void openFileWith(); void exit(); @@ -186,6 +188,7 @@ private: QAction *m_newAction = nullptr; QAction *m_openAction = nullptr; QAction *m_openWithAction = nullptr; + QAction *m_openFromDeviceAction = nullptr; QAction *m_saveAllAction = nullptr; QAction *m_exitAction = nullptr; QAction *m_optionsAction = nullptr; diff --git a/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp index 0f3b8a94654..79aff12ed56 100644 --- a/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp +++ b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp @@ -25,10 +25,9 @@ #include "cppcheckdiagnosticsmodel.h" -#include <coreplugin/fileiconprovider.h> - #include <debugger/analyzer/diagnosticlocation.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/utilsicons.h> namespace Cppcheck { @@ -47,7 +46,7 @@ QVariant FilePathItem::data(int column, int role) const case Qt::DisplayRole: return m_filePath; case Qt::DecorationRole: - return Core::FileIconProvider::icon(Utils::FilePath::fromString(m_filePath)); + return Utils::FileIconProvider::icon(Utils::FilePath::fromString(m_filePath)); case Debugger::DetailedErrorView::FullTextRole: return m_filePath; default: diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index e2999002e35..4b1b3eae771 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -83,7 +83,6 @@ #include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditorfactory.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <coreplugin/idocument.h> #include <coreplugin/navigationwidget.h> @@ -105,6 +104,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <utils/macroexpander.h> #include <utils/mimeutils.h> diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp index b11c4f28028..9ec564ac571 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy.cpp @@ -35,7 +35,6 @@ #include "cppmodelmanager.h" #include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/find/itemviewfind.h> #include <cplusplus/CppDocument.h> @@ -45,6 +44,7 @@ #include <utils/delegates.h> #include <utils/dropsupport.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/navigationtreeview.h> #include <utils/qtcassert.h> #include <utils/qtcsettings.h> diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index 8e12a8b30ec..02afba73f48 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -318,6 +318,7 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget() item.reinitializeFromFile({}, errorMessage); return errorMessage->isEmpty(); }); + m_binaryChooser->setAllowPathFromDevice(true); m_workingDirectoryChooser = new PathChooser(this); m_workingDirectoryChooser->setExpectedKind(PathChooser::Directory); diff --git a/src/plugins/designer/formeditorfactory.cpp b/src/plugins/designer/formeditorfactory.cpp index 5d0cc0438b5..58749250d94 100644 --- a/src/plugins/designer/formeditorfactory.cpp +++ b/src/plugins/designer/formeditorfactory.cpp @@ -28,14 +28,15 @@ #include "formwindoweditor.h" #include <coreplugin/coreconstants.h> -#include <coreplugin/fileiconprovider.h> #include <projectexplorer/projectexplorerconstants.h> +#include <utils/fsengine/fileiconprovider.h> #include <QCoreApplication> #include <QDebug> using namespace Core; using namespace Designer::Constants; +using namespace Utils; namespace Designer { namespace Internal { diff --git a/src/plugins/docker/dockerconstants.h b/src/plugins/docker/dockerconstants.h index 8dc823a65ba..b1ed087b228 100644 --- a/src/plugins/docker/dockerconstants.h +++ b/src/plugins/docker/dockerconstants.h @@ -28,6 +28,7 @@ namespace Docker::Constants { const char DOCKER[] = "docker"; +const char DOCKER_DEVICE_SCHEME[] = "docker"; const char DOCKER_SETTINGS_ID[] = "Docker.Settings"; const char DOCKER_DEVICE_TYPE[] = "DockerDeviceType"; diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index a3e157ad68a..08eaf00c490 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -641,6 +641,15 @@ QString DockerDevice::mapToDevicePath(const Utils::FilePath &globalPath) const return path; } +Utils::FilePath DockerDevice::rootPath() const +{ + FilePath root; + root.setScheme(Constants::DOCKER_DEVICE_SCHEME); + root.setHost(d->m_data.repoAndTag()); + root.setPath("/"); + return root; +} + bool DockerDevice::handlesFile(const FilePath &filePath) const { if (filePath.scheme() == "device" && filePath.host() == id().toString()) diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index aead51456a6..33845477f2e 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -81,6 +81,8 @@ public: Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override; QString mapToDevicePath(const Utils::FilePath &globalPath) const override; + Utils::FilePath rootPath() const override; + bool handlesFile(const Utils::FilePath &filePath) const override; bool isExecutableFile(const Utils::FilePath &filePath) const override; bool isReadableFile(const Utils::FilePath &filePath) const override; diff --git a/src/plugins/docker/dockerplugin.cpp b/src/plugins/docker/dockerplugin.cpp index 9e6a33789ba..8bb9a8e051a 100644 --- a/src/plugins/docker/dockerplugin.cpp +++ b/src/plugins/docker/dockerplugin.cpp @@ -26,11 +26,13 @@ #include "dockerplugin.h" #include "dockerapi.h" +#include "dockerconstants.h" #include "dockerdevice.h" #include "dockersettings.h" #include <projectexplorer/projectexplorerconstants.h> +#include <utils/fsengine/fsengine.h> #include <utils/qtcassert.h> using namespace Core; @@ -57,6 +59,7 @@ static DockerPlugin *s_instance = nullptr; DockerPlugin::DockerPlugin() { s_instance = this; + FSEngine::registerDeviceScheme(Constants::DOCKER_DEVICE_SCHEME); } DockerApi *DockerPlugin::dockerApi() @@ -67,6 +70,7 @@ DockerApi *DockerPlugin::dockerApi() DockerPlugin::~DockerPlugin() { + FSEngine::unregisterDeviceScheme(Constants::DOCKER_DEVICE_SCHEME); s_instance = nullptr; delete d; } diff --git a/src/plugins/glsleditor/glsleditorplugin.cpp b/src/plugins/glsleditor/glsleditorplugin.cpp index 44785ba36ec..7b614e5df97 100644 --- a/src/plugins/glsleditor/glsleditorplugin.cpp +++ b/src/plugins/glsleditor/glsleditorplugin.cpp @@ -38,9 +38,10 @@ #include <coreplugin/actionmanager/command.h> #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> +#include <utils/fsengine/fileiconprovider.h> + #include <QMenu> using namespace Core; diff --git a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp index ddfa6e4df04..de74b780217 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp +++ b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp @@ -37,13 +37,14 @@ #include "settings/tools/toolssettingsaccessor.h" #include "settings/tools/toolssettingspage.h" -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/runcontrol.h> +#include <utils/fsengine/fileiconprovider.h> + using namespace Core; using namespace ProjectExplorer; using namespace Utils; diff --git a/src/plugins/mesonprojectmanager/project/projecttree/mesonprojectnodes.h b/src/plugins/mesonprojectmanager/project/projecttree/mesonprojectnodes.h index 2419ac0d6eb..819b461090d 100644 --- a/src/plugins/mesonprojectmanager/project/projecttree/mesonprojectnodes.h +++ b/src/plugins/mesonprojectmanager/project/projecttree/mesonprojectnodes.h @@ -25,11 +25,10 @@ #pragma once -#include <coreplugin/fileiconprovider.h> - #include <projectexplorer/projectnodes.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> namespace MesonProjectManager { namespace Internal { diff --git a/src/plugins/nim/nimplugin.cpp b/src/plugins/nim/nimplugin.cpp index 418032f6e90..bdad9176c69 100644 --- a/src/plugins/nim/nimplugin.cpp +++ b/src/plugins/nim/nimplugin.cpp @@ -43,8 +43,6 @@ #include "settings/nimsettings.h" #include "suggest/nimsuggestcache.h" -#include <coreplugin/fileiconprovider.h> - #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/runcontrol.h> @@ -53,6 +51,8 @@ #include <texteditor/snippets/snippetprovider.h> +#include <utils/fsengine/fileiconprovider.h> + using namespace Utils; using namespace ProjectExplorer; @@ -128,13 +128,13 @@ bool NimPlugin::initialize(const QStringList &arguments, QString *errorMessage) void NimPlugin::extensionsInitialized() { // Add MIME overlay icons (these icons displayed at Project dock panel) - const QIcon icon = Utils::Icon({{":/nim/images/settingscategory_nim.png", - Utils::Theme::PanelTextColorDark - }}, Utils::Icon::Tint).icon(); + const QIcon icon = Icon({{":/nim/images/settingscategory_nim.png", + Theme::PanelTextColorDark + }}, Icon::Tint).icon(); if (!icon.isNull()) { - Core::FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIM_MIMETYPE); - Core::FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIM_SCRIPT_MIMETYPE); - Core::FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIMBLE_MIMETYPE); + FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIM_MIMETYPE); + FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIM_SCRIPT_MIMETYPE); + FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIMBLE_MIMETYPE); } TaskHub::addCategory(Constants::C_NIMPARSE_ID, "Nim"); } diff --git a/src/plugins/projectexplorer/dependenciespanel.cpp b/src/plugins/projectexplorer/dependenciespanel.cpp index b5ec18b984c..ca945d910b9 100644 --- a/src/plugins/projectexplorer/dependenciespanel.cpp +++ b/src/plugins/projectexplorer/dependenciespanel.cpp @@ -27,11 +27,11 @@ #include "project.h" #include "session.h" -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> -#include <utils/detailswidget.h> #include <utils/algorithm.h> +#include <utils/detailswidget.h> +#include <utils/fsengine/fileiconprovider.h> #include <QDebug> #include <QSize> @@ -99,7 +99,7 @@ QVariant DependenciesModel::data(const QModelIndex &index, int role) const case Qt::CheckStateRole: return SessionManager::hasDependency(m_project, p) ? Qt::Checked : Qt::Unchecked; case Qt::DecorationRole: - return Core::FileIconProvider::icon(p->projectFilePath()); + return Utils::FileIconProvider::icon(p->projectFilePath()); default: return QVariant(); } diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index deb96f14291..65d7ce56056 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -34,6 +34,7 @@ #include <utils/algorithm.h> #include <utils/environment.h> #include <utils/fileutils.h> +#include <utils/fsengine/fsengine.h> #include <utils/persistentsettings.h> #include <utils/portlist.h> #include <utils/qtcassert.h> @@ -304,6 +305,10 @@ void DeviceManager::addDevice(const IDevice::ConstPtr &_device) d->devices << device; } emit deviceAdded(device->id()); + + if (FSEngine::isAvailable()) { + Utils::FSEngine::addDevice(device->rootPath()); + } } emit updated(); @@ -323,6 +328,10 @@ void DeviceManager::removeDevice(Utils::Id id) } emit deviceRemoved(device->id()); + if (FSEngine::isAvailable()) { + Utils::FSEngine::removeDevice(device->rootPath()); + } + if (wasDefault) { for (int i = 0; i < d->devices.count(); ++i) { if (deviceAt(i)->type() == deviceType) { diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 22e125ddd9d..a21cbdae616 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -205,7 +205,7 @@ public: MachineType machineType() const; void setMachineType(MachineType machineType); - Utils::FilePath rootPath() const; + virtual Utils::FilePath rootPath() const; Utils::FilePath filePath(const QString &pathOnDevice) const; Utils::FilePath debugServerPath() const; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index ee0a0e3e363..a03bad61795 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1341,6 +1341,7 @@ GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) : m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); m_compilerCommand->setHistoryCompleter("PE.Gcc.Command.History"); + m_compilerCommand->setAllowPathFromDevice(true); m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand); m_platformCodeGenFlagsLineEdit = new QLineEdit(this); m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags())); diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index 68152d940ca..39010e6a53f 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -35,13 +35,13 @@ #include <app/app_version.h> #include <coreplugin/documentmanager.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <coreplugin/iversioncontrol.h> #include <coreplugin/vcsmanager.h> #include <utils/utilsicons.h> #include <utils/algorithm.h> #include <utils/dropsupport.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/pathchooser.h> #include <utils/stringutils.h> #include <utils/theme/theme.h> diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 61dfff43d05..d10c6521b70 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -32,12 +32,12 @@ #include "projecttree.h" #include "target.h" -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <coreplugin/iversioncontrol.h> #include <coreplugin/vcsmanager.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <utils/mimeutils.h> #include <utils/pointeralgorithm.h> @@ -257,7 +257,7 @@ QIcon FileNode::icon() const if (hasError()) return Utils::Icons::WARNING.icon(); if (m_icon.isNull()) - m_icon = Core::FileIconProvider::icon(filePath()); + m_icon = Utils::FileIconProvider::icon(filePath()); return m_icon; } @@ -479,7 +479,7 @@ QIcon FolderNode::icon() const } else { auto iconPtr = Utils::get_if<QIcon>(&m_icon); if (!iconPtr || iconPtr->isNull()) - m_icon = Core::FileIconProvider::icon(QFileIconProvider::Folder); + m_icon = Utils::FileIconProvider::icon(QFileIconProvider::Folder); } return Utils::get<QIcon>(m_icon); } @@ -1089,7 +1089,7 @@ QIcon DirectoryIcon::icon() const const auto it = m_cache.find(m_overlay); if (it != m_cache.end()) return it.value(); - const QIcon icon = Core::FileIconProvider::directoryIcon(m_overlay); + const QIcon icon = Utils::FileIconProvider::directoryIcon(m_overlay); m_cache.insert(m_overlay, icon); return icon; } diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index f8da814b7a6..fc5bfddc4b7 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -514,24 +514,22 @@ void RunControl::initiateFinish() RunWorker *RunControl::createWorker(Utils::Id workerId) { - const auto check = std::bind(&RunWorkerFactory::canRun, - std::placeholders::_1, - workerId, - DeviceTypeKitAspect::deviceTypeId(d->kit), - QString{}); - RunWorkerFactory *factory = Utils::findOrDefault(g_runWorkerFactories, check); + RunWorkerFactory *factory + = Utils::findOrDefault(g_runWorkerFactories, [this, workerId](RunWorkerFactory *factory) { + return factory->canRun(workerId, DeviceTypeKitAspect::deviceTypeId(d->kit), QString{}); + }); return factory ? factory->producer()(this) : nullptr; } bool RunControl::createMainWorker() { - const auto canRun = std::bind(&RunWorkerFactory::canRun, - std::placeholders::_1, - d->runMode, - DeviceTypeKitAspect::deviceTypeId(d->kit), - d->runConfigId.toString()); + const QList<RunWorkerFactory *> candidates + = Utils::filtered(g_runWorkerFactories, [this](RunWorkerFactory *factory) { + return factory->canRun(d->runMode, + DeviceTypeKitAspect::deviceTypeId(d->kit), + d->runConfigId.toString()); + }); - const QList<RunWorkerFactory *> candidates = Utils::filtered(g_runWorkerFactories, canRun); // There might be combinations that cannot run. But that should have been checked // with canRun below. QTC_ASSERT(!candidates.empty(), return false); diff --git a/src/plugins/projectexplorer/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp index 80eb130b738..6ba1c7613b9 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.cpp +++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp @@ -26,11 +26,11 @@ #include "selectablefilesmodel.h" #include "projectexplorerconstants.h" -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <utils/algorithm.h> #include <utils/fancylineedit.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/pathchooser.h> #include <utils/runextensions.h> #include <utils/stringutils.h> @@ -231,7 +231,7 @@ QVariant SelectableFilesModel::data(const QModelIndex &index, int role) const return t->checked; if (role == Qt::DecorationRole) { if (t->icon.isNull()) - t->icon = Core::FileIconProvider::icon(t->fullPath); + t->icon = Utils::FileIconProvider::icon(t->fullPath); return t->icon; } return QVariant(); diff --git a/src/plugins/python/pythonplugin.cpp b/src/plugins/python/pythonplugin.cpp index c68c96fa659..2c5f954770d 100644 --- a/src/plugins/python/pythonplugin.cpp +++ b/src/plugins/python/pythonplugin.cpp @@ -31,8 +31,6 @@ #include "pythonsettings.h" #include "pythonrunconfiguration.h" -#include <coreplugin/fileiconprovider.h> - #include <projectexplorer/buildtargetinfo.h> #include <projectexplorer/localenvironmentaspect.h> #include <projectexplorer/projectexplorerconstants.h> @@ -40,6 +38,7 @@ #include <projectexplorer/runcontrol.h> #include <projectexplorer/taskhub.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/theme/theme.h> using namespace ProjectExplorer; @@ -100,7 +99,7 @@ void PythonPlugin::extensionsInitialized() // Add MIME overlay icons (these icons displayed at Project dock panel) QString imageFile = Utils::creatorTheme()->imageFile(Utils::Theme::IconOverlayPro, ::Constants::FILEOVERLAY_PY); - Core::FileIconProvider::registerIconOverlayForSuffix(imageFile, "py"); + Utils::FileIconProvider::registerIconOverlayForSuffix(imageFile, "py"); TaskHub::addCategory(PythonErrorTaskCategory, "Python", true); } diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp index 2d6049b5702..2dd46dd93ae 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp @@ -32,13 +32,14 @@ #include "qbssession.h" #include <android/androidconstants.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/idocument.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <qtsupport/qtsupportconstants.h> #include <resourceeditor/resourcenode.h> + #include <utils/algorithm.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp index f96a86656eb..7de1ebac225 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp @@ -46,7 +46,6 @@ #include <coreplugin/helpmanager.h> #include <coreplugin/icore.h> #include <coreplugin/idocument.h> -#include <coreplugin/fileiconprovider.h> #include <projectexplorer/buildmanager.h> #include <projectexplorer/project.h> @@ -63,6 +62,7 @@ #include <qmljstools/qmljstoolsconstants.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> @@ -112,7 +112,7 @@ bool QbsProjectManagerPlugin::initialize(const QStringList &arguments, QString * const Core::Context projectContext(::QbsProjectManager::Constants::PROJECT_ID); - Core::FileIconProvider::registerIconOverlayForSuffix(ProjectExplorer::Constants::FILEOVERLAY_QT, "qbs"); + Utils::FileIconProvider::registerIconOverlayForSuffix(ProjectExplorer::Constants::FILEOVERLAY_QT, "qbs"); Core::HelpManager::registerDocumentation({Core::HelpManager::documentationPath() + "/qbs.qch"}); ProjectManager::registerProjectType<QbsProject>(QmlJSTools::Constants::QBS_MIMETYPE); diff --git a/src/plugins/qmakeprojectmanager/profileeditor.cpp b/src/plugins/qmakeprojectmanager/profileeditor.cpp index ecb3bfbda78..0406c01e365 100644 --- a/src/plugins/qmakeprojectmanager/profileeditor.cpp +++ b/src/plugins/qmakeprojectmanager/profileeditor.cpp @@ -32,7 +32,6 @@ #include "qmakeproject.h" #include "qmakeprojectmanagerconstants.h" -#include <coreplugin/fileiconprovider.h> #include <extensionsystem/pluginmanager.h> #include <qtsupport/qtsupportconstants.h> #include <projectexplorer/buildconfiguration.h> @@ -41,6 +40,7 @@ #include <projectexplorer/session.h> #include <texteditor/texteditoractionhandler.h> #include <texteditor/textdocument.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> #include <utils/theme/theme.h> @@ -298,11 +298,11 @@ ProFileEditorFactory::ProFileEditorFactory() setSyntaxHighlighterCreator([]() { return new ProFileHighlighter; }); const QString defaultOverlay = QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QT); - Core::FileIconProvider::registerIconOverlayForSuffix( + Utils::FileIconProvider::registerIconOverlayForSuffix( creatorTheme()->imageFile(Theme::IconOverlayPro, defaultOverlay), "pro"); - Core::FileIconProvider::registerIconOverlayForSuffix( + Utils::FileIconProvider::registerIconOverlayForSuffix( creatorTheme()->imageFile(Theme::IconOverlayPri, defaultOverlay), "pri"); - Core::FileIconProvider::registerIconOverlayForSuffix( + Utils::FileIconProvider::registerIconOverlayForSuffix( creatorTheme()->imageFile(Theme::IconOverlayPrf, defaultOverlay), "prf"); } diff --git a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp index 10fc625d562..477fd311765 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp @@ -27,7 +27,6 @@ #include "qmakeproject.h" -#include <coreplugin/fileiconprovider.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <qtsupport/baseqtversion.h> @@ -35,6 +34,7 @@ #include <resourceeditor/resourcenode.h> #include <utils/algorithm.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> using namespace Core; @@ -112,12 +112,12 @@ QmakeStaticData::QmakeStaticData() const QString filter = QString::fromUtf8(fileType.addFileFilter); fileTypeData.push_back(QmakeStaticData::FileTypeData(fileType.type, desc, filter, - Core::FileIconProvider::directoryIcon(QLatin1String(fileType.icon)))); + Utils::FileIconProvider::directoryIcon(QLatin1String(fileType.icon)))); } // Project icon - projectIcon = Core::FileIconProvider::directoryIcon(ProjectExplorer::Constants::FILEOVERLAY_QT); - productIcon = Core::FileIconProvider::directoryIcon(ProjectExplorer::Constants::FILEOVERLAY_PRODUCT); - groupIcon = Core::FileIconProvider::directoryIcon(ProjectExplorer::Constants::FILEOVERLAY_GROUP); + projectIcon = Utils::FileIconProvider::directoryIcon(ProjectExplorer::Constants::FILEOVERLAY_QT); + productIcon = Utils::FileIconProvider::directoryIcon(ProjectExplorer::Constants::FILEOVERLAY_PRODUCT); + groupIcon = Utils::FileIconProvider::directoryIcon(ProjectExplorer::Constants::FILEOVERLAY_GROUP); qAddPostRoutine(clearQmakeStaticData); } diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index c83dc5320a5..cf045a12c65 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -43,7 +43,6 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/icore.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/command.h> @@ -55,6 +54,7 @@ #include <texteditor/snippets/snippetprovider.h> #include <texteditor/texteditorconstants.h> #include <texteditor/tabsettings.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> #include <utils/json.h> diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp index b2265540836..f5c3bb6fb71 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp @@ -25,7 +25,7 @@ #include "qmlprojectnodes.h" -#include <coreplugin/fileiconprovider.h> +#include <utils/fsengine/fileiconprovider.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index a24beeebc4e..54aaa2afa00 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -38,7 +38,6 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/designmode.h> #include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <coreplugin/messagebox.h> #include <coreplugin/modemanager.h> @@ -62,6 +61,7 @@ #include <extensionsystem/pluginspec.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcprocess.h> #include <QAction> @@ -286,7 +286,7 @@ bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) } ProjectManager::registerProjectType<QmlProject>(QmlJSTools::Constants::QMLPROJECT_MIMETYPE); - Core::FileIconProvider::registerIconOverlayForSuffix(":/qmlproject/images/qmlproject.png", + Utils::FileIconProvider::registerIconOverlayForSuffix(":/qmlproject/images/qmlproject.png", "qmlproject"); if (QmlProject::isQtDesignStudio()) { diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index 9f9466a177a..349375d2cf8 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -60,6 +60,7 @@ #include <QDialogButtonBox> #include <QDir> #include <QFormLayout> +#include <QGuiApplication> #include <QHeaderView> #include <QLabel> #include <QMessageBox> @@ -644,12 +645,15 @@ QtOptionsPageWidget::~QtOptionsPageWidget() void QtOptionsPageWidget::addQtDir() { - FilePath qtVersion = FileUtils::getOpenFilePath(this, - tr("Select a qmake Executable"), - {}, - BuildableHelperLibrary::filterForQmakeFileDialog(), - 0, - QFileDialog::DontResolveSymlinks); + FilePath qtVersion + = FileUtils::getOpenFilePath(this, + tr("Select a qmake Executable"), + {}, + BuildableHelperLibrary::filterForQmakeFileDialog(), + nullptr, + QFileDialog::DontResolveSymlinks, + true); + if (qtVersion.isEmpty()) return; @@ -1025,6 +1029,7 @@ void QtOptionsPageWidget::linkWithQt() }); const Utils::optional<FilePath> currentLink = currentlyLinkedQtDir(nullptr); pathInput->setFilePath(currentLink ? *currentLink : defaultQtInstallationPath()); + pathInput->setAllowPathFromDevice(true); auto buttons = new QDialogButtonBox; layout->addStretch(10); layout->addWidget(buttons); diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 39f7ba65e62..fa83b6d7dab 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1046,6 +1046,15 @@ QString LinuxDevice::userAtHost() const return sshParameters().userAtHost(); } +Utils::FilePath LinuxDevice::rootPath() const +{ + Utils::FilePath root; + root.setScheme("ssh"); + root.setHost(userAtHost()); + root.setPath("/"); + return root; +} + bool LinuxDevice::handlesFile(const FilePath &filePath) const { if (filePath.scheme() == "device" && filePath.host() == id().toString()) diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index 2e7000a09dd..32e42ee9081 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -57,6 +57,8 @@ public: QString userAtHost() const; + Utils::FilePath rootPath() const override; + bool handlesFile(const Utils::FilePath &filePath) const override; bool isExecutableFile(const Utils::FilePath &filePath) const override; bool isReadableFile(const Utils::FilePath &filePath) const override; diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp index 29aabd18d19..ef93ad8f3fe 100644 --- a/src/plugins/remotelinux/remotelinuxplugin.cpp +++ b/src/plugins/remotelinux/remotelinuxplugin.cpp @@ -49,7 +49,10 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> +#include <utils/fsengine/fsengine.h> + using namespace ProjectExplorer; +using namespace Utils; namespace RemoteLinux { namespace Internal { @@ -114,10 +117,12 @@ static RemoteLinuxPluginPrivate *dd = nullptr; RemoteLinuxPlugin::RemoteLinuxPlugin() { setObjectName(QLatin1String("RemoteLinuxPlugin")); + FSEngine::registerDeviceScheme("ssh"); } RemoteLinuxPlugin::~RemoteLinuxPlugin() { + FSEngine::unregisterDeviceScheme("ssh"); delete dd; } diff --git a/src/plugins/resourceeditor/qrceditor/resourcefile.cpp b/src/plugins/resourceeditor/qrceditor/resourcefile.cpp index b89ddffb88a..fd8e7469a9e 100644 --- a/src/plugins/resourceeditor/qrceditor/resourcefile.cpp +++ b/src/plugins/resourceeditor/qrceditor/resourcefile.cpp @@ -25,7 +25,6 @@ #include "resourcefile_p.h" -#include <coreplugin/fileiconprovider.h> #include <coreplugin/fileutils.h> #include <coreplugin/icore.h> #include <coreplugin/vcsmanager.h> @@ -33,6 +32,7 @@ #include <utils/algorithm.h> #include <utils/filepath.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/removefiledialog.h> #include <utils/theme/theme.h> @@ -580,7 +580,7 @@ void ResourceFile::clearPrefixList() ResourceModel::ResourceModel(QObject *parent) : QAbstractItemModel(parent), m_dirty(false) { - static QIcon resourceFolderIcon = Core::FileIconProvider::directoryIcon(QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QRC)); + static QIcon resourceFolderIcon = Utils::FileIconProvider::directoryIcon(QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QRC)); m_prefixIcon = resourceFolderIcon; } @@ -797,7 +797,7 @@ QVariant ResourceModel::data(const QModelIndex &index, int role) const if (iconFileExtension(path)) file->icon = QIcon(path); else - file->icon = Core::FileIconProvider::icon(Utils::FilePath::fromString(path)); + file->icon = Utils::FileIconProvider::icon(Utils::FilePath::fromString(path)); } if (!file->icon.isNull()) result = file->icon; diff --git a/src/plugins/resourceeditor/resourceeditorfactory.cpp b/src/plugins/resourceeditor/resourceeditorfactory.cpp index 70077bf6fc0..10a6f582717 100644 --- a/src/plugins/resourceeditor/resourceeditorfactory.cpp +++ b/src/plugins/resourceeditor/resourceeditorfactory.cpp @@ -28,9 +28,9 @@ #include "resourceeditorplugin.h" #include "resourceeditorconstants.h" -#include <coreplugin/fileiconprovider.h> #include <coreplugin/editormanager/editormanager.h> #include <projectexplorer/projectexplorerconstants.h> +#include <utils/fsengine/fileiconprovider.h> #include <QCoreApplication> #include <QFileInfo> @@ -45,7 +45,7 @@ ResourceEditorFactory::ResourceEditorFactory(ResourceEditorPlugin *plugin) setMimeTypes(QStringList(QLatin1String(C_RESOURCE_MIMETYPE))); setDisplayName(QCoreApplication::translate("OpenWith::Editors", C_RESOURCEEDITOR_DISPLAY_NAME)); - Core::FileIconProvider::registerIconOverlayForSuffix( + Utils::FileIconProvider::registerIconOverlayForSuffix( ProjectExplorer::Constants::FILEOVERLAY_QRC, "qrc"); setEditorCreator([plugin] { diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp index bc9274fa895..2de68d2a3f4 100644 --- a/src/plugins/resourceeditor/resourcenode.cpp +++ b/src/plugins/resourceeditor/resourcenode.cpp @@ -28,12 +28,12 @@ #include "qrceditor/resourcefile_p.h" #include <coreplugin/documentmanager.h> -#include <coreplugin/fileiconprovider.h> #include <qmljstools/qmljstoolsconstants.h> #include <utils/algorithm.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/threadutils.h> diff --git a/src/plugins/scxmleditor/scxmleditorfactory.cpp b/src/plugins/scxmleditor/scxmleditorfactory.cpp index 56d707173e8..2018543abe5 100644 --- a/src/plugins/scxmleditor/scxmleditorfactory.cpp +++ b/src/plugins/scxmleditor/scxmleditorfactory.cpp @@ -29,8 +29,8 @@ #include "scxmleditordata.h" #include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/fileiconprovider.h> #include <projectexplorer/projectexplorerconstants.h> +#include <utils/fsengine/fileiconprovider.h> #include <QGuiApplication> #include <QFileInfo> @@ -44,7 +44,7 @@ ScxmlEditorFactory::ScxmlEditorFactory() setDisplayName(QCoreApplication::translate("ScxmlEditor", C_SCXMLEDITOR_DISPLAY_NAME)); addMimeType(ProjectExplorer::Constants::SCXML_MIMETYPE); - Core::FileIconProvider::registerIconOverlayForSuffix(":/projectexplorer/images/fileoverlay_scxml.png", "scxml"); + Utils::FileIconProvider::registerIconOverlayForSuffix(":/projectexplorer/images/fileoverlay_scxml.png", "scxml"); setEditorCreator([this] { if (!m_editorData) { diff --git a/src/plugins/vcsbase/submitfilemodel.cpp b/src/plugins/vcsbase/submitfilemodel.cpp index 7366e8649e8..55d2f1ad2bc 100644 --- a/src/plugins/vcsbase/submitfilemodel.cpp +++ b/src/plugins/vcsbase/submitfilemodel.cpp @@ -25,8 +25,8 @@ #include "submitfilemodel.h" -#include <coreplugin/fileiconprovider.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> #include <utils/theme/theme.h> @@ -88,8 +88,8 @@ static QList<QStandardItem *> createFileRow(const QString &repositoryRoot, fileItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); // For some reason, Windows (at least) requires a valid (existing) file path to the icon, so // the repository root is needed here. - // Note: for "overlaid" icons in Core::FileIconProvider a valid file path is not required - fileItem->setIcon(Core::FileIconProvider::icon( + // Note: for "overlaid" icons in Utils::FileIconProvider a valid file path is not required + fileItem->setIcon(Utils::FileIconProvider::icon( Utils::FilePath::fromString(repositoryRoot).pathAppended(fileName))); const QList<QStandardItem *> row{statusItem, fileItem}; if (statusHint != SubmitFileModel::FileStatusUnknown) { diff --git a/tests/auto/filesystemmodel/CMakeLists.txt b/tests/auto/filesystemmodel/CMakeLists.txt new file mode 100644 index 00000000000..d7038a1d704 --- /dev/null +++ b/tests/auto/filesystemmodel/CMakeLists.txt @@ -0,0 +1,4 @@ +add_qtc_test(tst_filesystemmodel + DEPENDS Utils + SOURCES tst_filesystemmodel.cpp ../../../src/shared/modeltest/modeltest.cpp +) diff --git a/tests/auto/filesystemmodel/tst_filesystemmodel.cpp b/tests/auto/filesystemmodel/tst_filesystemmodel.cpp new file mode 100644 index 00000000000..db1d88ccd32 --- /dev/null +++ b/tests/auto/filesystemmodel/tst_filesystemmodel.cpp @@ -0,0 +1,75 @@ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "../../../src/shared/modeltest/modeltest.h" + +#include <utils/filesystemmodel/filesystemmodel.h> + +#include <QCoreApplication> +#include <QDebug> +#include <QLocalSocket> +#include <QNetworkAccessManager> +#include <QProcess> +#include <QtTest> + +#ifndef WITH_TESTS +#define WITH_TESTS +#endif + +class FileSystemModelTest : public ModelTest +{ + Q_OBJECT + +public: + FileSystemModelTest() + : ModelTest(new Utils::FileSystemModel()) + { + static_cast<Utils::FileSystemModel*>(this->model())->setRootPath("/"); + } + +private slots: + void qTestWithTestClass() { + Utils::FileSystemModel modelToBeTested; + modelToBeTested.setRootPath("/"); + auto tester = new QAbstractItemModelTester(&modelToBeTested); + } + + void testWithSortFilterProxyModel() { + Utils::FileSystemModel modelToBeTested; + QSortFilterProxyModel proxyModel; + + proxyModel.setSourceModel(&modelToBeTested); + proxyModel.setSortRole(Qt::UserRole + 50); + proxyModel.sort(0); + + modelToBeTested.setRootPath("/"); + auto tester = new QAbstractItemModelTester(&proxyModel); + } +}; + +QTEST_MAIN(FileSystemModelTest) + +#include "tst_filesystemmodel.moc" diff --git a/tests/auto/utils/CMakeLists.txt b/tests/auto/utils/CMakeLists.txt index 60dd1cfbccc..b8be57696bb 100644 --- a/tests/auto/utils/CMakeLists.txt +++ b/tests/auto/utils/CMakeLists.txt @@ -10,3 +10,4 @@ add_subdirectory(templateengine) add_subdirectory(treemodel) add_subdirectory(multicursor) add_subdirectory(deviceshell) +add_subdirectory(fsengine) diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index 38856873b97..1c6bb41984b 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -56,6 +56,10 @@ private slots: void relativePath(); void fromToString_data(); void fromToString(); + void fromString_data(); + void fromString(); + void toString_data(); + void toString(); void comparison_data(); void comparison(); void linkFromString_data(); @@ -281,6 +285,83 @@ void tst_fileutils::relativePath() QCOMPARE(actualPath.toString(), result); } +void tst_fileutils::toString_data() +{ + QTest::addColumn<QString>("scheme"); + QTest::addColumn<QString>("host"); + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("result"); + QTest::addColumn<QString>("userResult"); + + QTest::newRow("empty") << "" << "" << "" << "" << ""; + QTest::newRow("scheme") << "http" << "" << "" << QDir::rootPath() + "__qtc_devices__/http//./" << "http:///./"; + QTest::newRow("scheme-and-host") << "http" << "127.0.0.1" << "" << QDir::rootPath() + "__qtc_devices__/http/127.0.0.1/./" << "http://127.0.0.1/./"; + QTest::newRow("root") << "http" << "127.0.0.1" << "/" << QDir::rootPath() + "__qtc_devices__/http/127.0.0.1/" << "http://127.0.0.1/"; + + QTest::newRow("root-folder") << "" << "" << "/" << "/" << "/"; + QTest::newRow("qtc-dev-root-folder") << "" << "" << QDir::rootPath() + "__qtc_devices__" << QDir::rootPath() + "__qtc_devices__" << QDir::rootPath() + "__qtc_devices__"; + QTest::newRow("qtc-dev-type-root-folder") << "" << "" << QDir::rootPath() + "__qtc_devices__/docker" << QDir::rootPath() + "__qtc_devices__/docker" << QDir::rootPath() + "__qtc_devices__/docker"; + QTest::newRow("qtc-root-folder") << "docker" << "alpine:latest" << "/" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest/" << "docker://alpine:latest/"; + QTest::newRow("qtc-root-folder-rel") << "docker" << "alpine:latest" << "" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest/./" << "docker://alpine:latest/./"; +} + +void tst_fileutils::toString() +{ + QFETCH(QString, scheme); + QFETCH(QString, host); + QFETCH(QString, path); + QFETCH(QString, result); + QFETCH(QString, userResult); + + FilePath filePath; + filePath.setScheme(scheme); + filePath.setHost(host); + filePath.setPath(path); + + QCOMPARE(filePath.toString(), result); + QCOMPARE(filePath.toUserOutput(), userResult); +} + +void tst_fileutils::fromString_data() +{ + QTest::addColumn<QString>("input"); + + QTest::addColumn<QString>("scheme"); + QTest::addColumn<QString>("host"); + QTest::addColumn<QString>("path"); + + QTest::newRow("empty") << "" << "" << "" << ""; + + QTest::newRow("unix-root") << "/" << "" << "" << "/"; + QTest::newRow("unix-folder") << "/tmp" << "" << "" << "/tmp"; + QTest::newRow("unix-folder-with-trailing-slash") << "/tmp/" << "" << "" << "/tmp/"; + + QTest::newRow("windows-root") << "c:" << "" << "" << "c:"; + QTest::newRow("windows-folder") << "c:\\Windows" << "" << "" << "c:\\Windows"; + QTest::newRow("windows-folder-with-trailing-slash") << "c:\\Windows\\" << "" << "" << "c:\\Windows\\"; + QTest::newRow("windows-folder-slash") << "C:/Windows" << "" << "" << "C:/Windows"; + + QTest::newRow("docker-root-url") << "docker://1234/" << "docker" << "1234" << "/"; + QTest::newRow("docker-root-url-special") << QDir::rootPath() + "__qtc_devices__/docker/1234/" << "docker" << "1234" << "/"; + + QTest::newRow("qtc-dev") << QDir::rootPath() + "__qtc_devices__" << "" << "" << QDir::rootPath() + "__qtc_devices__"; + QTest::newRow("qtc-dev-type") << QDir::rootPath() + "__qtc_devices__/docker" << "" << "" << QDir::rootPath() + "__qtc_devices__/docker"; + QTest::newRow("qtc-dev-type-dev") << QDir::rootPath() + "__qtc_devices__/docker/1234" << "docker" << "1234" << "/"; + +} + +void tst_fileutils::fromString() +{ + QFETCH(QString, input); + QFETCH(QString, scheme); + QFETCH(QString, host); + QFETCH(QString, path); + FilePath filePath = FilePath::fromString(input); + QCOMPARE(filePath.scheme(), scheme); + QCOMPARE(filePath.host(), host); + QCOMPARE(filePath.path(), path); +} + void tst_fileutils::fromToString_data() { QTest::addColumn<QString>("scheme"); @@ -294,14 +375,14 @@ void tst_fileutils::fromToString_data() QTest::newRow("s3") << "" << "" << "/a/b" << "/a/b"; QTest::newRow("s4") - << "docker" << "1234abcdef" << "/bin/ls" << "docker://1234abcdef/bin/ls"; + << "docker" << "1234abcdef" << "/bin/ls" << QDir::rootPath() + "__qtc_devices__/docker/1234abcdef/bin/ls"; QTest::newRow("s5") - << "docker" << "1234" << "/bin/ls" << "docker://1234/bin/ls"; + << "docker" << "1234" << "/bin/ls" << QDir::rootPath() + "__qtc_devices__/docker/1234/bin/ls"; // This is not a proper URL. QTest::newRow("s6") - << "docker" << "1234" << "somefile" << "docker://1234/./somefile"; + << "docker" << "1234" << "somefile" << QDir::rootPath() + "__qtc_devices__/docker/1234/./somefile"; // Local Windows paths: QTest::newRow("w1") << "" << "" << "C:/data" << "C:/data"; diff --git a/tests/auto/utils/fsengine/CMakeLists.txt b/tests/auto/utils/fsengine/CMakeLists.txt new file mode 100644 index 00000000000..4a8eb74df3e --- /dev/null +++ b/tests/auto/utils/fsengine/CMakeLists.txt @@ -0,0 +1,4 @@ +add_qtc_test(tst_utils_fsengine + DEPENDS Utils + SOURCES tst_fsengine.cpp +) diff --git a/tests/auto/utils/fsengine/tst_fsengine.cpp b/tests/auto/utils/fsengine/tst_fsengine.cpp new file mode 100644 index 00000000000..00e66e9df97 --- /dev/null +++ b/tests/auto/utils/fsengine/tst_fsengine.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include <utils/filepath.h> +#include <utils/fileutils.h> +#include <utils/fsengine/fsengine.h> +#include <utils/hostosinfo.h> + +#include <QDebug> +#include <QFileSystemModel> +#include <QtTest> + +using namespace Utils; + +class tst_fsengine : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void testFilePathFromToString(); + + void testRootPathContainsFakeDir(); + void testNotExistingFile(); + void testCreateFile(); + void testListDir(); + void testCreateDir(); + void testWindowsPaths(); + void testUrl(); + +private: + QString makeTestPath(QString path, bool asUrl = false); + +private: + FSEngine engine; + QString tempFolder; +}; + +template<class... Args> +using Continuation = std::function<void(Args...)>; + +void tst_fsengine::initTestCase() +{ + if (!FSEngine::isAvailable()) + QSKIP("Utils was built without Filesystem Engine"); + + DeviceFileHooks deviceHooks; + + deviceHooks.fileContents = + [](const FilePath &path, qint64 maxSize, qint64 offset) -> QByteArray { + return FilePath::fromString(path.path()).fileContents(maxSize, offset); + }; + + deviceHooks.isExecutableFile = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).isExecutableFile(); + }; + deviceHooks.isReadableFile = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).isReadableFile(); + }; + deviceHooks.isReadableDir = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).isReadableDir(); + }; + deviceHooks.isWritableDir = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).isWritableDir(); + }; + deviceHooks.isWritableFile = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).isWritableFile(); + }; + deviceHooks.isFile = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).isFile(); + }; + deviceHooks.isDir = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).isDir(); + }; + deviceHooks.ensureWritableDir = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).ensureWritableDir(); + }; + deviceHooks.ensureExistingFile = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).ensureExistingFile(); + }; + deviceHooks.createDir = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).createDir(); + }; + deviceHooks.exists = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).exists(); + }; + deviceHooks.removeFile = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).removeFile(); + }; + deviceHooks.removeRecursively = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).removeRecursively(); + }; + deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) { + return FilePath::fromString(filePath.path()).copyFile(target); + }; + deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) { + return FilePath::fromString(filePath.path()).renameFile(target); + }; + deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) { + return FilePath::fromString(filePath.path()).searchInPath(dirs); + }; + deviceHooks.symLinkTarget = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).symLinkTarget(); + }; + deviceHooks.iterateDirectory = [](const FilePath &filePath, + const std::function<bool(const FilePath &)> &callBack, + const FileFilter &filter) { + return FilePath::fromString(filePath.path()) + .iterateDirectory( + [&filePath, &callBack](const FilePath &path) -> bool { + const FilePath devicePath = path.onDevice(filePath); + + return callBack(devicePath); + }, + filter); + }; + deviceHooks.asyncFileContents = [](const Continuation<QByteArray> &cont, + const FilePath &filePath, + qint64 maxSize, + qint64 offset) { + return FilePath::fromString(filePath.path()).asyncFileContents(cont, maxSize, offset); + }; + deviceHooks.writeFileContents = [](const FilePath &filePath, const QByteArray &data) { + return FilePath::fromString(filePath.path()).writeFileContents(data); + }; + deviceHooks.lastModified = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).lastModified(); + }; + deviceHooks.permissions = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).permissions(); + }; + deviceHooks.setPermissions = [](const FilePath &filePath, QFile::Permissions permissions) { + return FilePath::fromString(filePath.path()).setPermissions(permissions); + }; + deviceHooks.osType = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).osType(); + }; + // deviceHooks.environment = [](const FilePath &filePath) -> Environment {return {};}; + deviceHooks.fileSize = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).fileSize(); + }; + deviceHooks.bytesAvailable = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).bytesAvailable(); + }; + + deviceHooks.mapToDevicePath = [](const FilePath &filePath) { return filePath.path(); }; + + FileUtils::setDeviceFileHooks(deviceHooks); + + FSEngine::addDevice(FilePath::fromString("device://test")); + + tempFolder = QDir::tempPath(); + QDir testFolder(QString("%1/tst_fsengine").arg(tempFolder)); + if (testFolder.exists()) + QVERIFY(testFolder.removeRecursively()); + + QDir(tempFolder).mkdir("tst_fsengine"); +} + +void tst_fsengine::testFilePathFromToString() +{ + FilePath p = FilePath::fromString("device://test/test.txt"); + QCOMPARE(p.scheme(), "device"); + QCOMPARE(p.host(), "test"); + QCOMPARE(p.path(), "/test.txt"); + + QString asString = p.toString(); + QCOMPARE(asString, + FilePath::specialPath(FilePath::SpecialPathComponent::DeviceRootPath) + + "/test/test.txt"); + + FilePath p2 = FilePath::fromString(asString); + QCOMPARE(p.scheme(), "device"); + QCOMPARE(p.host(), "test"); + QCOMPARE(p.path(), "/test.txt"); +} + +void tst_fsengine::testRootPathContainsFakeDir() +{ + QDir root(HostOsInfo::isWindowsHost() ? "c:/" : "/"); + + const auto rootList = root.entryList(); + QVERIFY(rootList.contains(FilePath::specialPath(FilePath::SpecialPathComponent::RootName))); + + QDir schemes(FilePath::specialPath(FilePath::SpecialPathComponent::RootPath)); + const auto schemeList = schemes.entryList(); + QVERIFY(schemeList.contains("device")); + + QDir deviceRoot(FilePath::specialPath(FilePath::SpecialPathComponent::DeviceRootPath) + "/test"); + const auto deviceRootList = deviceRoot.entryList(); + QVERIFY(!deviceRootList.isEmpty()); +} + +void tst_fsengine::testNotExistingFile() +{ + QFile f(makeTestPath("test-does-not-exist.txt")); + + QCOMPARE(f.open(QIODevice::ReadOnly), false); +} + +void tst_fsengine::testCreateFile() +{ + { + QFile f(makeTestPath("test-create-file.txt")); + QCOMPARE(f.exists(), false); + QVERIFY(f.open(QIODevice::WriteOnly)); + } + + QFile f(makeTestPath("test-create-file.txt")); + QCOMPARE(f.exists(), true); +} + +void tst_fsengine::testCreateDir() +{ + QDir d(makeTestPath({})); + QCOMPARE(d.mkdir("test-create-dir"), true); +} + +QString tst_fsengine::makeTestPath(QString path, bool asUrl) +{ + if (asUrl) { + return QString("device://test%1/tst_fsengine/%2").arg(tempFolder, path); + } + + return QString(FilePath::specialPath(FilePath::SpecialPathComponent::DeviceRootPath) + + "/test%1/tst_fsengine/%2") + .arg(tempFolder, path); +} + +void tst_fsengine::testListDir() +{ + QDir dd(makeTestPath({})); + QCOMPARE(dd.mkdir("test-list-dir"), true); + + QDir d(makeTestPath("test-list-dir")); + + { + QFile f(makeTestPath("test-list-dir/f1.txt")); + QVERIFY(f.open(QIODevice::WriteOnly)); + } + + const auto list = d.entryList(); + QVERIFY(list.contains("f1.txt")); +} + +void tst_fsengine::testWindowsPaths() +{ + if (!HostOsInfo::isWindowsHost()) + QSKIP("This test is only valid on windows"); + + // Test upper-case "C:" + QVERIFY(FilePath::fromString("C:/__qtc_devices__/device/{cd6c7e4b-12fd-43ca-9bb2-053a38e6b7c5}") + .needsDevice()); + + // Test lower-case "C:" + QVERIFY(FilePath::fromString("c:/__qtc_devices__/device/{cd6c7e4b-12fd-43ca-9bb2-053a38e6b7c5}") + .needsDevice()); +} + +void tst_fsengine::testUrl() +{ + FilePath p = FilePath::fromString(makeTestPath("", true)); + + QVERIFY(p.needsDevice()); +} + +QTEST_GUILESS_MAIN(tst_fsengine) +#include "tst_fsengine.moc" |