1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #pragma once
27 
28 #include "projectexplorer_export.h"
29 
30 #include <QFutureInterface>
31 #include <QIcon>
32 #include <QStringList>
33 
34 #include <utils/fileutils.h>
35 #include <utils/id.h>
36 #include <utils/optional.h>
37 #include <utils/variant.h>
38 
39 #include <functional>
40 
41 namespace Utils { class MimeType; }
42 
43 namespace ProjectExplorer {
44 
45 class BuildSystem;
46 class Project;
47 
48 // File types common for qt projects
49 enum class FileType : quint16 {
50     Unknown = 0,
51     Header,
52     Source,
53     Form,
54     StateChart,
55     Resource,
56     QML,
57     Project,
58     FileTypeSize
59 };
60 
61 enum class ProductType { App, Lib, Other, None };
62 
63 enum ProjectAction {
64     // Special value to indicate that the actions are handled by the parent
65     InheritedFromParent,
66     AddSubProject,
67     AddExistingProject,
68     RemoveSubProject,
69     // Let's the user select to which project file
70     // the file is added
71     AddNewFile,
72     AddExistingFile,
73     // Add files, which match user defined filters,
74     // from an existing directory and its subdirectories
75     AddExistingDirectory,
76     // Removes a file from the project, optionally also
77     // delete it on disc
78     RemoveFile,
79     // Deletes a file from the file system, informs the project
80     // that a file was deleted
81     // DeleteFile is a define on windows...
82     EraseFile,
83     Rename,
84     // hides actions that use the path(): Open containing folder, open terminal here and Find in Directory
85     HidePathActions,
86     HideFileActions,
87     HideFolderActions,
88 };
89 
90 enum class RemovedFilesFromProject { Ok, Wildcard, Error };
91 
92 class FileNode;
93 class FolderNode;
94 class ProjectNode;
95 class ContainerNode;
96 
97 class PROJECTEXPLORER_EXPORT DirectoryIcon
98 {
99 public:
100     explicit DirectoryIcon(const QString &overlay);
101 
102     QIcon icon() const; // only safe in UI thread
103 
104 private:
105     QString m_overlay;
106     static QHash<QString, QIcon> m_cache;
107 };
108 
109 using IconCreator = std::function<QIcon()>;
110 
111 // Documentation inside.
112 class PROJECTEXPLORER_EXPORT Node
113 {
114 public:
115     enum PriorityLevel {
116         DefaultPriority = 0,
117         DefaultFilePriority = 100000,
118         DefaultFolderPriority = 200000,
119         DefaultVirtualFolderPriority = 300000,
120         DefaultProjectPriority = 400000,
121         DefaultProjectFilePriority = 500000
122     };
123 
124     virtual ~Node();
125 
isFolderNodeType()126     virtual bool isFolderNodeType() const { return false; }
isProjectNodeType()127     virtual bool isProjectNodeType() const { return false; }
isVirtualFolderType()128     virtual bool isVirtualFolderType() const { return false; }
129 
130     int priority() const;
131 
132     ProjectNode *parentProjectNode() const; // parent project, will be nullptr for the top-level project
133     FolderNode *parentFolderNode() const; // parent folder or project
134 
135     ProjectNode *managingProject();  // project managing this node.
136                                      // result is the container's rootProject node if this is a project container node
137                                      // (i.e. possibly null)
138                                      // or node if node is a top-level ProjectNode directly below a container
139                                      // or node->parentProjectNode() for all other cases.
140     const ProjectNode *managingProject() const; // see above.
141 
142     Project *getProject() const;
143 
144     const Utils::FilePath &filePath() const;  // file system path
145     int line() const;
146     virtual QString displayName() const;
147     virtual QString tooltip() const;
148     bool isEnabled() const;
149     bool listInProject() const;
150     bool isGenerated() const;
151 
152     virtual bool supportsAction(ProjectAction action, const Node *node) const;
153 
154     void setEnabled(bool enabled);
155     void setAbsoluteFilePathAndLine(const Utils::FilePath &filePath, int line);
156 
asFileNode()157     virtual FileNode *asFileNode() { return nullptr; }
asFileNode()158     virtual const FileNode *asFileNode() const { return nullptr; }
asFolderNode()159     virtual FolderNode *asFolderNode() { return nullptr; }
asFolderNode()160     virtual const FolderNode *asFolderNode() const { return nullptr; }
asProjectNode()161     virtual ProjectNode *asProjectNode() { return nullptr; }
asProjectNode()162     virtual const ProjectNode *asProjectNode() const { return nullptr; }
asContainerNode()163     virtual ContainerNode *asContainerNode() { return nullptr; }
asContainerNode()164     virtual const ContainerNode *asContainerNode() const { return nullptr; }
165 
buildKey()166     virtual QString buildKey() const { return QString(); }
167 
168     static bool sortByPath(const Node *a, const Node *b);
169     void setParentFolderNode(FolderNode *parentFolder);
170 
171     void setListInProject(bool l);
172     void setIsGenerated(bool g);
173     void setPriority(int priority);
174     void setLine(int line);
175 
176     static FileType fileTypeForMimeType(const Utils::MimeType &mt);
177     static FileType fileTypeForFileName(const Utils::FilePath &file);
178 
path()179     QString path() const { return pathOrDirectory(false); }
directory()180     QString directory() const { return pathOrDirectory(true); }
181 
182 protected:
183     Node();
184     Node(const Node &other) = delete;
185     bool operator=(const Node &other) = delete;
186 
187     void setFilePath(const Utils::FilePath &filePath);
188 
189 private:
190     QString pathOrDirectory(bool dir) const;
191 
192     FolderNode *m_parentFolderNode = nullptr;
193     Utils::FilePath m_filePath;
194     int m_line = -1;
195     int m_priority = DefaultPriority;
196 
197     enum NodeFlag : quint16 {
198         FlagNone = 0,
199         FlagIsEnabled = 1 << 0,
200         FlagIsGenerated = 1 << 1,
201         FlagListInProject = 1 << 2,
202     };
203     NodeFlag m_flags = FlagIsEnabled;
204 };
205 
206 class PROJECTEXPLORER_EXPORT FileNode : public Node
207 {
208 public:
209     FileNode(const Utils::FilePath &filePath, const FileType fileType);
210 
211     FileNode *clone() const;
212 
213     FileType fileType() const;
214 
asFileNode()215     FileNode *asFileNode() final { return this; }
asFileNode()216     const FileNode *asFileNode() const final { return this; }
217 
218     bool supportsAction(ProjectAction action, const Node *node) const override;
219     QString displayName() const override;
220 
221     bool hasError() const;
222     void setHasError(const bool error);
223     void setHasError(const bool error) const;
224 
225     QIcon icon() const;
226     void setIcon(const QIcon icon);
227 
228 private:
229     FileType m_fileType;
230     mutable QIcon m_icon;
231     mutable bool m_hasError = false;
232 };
233 
234 // Documentation inside.
235 class PROJECTEXPLORER_EXPORT FolderNode : public Node
236 {
237 public:
238     explicit FolderNode(const Utils::FilePath &folderPath);
239 
240     QString displayName() const override;
241     // only safe from UI thread
242     QIcon icon() const;
243 
isFolderNodeType()244     bool isFolderNodeType() const override { return true; }
245 
246     Node *findNode(const std::function<bool(Node *)> &filter);
247     QList<Node *> findNodes(const std::function<bool(Node *)> &filter);
248 
249     void forEachNode(const std::function<void(FileNode *)> &fileTask,
250                      const std::function<void(FolderNode *)> &folderTask = {},
251                      const std::function<bool(const FolderNode *)> &folderFilterTask = {}) const;
252     void forEachGenericNode(const std::function<void(Node *)> &genericTask) const;
253     void forEachProjectNode(const std::function<void(const ProjectNode *)> &genericTask) const;
254     ProjectNode *findProjectNode(const std::function<bool(const ProjectNode *)> &predicate);
255     const QList<Node *> nodes() const;
256     QList<FileNode *> fileNodes() const;
257     FileNode *fileNode(const Utils::FilePath &file) const;
258     QList<FolderNode *> folderNodes() const;
259     FolderNode *folderNode(const Utils::FilePath &directory) const;
260 
261     using FolderNodeFactory = std::function<std::unique_ptr<FolderNode>(const Utils::FilePath &)>;
262     void addNestedNodes(std::vector<std::unique_ptr<FileNode>> &&files,
263                         const Utils::FilePath &overrideBaseDir = Utils::FilePath(),
264                         const FolderNodeFactory &factory
265                         = [](const Utils::FilePath &fn) { return std::make_unique<FolderNode>(fn); });
266     void addNestedNode(std::unique_ptr<FileNode> &&fileNode,
267                        const Utils::FilePath &overrideBaseDir = Utils::FilePath(),
268                        const FolderNodeFactory &factory
269                        = [](const Utils::FilePath &fn) { return std::make_unique<FolderNode>(fn); });
270     void compress();
271 
272     // takes ownership of newNode.
273     // Will delete newNode if oldNode is not a child of this node.
274     bool replaceSubtree(Node *oldNode, std::unique_ptr<Node> &&newNode);
275 
276     void setDisplayName(const QString &name);
277     // you have to make sure the QIcon is created in the UI thread if you are calling setIcon(QIcon)
278     void setIcon(const QIcon &icon);
279     void setIcon(const DirectoryIcon &directoryIcon);
280     void setIcon(const QString &path);
281     void setIcon(const IconCreator &iconCreator);
282 
283     class LocationInfo
284     {
285     public:
286         LocationInfo() = default;
287         LocationInfo(const QString &dn,
288                      const Utils::FilePath &p,
289                      const int l = 0,
290                      const unsigned int prio = 0)
path(p)291             : path(p)
292             , line(l)
293             , priority(prio)
294             , displayName(dn)
295         {}
296 
297         Utils::FilePath path;
298         int line = -1;
299         unsigned int priority = 0;
300         QString displayName;
301     };
302     void setLocationInfo(const QVector<LocationInfo> &info);
303     const QVector<LocationInfo> locationInfo() const;
304 
305     QString addFileFilter() const;
setAddFileFilter(const QString & filter)306     void setAddFileFilter(const QString &filter) { m_addFileFilter = filter; }
307 
308     bool supportsAction(ProjectAction action, const Node *node) const override;
309 
310     virtual bool addFiles(const Utils::FilePaths &filePaths, Utils::FilePaths *notAdded = nullptr);
311     virtual RemovedFilesFromProject removeFiles(const Utils::FilePaths &filePaths,
312                                                 Utils::FilePaths *notRemoved = nullptr);
313     virtual bool deleteFiles(const Utils::FilePaths &filePaths);
314     virtual bool canRenameFile(const Utils::FilePath &oldFilePath,
315                                const Utils::FilePath &newFilePath);
316     virtual bool renameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath);
317     virtual bool addDependencies(const QStringList &dependencies);
318 
319     class AddNewInformation
320     {
321     public:
AddNewInformation(const QString & name,int p)322         AddNewInformation(const QString &name, int p)
323             :displayName(name), priority(p)
324         { }
325         QString displayName;
326         int priority;
327     };
328 
329     virtual AddNewInformation addNewInformation(const QStringList &files, Node *context) const;
330 
331 
332     // determines if node will be shown in the flat view, by default folder and projects aren't shown
333     virtual bool showInSimpleTree() const;
334 
335     // determines if node will always be shown when hiding empty directories
336     bool showWhenEmpty() const;
337     void setShowWhenEmpty(bool showWhenEmpty);
338 
339     void addNode(std::unique_ptr<Node> &&node);
340 
341     bool isEmpty() const;
342 
asFolderNode()343     FolderNode *asFolderNode() override { return this; }
asFolderNode()344     const FolderNode *asFolderNode() const override { return this; }
345 
346 protected:
347     virtual void handleSubTreeChanged(FolderNode *node);
348 
349     std::vector<std::unique_ptr<Node>> m_nodes;
350     QVector<LocationInfo> m_locations;
351 
352 private:
353     std::unique_ptr<Node> takeNode(Node *node);
354 
355     QString m_displayName;
356     QString m_addFileFilter;
357     mutable Utils::variant<QIcon, DirectoryIcon, QString, IconCreator> m_icon;
358     bool m_showWhenEmpty = false;
359 };
360 
361 class PROJECTEXPLORER_EXPORT VirtualFolderNode : public FolderNode
362 {
363 public:
364     explicit VirtualFolderNode(const Utils::FilePath &folderPath);
365 
isFolderNodeType()366     bool isFolderNodeType() const override { return false; }
isVirtualFolderType()367     bool isVirtualFolderType() const override { return true; }
368 
isSourcesOrHeaders()369     bool isSourcesOrHeaders() const { return m_isSourcesOrHeaders; }
setIsSourcesOrHeaders(bool on)370     void setIsSourcesOrHeaders(bool on) { m_isSourcesOrHeaders = on; }
371 
372 private:
373     bool m_isSourcesOrHeaders = false; // "Sources" or "Headers"
374 };
375 
376 // Documentation inside.
377 class PROJECTEXPLORER_EXPORT ProjectNode : public FolderNode
378 {
379 public:
380     explicit ProjectNode(const Utils::FilePath &projectFilePath);
381 
382     virtual bool canAddSubProject(const QString &proFilePath) const;
383     virtual bool addSubProject(const QString &proFile);
384     virtual QStringList subProjectFileNamePatterns() const;
385     virtual bool removeSubProject(const QString &proFilePath);
visibleAfterAddFileAction()386     virtual Utils::optional<Utils::FilePath> visibleAfterAddFileAction() const {
387         return Utils::nullopt;
388     }
389 
isFolderNodeType()390     bool isFolderNodeType() const override { return false; }
isProjectNodeType()391     bool isProjectNodeType() const override { return true; }
showInSimpleTree()392     bool showInSimpleTree() const override { return true; }
393 
394     bool addFiles(const Utils::FilePaths &filePaths, Utils::FilePaths *notAdded = nullptr) final;
395     RemovedFilesFromProject removeFiles(const Utils::FilePaths &filePaths,
396                                         Utils::FilePaths *notRemoved = nullptr) final;
397     bool deleteFiles(const Utils::FilePaths &filePaths) final;
398     bool canRenameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath) final;
399     bool renameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath) final;
400     bool addDependencies(const QStringList &dependencies) final;
401     bool supportsAction(ProjectAction action, const Node *node) const final;
402 
403     // by default returns false
404     virtual bool deploysFolder(const QString &folder) const;
405 
406     ProjectNode *projectNode(const Utils::FilePath &file) const;
407 
asProjectNode()408     ProjectNode *asProjectNode() final { return this; }
asProjectNode()409     const ProjectNode *asProjectNode() const final { return this; }
410 
targetApplications()411     virtual QStringList targetApplications() const { return {}; }
parseInProgress()412     virtual bool parseInProgress() const { return false; }
413 
validParse()414     virtual bool validParse() const { return false; }
415     virtual QVariant data(Utils::Id role) const;
416     virtual bool setData(Utils::Id role, const QVariant &value) const;
417 
isProduct()418     bool isProduct() const { return m_productType != ProductType::None; }
productType()419     ProductType productType() const { return m_productType; }
420 
421     // TODO: Currently used only for "Build for current run config" functionality, but we should
422     //       probably use it to centralize the node-specific "Build" functionality that
423     //       currently each project manager plugin adds to the context menu by itself.
424     //       The function should then move up to the Node class, so it can also serve the
425     //       "build single file" case.
build()426     virtual void build() {}
427 
428     void setFallbackData(Utils::Id key, const QVariant &value);
429 
430 protected:
setProductType(ProductType type)431     void setProductType(ProductType type) { m_productType = type; }
432 
433     QString m_target;
434 
435 private:
436     BuildSystem *buildSystem() const;
437 
438     QHash<Utils::Id, QVariant> m_fallbackData; // Used in data(), unless overridden.
439     ProductType m_productType = ProductType::None;
440 };
441 
442 class PROJECTEXPLORER_EXPORT ContainerNode : public FolderNode
443 {
444 public:
445     ContainerNode(Project *project);
446 
447     QString displayName() const final;
448     bool supportsAction(ProjectAction action, const Node *node) const final;
449 
isFolderNodeType()450     bool isFolderNodeType() const override { return false; }
isProjectNodeType()451     bool isProjectNodeType() const override { return true; }
452 
asContainerNode()453     ContainerNode *asContainerNode() final { return this; }
asContainerNode()454     const ContainerNode *asContainerNode() const final { return this; }
455 
456     ProjectNode *rootProjectNode() const;
project()457     Project *project() const { return m_project; }
458 
459     void removeAllChildren();
460 
461 private:
462     void handleSubTreeChanged(FolderNode *node) final;
463 
464     Project *m_project;
465 };
466 
467 } // namespace ProjectExplorer
468 
469 Q_DECLARE_METATYPE(ProjectExplorer::Node *)
470 Q_DECLARE_METATYPE(ProjectExplorer::FolderNode *)
471