// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "projectexplorer_export.h" #include #include #include #include #include #include #include namespace Utils { class MimeType; } namespace ProjectExplorer { class BuildSystem; class Project; // File types common for qt projects enum class FileType : quint16 { Unknown = 0, Header, Source, Form, StateChart, Resource, QML, Project, App, Lib, FileTypeSize }; enum class ProductType { App, Lib, Other, None }; enum ProjectAction { // Special value to indicate that the actions are handled by the parent InheritedFromParent, AddSubProject, AddExistingProject, RemoveSubProject, // Let's the user select to which project file // the file is added AddNewFile, AddExistingFile, // Add files, which match user defined filters, // from an existing directory and its subdirectories AddExistingDirectory, // Removes a file from the project, optionally also // delete it on disc RemoveFile, // Deletes a file from the file system, informs the project // that a file was deleted // DeleteFile is a define on windows... EraseFile, Rename, // hides actions that use the path(): Open containing folder, open terminal here and Find in Directory HidePathActions, HideFileActions, HideFolderActions, }; enum class RemovedFilesFromProject { Ok, Wildcard, Error }; class FileNode; class FolderNode; class ProjectNode; class ContainerNode; class PROJECTEXPLORER_EXPORT DirectoryIcon { public: explicit DirectoryIcon(const QString &overlay); QIcon icon() const; // only safe in UI thread private: QString m_overlay; static QHash m_cache; }; using IconCreator = std::function; // Documentation inside. class PROJECTEXPLORER_EXPORT Node { public: enum PriorityLevel { DefaultPriority = 0, DefaultFilePriority = 100000, DefaultFolderPriority = 200000, DefaultVirtualFolderPriority = 300000, DefaultProjectPriority = 400000, DefaultProjectFilePriority = 500000 }; virtual ~Node(); virtual bool isFolderNodeType() const { return false; } virtual bool isProjectNodeType() const { return false; } virtual bool isVirtualFolderType() const { return false; } int priority() const; ProjectNode *parentProjectNode() const; // parent project, will be nullptr for the top-level project FolderNode *parentFolderNode() const; // parent folder or project ProjectNode *managingProject(); // project managing this node. // result is the container's rootProject node if this is a project container node // (i.e. possibly null) // or node if node is a top-level ProjectNode directly below a container // or node->parentProjectNode() for all other cases. const ProjectNode *managingProject() const; // see above. Project *getProject() const; const Utils::FilePath &filePath() const; // file system path int line() const; virtual QString displayName() const; virtual QString tooltip() const; bool isEnabled() const; bool listInProject() const; bool isGenerated() const; virtual bool supportsAction(ProjectAction action, const Node *node) const; void setEnabled(bool enabled); void setAbsoluteFilePathAndLine(const Utils::FilePath &filePath, int line); virtual FileNode *asFileNode() { return nullptr; } virtual const FileNode *asFileNode() const { return nullptr; } virtual FolderNode *asFolderNode() { return nullptr; } virtual const FolderNode *asFolderNode() const { return nullptr; } virtual ProjectNode *asProjectNode() { return nullptr; } virtual const ProjectNode *asProjectNode() const { return nullptr; } virtual ContainerNode *asContainerNode() { return nullptr; } virtual const ContainerNode *asContainerNode() const { return nullptr; } virtual QString buildKey() const { return {}; } static bool sortByPath(const Node *a, const Node *b); void setParentFolderNode(FolderNode *parentFolder); void setListInProject(bool l); void setIsGenerated(bool g); void setPriority(int priority); void setLine(int line); static FileType fileTypeForMimeType(const Utils::MimeType &mt); static FileType fileTypeForFileName(const Utils::FilePath &file); Utils::FilePath path() const { return pathOrDirectory(false); } Utils::FilePath directory() const { return pathOrDirectory(true); } protected: Node(); Node(const Node &other) = delete; bool operator=(const Node &other) = delete; void setFilePath(const Utils::FilePath &filePath); private: Utils::FilePath pathOrDirectory(bool dir) const; FolderNode *m_parentFolderNode = nullptr; Utils::FilePath m_filePath; int m_line = -1; int m_priority = DefaultPriority; enum NodeFlag : quint16 { FlagNone = 0, FlagIsEnabled = 1 << 0, FlagIsGenerated = 1 << 1, FlagListInProject = 1 << 2, }; NodeFlag m_flags = FlagIsEnabled; }; class PROJECTEXPLORER_EXPORT FileNode : public Node { public: FileNode(const Utils::FilePath &filePath, const FileType fileType); FileNode *clone() const; FileType fileType() const; FileNode *asFileNode() final { return this; } const FileNode *asFileNode() const final { return this; } bool supportsAction(ProjectAction action, const Node *node) const override; QString displayName() const override; bool hasError() const; void setHasError(const bool error); void setHasError(const bool error) const; QIcon icon() const; void setIcon(const QIcon icon); bool useUnavailableMarker() const; void setUseUnavailableMarker(bool useUnavailableMarker); private: FileType m_fileType; mutable QIcon m_icon; mutable bool m_hasError = false; bool m_useUnavailableMarker = false; }; // Documentation inside. class PROJECTEXPLORER_EXPORT FolderNode : public Node { public: explicit FolderNode(const Utils::FilePath &folderPath); QString displayName() const override; // only safe from UI thread QIcon icon() const; bool isFolderNodeType() const override { return true; } Node *findNode(const std::function &filter); QList findNodes(const std::function &filter); void forEachNode(const std::function &fileTask, const std::function &folderTask = {}, const std::function &folderFilterTask = {}) const; void forEachGenericNode(const std::function &genericTask) const; void forEachProjectNode(const std::function &genericTask) const; void forEachFileNode(const std::function &fileTask) const; void forEachFolderNode(const std::function &folderTask) const; ProjectNode *findProjectNode(const std::function &predicate); // recursive FolderNode *findChildFolderNode(const std::function &predicate) const; // non-recursive FileNode *findChildFileNode(const std::function &predicate) const; // non-recursive const QList nodes() const; FileNode *fileNode(const Utils::FilePath &file) const; FolderNode *folderNode(const Utils::FilePath &directory) const; using FolderNodeFactory = std::function(const Utils::FilePath &)>; void addNestedNodes(std::vector> &&files, const Utils::FilePath &overrideBaseDir = Utils::FilePath(), const FolderNodeFactory &factory = [](const Utils::FilePath &fn) { return std::make_unique(fn); }); void addNestedNode(std::unique_ptr &&fileNode, const Utils::FilePath &overrideBaseDir = Utils::FilePath(), const FolderNodeFactory &factory = [](const Utils::FilePath &fn) { return std::make_unique(fn); }); virtual void compress(); // takes ownership of newNode. // Will delete newNode if oldNode is not a child of this node. bool replaceSubtree(Node *oldNode, std::unique_ptr &&newNode); void setDisplayName(const QString &name); // you have to make sure the QIcon is created in the UI thread if you are calling setIcon(QIcon) void setIcon(const QIcon &icon); void setIcon(const DirectoryIcon &directoryIcon); void setIcon(const QString &path); void setIcon(const IconCreator &iconCreator); class LocationInfo { public: LocationInfo() = default; LocationInfo(const QString &dn, const Utils::FilePath &p, const int l = 0, const unsigned int prio = 0) : path(p) , line(l) , priority(prio) , displayName(dn) {} Utils::FilePath path; int line = -1; unsigned int priority = 0; QString displayName; }; void setLocationInfo(const QVector &info); const QVector locationInfo() const; QString addFileFilter() const; void setAddFileFilter(const QString &filter) { m_addFileFilter = filter; } bool supportsAction(ProjectAction action, const Node *node) const override; virtual bool addFiles(const Utils::FilePaths &filePaths, Utils::FilePaths *notAdded = nullptr); virtual RemovedFilesFromProject removeFiles(const Utils::FilePaths &filePaths, Utils::FilePaths *notRemoved = nullptr); virtual bool deleteFiles(const Utils::FilePaths &filePaths); virtual bool canRenameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath); virtual bool renameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath); virtual bool addDependencies(const QStringList &dependencies); class AddNewInformation { public: AddNewInformation(const QString &name, int p) :displayName(name), priority(p) { } QString displayName; int priority; }; virtual AddNewInformation addNewInformation(const Utils::FilePaths &files, Node *context) const; // determines if node will be shown in the flat view, by default folder and projects aren't shown virtual bool showInSimpleTree() const; // determines if node will always be shown when hiding empty directories bool showWhenEmpty() const; void setShowWhenEmpty(bool showWhenEmpty); void addNode(std::unique_ptr &&node); bool isEmpty() const; FolderNode *asFolderNode() override { return this; } const FolderNode *asFolderNode() const override { return this; } protected: virtual void handleSubTreeChanged(FolderNode *node); std::vector> m_nodes; QVector m_locations; private: std::unique_ptr takeNode(Node *node); QString m_displayName; QString m_addFileFilter; mutable std::variant m_icon; bool m_showWhenEmpty = false; }; class PROJECTEXPLORER_EXPORT VirtualFolderNode : public FolderNode { public: explicit VirtualFolderNode(const Utils::FilePath &folderPath); bool isFolderNodeType() const override { return false; } bool isVirtualFolderType() const override { return true; } bool isSourcesOrHeaders() const { return m_isSourcesOrHeaders; } void setIsSourcesOrHeaders(bool on) { m_isSourcesOrHeaders = on; } private: bool m_isSourcesOrHeaders = false; // "Sources" or "Headers" }; // Documentation inside. class PROJECTEXPLORER_EXPORT ProjectNode : public FolderNode { public: explicit ProjectNode(const Utils::FilePath &projectFilePath); virtual bool canAddSubProject(const Utils::FilePath &proFilePath) const; virtual bool addSubProject(const Utils::FilePath &proFile); virtual QStringList subProjectFileNamePatterns() const; virtual bool removeSubProject(const Utils::FilePath &proFilePath); virtual std::optional visibleAfterAddFileAction() const { return std::nullopt; } bool isFolderNodeType() const override { return false; } bool isProjectNodeType() const override { return true; } bool showInSimpleTree() const override { return true; } bool addFiles(const Utils::FilePaths &filePaths, Utils::FilePaths *notAdded = nullptr) final; RemovedFilesFromProject removeFiles(const Utils::FilePaths &filePaths, Utils::FilePaths *notRemoved = nullptr) final; bool deleteFiles(const Utils::FilePaths &filePaths) final; bool canRenameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath) final; bool renameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath) final; bool addDependencies(const QStringList &dependencies) final; bool supportsAction(ProjectAction action, const Node *node) const final; // by default returns false virtual bool deploysFolder(const QString &folder) const; ProjectNode *projectNode(const Utils::FilePath &file) const; ProjectNode *asProjectNode() final { return this; } const ProjectNode *asProjectNode() const final { return this; } virtual QStringList targetApplications() const { return {}; } virtual bool parseInProgress() const { return false; } virtual bool validParse() const { return false; } virtual QVariant data(Utils::Id role) const; virtual bool setData(Utils::Id role, const QVariant &value) const; bool isProduct() const { return m_productType != ProductType::None; } ProductType productType() const { return m_productType; } // TODO: Currently used only for "Build for current run config" functionality, but we should // probably use it to centralize the node-specific "Build" functionality that // currently each project manager plugin adds to the context menu by itself. // The function should then move up to the Node class, so it can also serve the // "build single file" case. virtual void build() {} void setFallbackData(Utils::Id key, const QVariant &value); protected: void setProductType(ProductType type) { m_productType = type; } QString m_target; private: BuildSystem *buildSystem() const; QHash m_fallbackData; // Used in data(), unless overridden. ProductType m_productType = ProductType::None; }; class PROJECTEXPLORER_EXPORT ContainerNode : public FolderNode { public: ContainerNode(Project *project); QString displayName() const final; bool supportsAction(ProjectAction action, const Node *node) const final; bool isFolderNodeType() const override { return false; } bool isProjectNodeType() const override { return true; } ContainerNode *asContainerNode() final { return this; } const ContainerNode *asContainerNode() const final { return this; } ProjectNode *rootProjectNode() const; Project *project() const { return m_project; } void removeAllChildren(); private: void handleSubTreeChanged(FolderNode *node) final; Project *m_project; }; class PROJECTEXPLORER_EXPORT ResourceFileNode : public ProjectExplorer::FileNode { public: ResourceFileNode(const Utils::FilePath &filePath, const QString &qrcPath, const QString &displayName); QString displayName() const override; QString qrcPath() const; bool supportsAction(ProjectExplorer::ProjectAction action, const Node *node) const override; private: QString m_qrcPath; QString m_displayName; }; } // namespace ProjectExplorer Q_DECLARE_METATYPE(ProjectExplorer::Node *) Q_DECLARE_METATYPE(ProjectExplorer::FolderNode *)