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 #include "projectnodes.h"
27 
28 #include "buildconfiguration.h"
29 #include "buildsystem.h"
30 #include "project.h"
31 #include "projectexplorerconstants.h"
32 #include "projecttree.h"
33 #include "target.h"
34 
35 #include <coreplugin/fileiconprovider.h>
36 #include <coreplugin/icore.h>
37 #include <coreplugin/iversioncontrol.h>
38 #include <coreplugin/vcsmanager.h>
39 
40 #include <utils/fileutils.h>
41 #include <utils/hostosinfo.h>
42 #include <utils/mimetypes/mimedatabase.h>
43 #include <utils/mimetypes/mimetype.h>
44 #include <utils/pointeralgorithm.h>
45 #include <utils/qtcassert.h>
46 #include <utils/stringutils.h>
47 #include <utils/utilsicons.h>
48 
49 #include <QDir>
50 #include <QFileInfo>
51 #include <QIcon>
52 #include <QStyle>
53 #include <QThread>
54 #include <QTimer>
55 
56 #include <memory>
57 
58 using namespace Utils;
59 
60 namespace ProjectExplorer {
61 
62 QHash<QString, QIcon> DirectoryIcon::m_cache;
63 
recursiveFindOrCreateFolderNode(FolderNode * folder,const Utils::FilePath & directory,const Utils::FilePath & overrideBaseDir,const FolderNode::FolderNodeFactory & factory)64 static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder,
65                                                    const Utils::FilePath &directory,
66                                                    const Utils::FilePath &overrideBaseDir,
67                                                    const FolderNode::FolderNodeFactory &factory)
68 {
69     Utils::FilePath path = overrideBaseDir.isEmpty() ? folder->filePath() : overrideBaseDir;
70 
71     Utils::FilePath directoryWithoutPrefix;
72     bool isRelative = false;
73 
74     if (path.isEmpty() || path.toFileInfo().isRoot()) {
75         directoryWithoutPrefix = directory;
76         isRelative = false;
77     } else {
78         if (directory.isChildOf(path) || directory == path) {
79             isRelative = true;
80             directoryWithoutPrefix = directory.relativeChildPath(path);
81         } else {
82             isRelative = false;
83             path.clear();
84             directoryWithoutPrefix = directory;
85         }
86     }
87     QStringList parts = directoryWithoutPrefix.toString().split('/', Qt::SkipEmptyParts);
88     if (!Utils::HostOsInfo::isWindowsHost() && !isRelative && !parts.isEmpty())
89         parts[0].prepend('/');
90 
91     ProjectExplorer::FolderNode *parent = folder;
92     foreach (const QString &part, parts) {
93         path = path.pathAppended(part);
94         // Find folder in subFolders
95         FolderNode *next = parent->folderNode(path);
96         if (!next) {
97             // No FolderNode yet, so create it
98             auto tmp = factory(path);
99             tmp->setDisplayName(part);
100             next = tmp.get();
101             parent->addNode(std::move(tmp));
102         }
103         parent = next;
104     }
105     return parent;
106 }
107 
108 /*!
109   \class ProjectExplorer::Node
110 
111   \brief The Node class is the base class of all nodes in the node hierarchy.
112 
113   The nodes are arranged in a tree where leaves are FileNodes and non-leaves are FolderNodes
114   A Project is a special Folder that manages the files and normal folders underneath it.
115 
116   The Watcher emits signals for structural changes in the hierarchy.
117   A Visitor can be used to traverse all Projects and other Folders.
118 
119   \sa ProjectExplorer::FileNode, ProjectExplorer::FolderNode, ProjectExplorer::ProjectNode
120   \sa ProjectExplorer::NodesWatcher
121 */
122 
123 Node::Node() = default;
124 
setPriority(int p)125 void Node::setPriority(int p)
126 {
127     m_priority = p;
128 }
129 
setFilePath(const Utils::FilePath & filePath)130 void Node::setFilePath(const Utils::FilePath &filePath)
131 {
132     m_filePath = filePath;
133 }
134 
setLine(int line)135 void Node::setLine(int line)
136 {
137     m_line = line;
138 }
139 
setListInProject(bool l)140 void Node::setListInProject(bool l)
141 {
142     if (l)
143         m_flags = static_cast<NodeFlag>(m_flags | FlagListInProject);
144     else
145         m_flags = static_cast<NodeFlag>(m_flags & ~FlagListInProject);
146 }
147 
setIsGenerated(bool g)148 void Node::setIsGenerated(bool g)
149 {
150     if (g)
151         m_flags = static_cast<NodeFlag>(m_flags | FlagIsGenerated);
152     else
153         m_flags = static_cast<NodeFlag>(m_flags & ~FlagIsGenerated);
154 }
155 
setAbsoluteFilePathAndLine(const Utils::FilePath & path,int line)156 void Node::setAbsoluteFilePathAndLine(const Utils::FilePath &path, int line)
157 {
158     if (m_filePath == path && m_line == line)
159         return;
160 
161     m_filePath = path;
162     m_line = line;
163 }
164 
165 Node::~Node() = default;
166 
priority() const167 int Node::priority() const
168 {
169     return m_priority;
170 }
171 
172 /*!
173   Returns \c true if the Node should be listed as part of the projects file list.
174   */
listInProject() const175 bool Node::listInProject() const
176 {
177     return (m_flags & FlagListInProject) == FlagListInProject;
178 }
179 
180 /*!
181   The project that owns and manages the node. It is the first project in the list
182   of ancestors.
183   */
parentProjectNode() const184 ProjectNode *Node::parentProjectNode() const
185 {
186     if (!m_parentFolderNode)
187         return nullptr;
188     auto pn = m_parentFolderNode->asProjectNode();
189     if (pn)
190         return pn;
191     return m_parentFolderNode->parentProjectNode();
192 }
193 
194 /*!
195   The parent in the node hierarchy.
196   */
parentFolderNode() const197 FolderNode *Node::parentFolderNode() const
198 {
199     return m_parentFolderNode;
200 }
201 
managingProject()202 ProjectNode *Node::managingProject()
203 {
204     if (asContainerNode())
205         return asContainerNode()->rootProjectNode();
206     QTC_ASSERT(m_parentFolderNode, return nullptr);
207     ProjectNode *pn = parentProjectNode();
208     return pn ? pn : asProjectNode(); // projects manage themselves...
209 }
210 
managingProject() const211 const ProjectNode *Node::managingProject() const
212 {
213     return const_cast<Node *>(this)->managingProject();
214 }
215 
getProject() const216 Project *Node::getProject() const
217 {
218     if (const ContainerNode * const cn = asContainerNode())
219         return cn->project();
220     if (!m_parentFolderNode)
221         return nullptr;
222     return m_parentFolderNode->getProject();
223 }
224 
225 /*!
226   The path of the file or folder in the filesystem the node represents.
227   */
filePath() const228 const Utils::FilePath &Node::filePath() const
229 {
230     return m_filePath;
231 }
232 
line() const233 int Node::line() const
234 {
235     return m_line;
236 }
237 
displayName() const238 QString Node::displayName() const
239 {
240     return filePath().fileName();
241 }
242 
tooltip() const243 QString Node::tooltip() const
244 {
245     return filePath().toUserOutput();
246 }
247 
isEnabled() const248 bool Node::isEnabled() const
249 {
250     if ((m_flags & FlagIsEnabled) == 0)
251         return false;
252     FolderNode *parent = parentFolderNode();
253     return parent ? parent->isEnabled() : true;
254 }
255 
icon() const256 QIcon FileNode::icon() const
257 {
258     if (hasError())
259         return Utils::Icons::WARNING.icon();
260     if (m_icon.isNull())
261         m_icon = Core::FileIconProvider::icon(filePath());
262     return m_icon;
263 }
264 
setIcon(const QIcon icon)265 void FileNode::setIcon(const QIcon icon)
266 {
267     m_icon = icon;
268 }
269 
hasError() const270 bool FileNode::hasError() const
271 {
272     return m_hasError;
273 }
274 
setHasError(bool error)275 void FileNode::setHasError(bool error)
276 {
277     m_hasError = error;
278 }
279 
setHasError(bool error) const280 void FileNode::setHasError(bool error) const
281 {
282     m_hasError = error;
283 }
284 
285 /*!
286   Returns \c true if the file is automatically generated by a compile step.
287   */
isGenerated() const288 bool Node::isGenerated() const
289 {
290     return (m_flags & FlagIsGenerated) == FlagIsGenerated;
291 }
292 
supportsAction(ProjectAction,const Node *) const293 bool Node::supportsAction(ProjectAction, const Node *) const
294 {
295     return false;
296 }
297 
setEnabled(bool enabled)298 void Node::setEnabled(bool enabled)
299 {
300     if (enabled)
301         m_flags = static_cast<NodeFlag>(m_flags | FlagIsEnabled);
302     else
303         m_flags = static_cast<NodeFlag>(m_flags & ~FlagIsEnabled);
304 }
305 
sortByPath(const Node * a,const Node * b)306 bool Node::sortByPath(const Node *a, const Node *b)
307 {
308     return a->filePath() < b->filePath();
309 }
310 
setParentFolderNode(FolderNode * parentFolder)311 void Node::setParentFolderNode(FolderNode *parentFolder)
312 {
313     m_parentFolderNode = parentFolder;
314 }
315 
fileTypeForMimeType(const Utils::MimeType & mt)316 FileType Node::fileTypeForMimeType(const Utils::MimeType &mt)
317 {
318     FileType type = FileType::Source;
319     if (mt.isValid()) {
320         const QString mtName = mt.name();
321         if (mtName == Constants::C_HEADER_MIMETYPE
322                 || mtName == Constants::CPP_HEADER_MIMETYPE)
323             type = FileType::Header;
324         else if (mtName == Constants::FORM_MIMETYPE)
325             type = FileType::Form;
326         else if (mtName == Constants::RESOURCE_MIMETYPE)
327             type = FileType::Resource;
328         else if (mtName == Constants::SCXML_MIMETYPE)
329             type = FileType::StateChart;
330         else if (mtName == Constants::QML_MIMETYPE
331                  || mtName == Constants::QMLUI_MIMETYPE)
332             type = FileType::QML;
333     } else {
334         type = FileType::Unknown;
335     }
336     return type;
337 }
338 
fileTypeForFileName(const Utils::FilePath & file)339 FileType Node::fileTypeForFileName(const Utils::FilePath &file)
340 {
341     return fileTypeForMimeType(Utils::mimeTypeForFile(file, Utils::MimeMatchMode::MatchExtension));
342 }
343 
pathOrDirectory(bool dir) const344 QString Node::pathOrDirectory(bool dir) const
345 {
346     QString location;
347     const FolderNode *folder = asFolderNode();
348     if (isVirtualFolderType() && folder) {
349         // Virtual Folder case
350         // If there are files directly below or no subfolders, take the folder path
351         if (!folder->fileNodes().isEmpty() || folder->folderNodes().isEmpty()) {
352             location = m_filePath.toString();
353         } else {
354             // Otherwise we figure out a commonPath from the subfolders
355             QStringList list;
356             foreach (FolderNode *f, folder->folderNodes())
357                 list << f->filePath().toString() + QLatin1Char('/');
358             location = Utils::commonPath(list);
359         }
360 
361         QFileInfo fi(location);
362         while ((!fi.exists() || !fi.isDir()) && !fi.isRoot())
363             fi.setFile(fi.absolutePath());
364         location = fi.absoluteFilePath();
365     } else if (!m_filePath.isEmpty()) {
366         QFileInfo fi = m_filePath.toFileInfo();
367         // remove any /suffixes, which e.g. ResourceNode uses
368         // Note this could be removed again by making path() a true path again
369         // That requires changes in both the VirtualFolderNode and ResourceNode
370         while (!fi.exists() && !fi.isRoot())
371             fi.setFile(fi.absolutePath());
372 
373         if (dir)
374             location = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath();
375         else
376             location = fi.absoluteFilePath();
377     }
378     return location;
379 }
380 
381 /*!
382   \class ProjectExplorer::FileNode
383 
384   \brief The FileNode class is an in-memory presentation of a file.
385 
386   All file nodes are leaf nodes.
387 
388   \sa ProjectExplorer::FolderNode, ProjectExplorer::ProjectNode
389 */
390 
FileNode(const Utils::FilePath & filePath,const FileType fileType)391 FileNode::FileNode(const Utils::FilePath &filePath, const FileType fileType) :
392     m_fileType(fileType)
393 {
394     setFilePath(filePath);
395     setListInProject(true);
396     if (fileType == FileType::Project)
397         setPriority(DefaultProjectFilePriority);
398     else
399         setPriority(DefaultFilePriority);
400 }
401 
clone() const402 FileNode *FileNode::clone() const
403 {
404     auto fn = new FileNode(filePath(), fileType());
405     fn->setLine(line());
406     fn->setIsGenerated(isGenerated());
407     fn->setEnabled(isEnabled());
408     fn->setPriority(priority());
409     fn->setListInProject(listInProject());
410     return fn;
411 }
412 
fileType() const413 FileType FileNode::fileType() const
414 {
415     return m_fileType;
416 }
417 
supportsAction(ProjectAction action,const Node * node) const418 bool FileNode::supportsAction(ProjectAction action, const Node *node) const
419 {
420     if (action == InheritedFromParent)
421         return true;
422     FolderNode *parentFolder = parentFolderNode();
423     return parentFolder && parentFolder->supportsAction(action, node);
424 }
425 
displayName() const426 QString FileNode::displayName() const
427 {
428     int l = line();
429     if (l < 0)
430         return Node::displayName();
431     return Node::displayName() + ':' + QString::number(l);
432 }
433 
434 /*!
435   \class ProjectExplorer::FolderNode
436 
437   In-memory presentation of a folder. Note that the node itself + all children (files and folders) are "managed" by the owning project.
438 
439   \sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
440 */
FolderNode(const Utils::FilePath & folderPath)441 FolderNode::FolderNode(const Utils::FilePath &folderPath)
442 {
443     setFilePath(folderPath);
444     setPriority(DefaultFolderPriority);
445     setListInProject(false);
446     setIsGenerated(false);
447     m_displayName = folderPath.toUserOutput();
448 }
449 
450 /*!
451     Contains the display name that should be used in a view.
452     \sa setFolderName()
453  */
454 
displayName() const455 QString FolderNode::displayName() const
456 {
457     return m_displayName;
458 }
459 
460 /*!
461     Contains the icon that should be used in a view. Default is the directory icon
462     (QStyle::S_PDirIcon). Calling this method is only safe in the UI thread.
463 
464     \sa setIcon()
465  */
icon() const466 QIcon FolderNode::icon() const
467 {
468     QTC_CHECK(QThread::currentThread() == QCoreApplication::instance()->thread());
469 
470     // Instantiating the Icon provider is expensive.
471     if (auto strPtr = Utils::get_if<QString>(&m_icon)) {
472         m_icon = QIcon(*strPtr);
473     } else if (auto directoryIconPtr = Utils::get_if<DirectoryIcon>(&m_icon)) {
474         m_icon = directoryIconPtr->icon();
475     } else if (auto creatorPtr = Utils::get_if<IconCreator>(&m_icon)) {
476         m_icon = (*creatorPtr)();
477     } else {
478         auto iconPtr = Utils::get_if<QIcon>(&m_icon);
479         if (!iconPtr || iconPtr->isNull())
480             m_icon = Core::FileIconProvider::icon(QFileIconProvider::Folder);
481     }
482     return Utils::get<QIcon>(m_icon);
483 }
484 
findNode(const std::function<bool (Node *)> & filter)485 Node *FolderNode::findNode(const std::function<bool(Node *)> &filter)
486 {
487     if (filter(this))
488         return this;
489 
490     for (const std::unique_ptr<Node> &n : m_nodes) {
491         if (n->asFileNode() && filter(n.get())) {
492             return n.get();
493         } else if (FolderNode *folder = n->asFolderNode()) {
494             Node *result = folder->findNode(filter);
495             if (result)
496                 return result;
497         }
498     }
499     return nullptr;
500 }
501 
findNodes(const std::function<bool (Node *)> & filter)502 QList<Node *> FolderNode::findNodes(const std::function<bool(Node *)> &filter)
503 {
504     QList<Node *> result;
505     if (filter(this))
506         result.append(this);
507     for (const std::unique_ptr<Node> &n  : m_nodes) {
508         if (n->asFileNode() && filter(n.get()))
509             result.append(n.get());
510         else if (FolderNode *folder = n->asFolderNode())
511             result.append(folder->findNodes(filter));
512     }
513     return result;
514 }
515 
forEachNode(const std::function<void (FileNode *)> & fileTask,const std::function<void (FolderNode *)> & folderTask,const std::function<bool (const FolderNode *)> & folderFilterTask) const516 void FolderNode::forEachNode(const std::function<void(FileNode *)> &fileTask,
517                              const std::function<void(FolderNode *)> &folderTask,
518                              const std::function<bool(const FolderNode *)> &folderFilterTask) const
519 {
520     if (folderFilterTask) {
521         if (!folderFilterTask(this))
522             return;
523     }
524     if (fileTask) {
525         for (const std::unique_ptr<Node> &n : m_nodes) {
526             if (FileNode *fn = n->asFileNode())
527                 fileTask(fn);
528         }
529     }
530     for (const std::unique_ptr<Node> &n : m_nodes) {
531         if (FolderNode *fn = n->asFolderNode()) {
532             if (folderTask)
533                 folderTask(fn);
534             fn->forEachNode(fileTask, folderTask, folderFilterTask);
535         }
536     }
537 }
538 
forEachGenericNode(const std::function<void (Node *)> & genericTask) const539 void FolderNode::forEachGenericNode(const std::function<void(Node *)> &genericTask) const
540 {
541     for (const std::unique_ptr<Node> &n : m_nodes) {
542         genericTask(n.get());
543         if (FolderNode *fn = n->asFolderNode())
544             fn->forEachGenericNode(genericTask);
545     }
546 }
547 
forEachProjectNode(const std::function<void (const ProjectNode *)> & task) const548 void FolderNode::forEachProjectNode(const std::function<void(const ProjectNode *)> &task) const
549 {
550     if (const ProjectNode *projectNode = asProjectNode())
551         task(projectNode);
552 
553     for (const std::unique_ptr<Node> &n : m_nodes) {
554         if (FolderNode *fn = n->asFolderNode())
555             fn->forEachProjectNode(task);
556     }
557 }
558 
findProjectNode(const std::function<bool (const ProjectNode *)> & predicate)559 ProjectNode *FolderNode::findProjectNode(const std::function<bool(const ProjectNode *)> &predicate)
560 {
561     if (ProjectNode *projectNode = asProjectNode()) {
562         if (predicate(projectNode))
563             return projectNode;
564     }
565 
566     for (const std::unique_ptr<Node> &n : m_nodes) {
567         if (FolderNode *fn = n->asFolderNode()) {
568             if (ProjectNode *pn = fn->findProjectNode(predicate))
569                 return pn;
570         }
571     }
572     return nullptr;
573 }
574 
nodes() const575 const QList<Node *> FolderNode::nodes() const
576 {
577     return Utils::toRawPointer<QList>(m_nodes);
578 }
579 
fileNodes() const580 QList<FileNode*> FolderNode::fileNodes() const
581 {
582     QList<FileNode *> result;
583     for (const std::unique_ptr<Node> &n : m_nodes) {
584         if (FileNode *fn = n->asFileNode())
585             result.append(fn);
586     }
587     return result;
588 }
589 
fileNode(const Utils::FilePath & file) const590 FileNode *FolderNode::fileNode(const Utils::FilePath &file) const
591 {
592     return static_cast<FileNode *>(Utils::findOrDefault(m_nodes,
593                                                         [&file](const std::unique_ptr<Node> &n) {
594         const FileNode *fn = n->asFileNode();
595         return fn && fn->filePath() == file;
596     }));
597 }
598 
folderNodes() const599 QList<FolderNode*> FolderNode::folderNodes() const
600 {
601     QList<FolderNode *> result;
602     for (const std::unique_ptr<Node> &n : m_nodes) {
603         if (FolderNode *fn = n->asFolderNode())
604             result.append(fn);
605     }
606     return result;
607 }
608 
folderNode(const Utils::FilePath & directory) const609 FolderNode *FolderNode::folderNode(const Utils::FilePath &directory) const
610 {
611     Node *node = Utils::findOrDefault(m_nodes, [directory](const std::unique_ptr<Node> &n) {
612         FolderNode *fn = n->asFolderNode();
613         return fn && fn->filePath() == directory;
614     });
615     return static_cast<FolderNode *>(node);
616 }
617 
addNestedNode(std::unique_ptr<FileNode> && fileNode,const Utils::FilePath & overrideBaseDir,const FolderNodeFactory & factory)618 void FolderNode::addNestedNode(std::unique_ptr<FileNode> &&fileNode,
619                                const Utils::FilePath &overrideBaseDir,
620                                const FolderNodeFactory &factory)
621 {
622     FolderNode *folder = recursiveFindOrCreateFolderNode(this, fileNode->filePath().parentDir(),
623                                                          overrideBaseDir, factory);
624     folder->addNode(std::move(fileNode));
625 }
626 
addNestedNodes(std::vector<std::unique_ptr<FileNode>> && files,const Utils::FilePath & overrideBaseDir,const FolderNode::FolderNodeFactory & factory)627 void FolderNode::addNestedNodes(std::vector<std::unique_ptr<FileNode> > &&files,
628                                 const Utils::FilePath &overrideBaseDir,
629                                 const FolderNode::FolderNodeFactory &factory)
630 {
631     using DirWithNodes = std::pair<Utils::FilePath, std::vector<std::unique_ptr<FileNode>>>;
632     std::vector<DirWithNodes> fileNodesPerDir;
633     for (auto &f : files) {
634         const Utils::FilePath parentDir = f->filePath().parentDir();
635         const auto it = std::lower_bound(fileNodesPerDir.begin(), fileNodesPerDir.end(), parentDir,
636             [](const DirWithNodes &nad, const Utils::FilePath &dir) { return nad.first < dir; });
637         if (it != fileNodesPerDir.end() && it->first == parentDir) {
638             it->second.emplace_back(std::move(f));
639         } else {
640             DirWithNodes dirWithNodes;
641             dirWithNodes.first = parentDir;
642             dirWithNodes.second.emplace_back(std::move(f));
643             fileNodesPerDir.insert(it, std::move(dirWithNodes));
644         }
645     }
646 
647     for (DirWithNodes &dirWithNodes : fileNodesPerDir) {
648         FolderNode * const folderNode = recursiveFindOrCreateFolderNode(this, dirWithNodes.first,
649                                                                         overrideBaseDir, factory);
650         for (auto &f : dirWithNodes.second)
651             folderNode->addNode(std::move(f));
652     }
653 }
654 
655 // "Compress" a tree of foldernodes such that foldernodes with exactly one foldernode as a child
656 // are merged into one. This e.g. turns a sequence of FolderNodes "foo" "bar" "baz" into one
657 // FolderNode named "foo/bar/baz", saving a lot of clicks in the Project View to get to the actual
658 // files.
compress()659 void FolderNode::compress()
660 {
661     if (auto subFolder = m_nodes.size() == 1 ? m_nodes.at(0)->asFolderNode() : nullptr) {
662         const bool sameType = (isFolderNodeType() && subFolder->isFolderNodeType())
663                 || (isProjectNodeType() && subFolder->isProjectNodeType())
664                 || (isVirtualFolderType() && subFolder->isVirtualFolderType());
665         if (!sameType)
666             return;
667 
668         // Only one subfolder: Compress!
669         setDisplayName(QDir::toNativeSeparators(displayName() + "/" + subFolder->displayName()));
670         for (Node *n : subFolder->nodes()) {
671             std::unique_ptr<Node> toMove = subFolder->takeNode(n);
672             toMove->setParentFolderNode(nullptr);
673             addNode(std::move(toMove));
674         }
675         setAbsoluteFilePathAndLine(subFolder->filePath(), -1);
676 
677         takeNode(subFolder);
678 
679         compress();
680     } else {
681         for (FolderNode *fn : folderNodes())
682             fn->compress();
683     }
684 }
685 
replaceSubtree(Node * oldNode,std::unique_ptr<Node> && newNode)686 bool FolderNode::replaceSubtree(Node *oldNode, std::unique_ptr<Node> &&newNode)
687 {
688     std::unique_ptr<Node> keepAlive;
689     if (!oldNode) {
690         addNode(std::move(newNode)); // Happens e.g. when a project is registered
691     } else {
692         auto it = std::find_if(m_nodes.begin(), m_nodes.end(),
693                                [oldNode](const std::unique_ptr<Node> &n) {
694             return oldNode == n.get();
695         });
696         QTC_ASSERT(it != m_nodes.end(), return false);
697         if (newNode) {
698             newNode->setParentFolderNode(this);
699             keepAlive = std::move(*it);
700             *it = std::move(newNode);
701         } else {
702             keepAlive = takeNode(oldNode); // Happens e.g. when project is shutting down
703         }
704     }
705     handleSubTreeChanged(this);
706     return true;
707 }
708 
setDisplayName(const QString & name)709 void FolderNode::setDisplayName(const QString &name)
710 {
711     if (m_displayName == name)
712         return;
713     m_displayName = name;
714 }
715 
716 /*!
717     Sets the \a icon for this node. Note that creating QIcon instances is only safe in the UI thread.
718 */
setIcon(const QIcon & icon)719 void FolderNode::setIcon(const QIcon &icon)
720 {
721     m_icon = icon;
722 }
723 
724 /*!
725     Sets the \a directoryIcon that is used to create the icon for this node on demand.
726 */
setIcon(const DirectoryIcon & directoryIcon)727 void FolderNode::setIcon(const DirectoryIcon &directoryIcon)
728 {
729     m_icon = directoryIcon;
730 }
731 
732 /*!
733     Sets the \a path that is used to create the icon for this node on demand.
734 */
setIcon(const QString & path)735 void FolderNode::setIcon(const QString &path)
736 {
737     m_icon = path;
738 }
739 
740 /*!
741     Sets the \a iconCreator function that is used to create the icon for this node on demand.
742 */
setIcon(const IconCreator & iconCreator)743 void FolderNode::setIcon(const IconCreator &iconCreator)
744 {
745     m_icon = iconCreator;
746 }
747 
setLocationInfo(const QVector<FolderNode::LocationInfo> & info)748 void FolderNode::setLocationInfo(const QVector<FolderNode::LocationInfo> &info)
749 {
750     m_locations = info;
751     Utils::sort(m_locations, &LocationInfo::priority);
752 }
753 
locationInfo() const754 const QVector<FolderNode::LocationInfo> FolderNode::locationInfo() const
755 {
756     return m_locations;
757 }
758 
addFileFilter() const759 QString FolderNode::addFileFilter() const
760 {
761     if (!m_addFileFilter.isNull())
762         return m_addFileFilter;
763 
764     FolderNode *fn = parentFolderNode();
765     return fn ? fn->addFileFilter() : QString();
766 }
767 
supportsAction(ProjectAction action,const Node * node) const768 bool FolderNode::supportsAction(ProjectAction action, const Node *node) const
769 {
770     if (action == InheritedFromParent)
771         return true;
772     FolderNode *parentFolder = parentFolderNode();
773     return parentFolder && parentFolder->supportsAction(action, node);
774 }
775 
addFiles(const FilePaths & filePaths,FilePaths * notAdded)776 bool FolderNode::addFiles(const FilePaths &filePaths, FilePaths *notAdded)
777 {
778     ProjectNode *pn = managingProject();
779     if (pn)
780         return pn->addFiles(filePaths, notAdded);
781     return false;
782 }
783 
removeFiles(const FilePaths & filePaths,FilePaths * notRemoved)784 RemovedFilesFromProject FolderNode::removeFiles(const FilePaths &filePaths, FilePaths *notRemoved)
785 {
786     if (ProjectNode * const pn = managingProject())
787         return pn->removeFiles(filePaths, notRemoved);
788     return RemovedFilesFromProject::Error;
789 }
790 
deleteFiles(const FilePaths & filePaths)791 bool FolderNode::deleteFiles(const FilePaths &filePaths)
792 {
793     ProjectNode *pn = managingProject();
794     if (pn)
795         return pn->deleteFiles(filePaths);
796     return false;
797 }
798 
canRenameFile(const Utils::FilePath & oldFilePath,const Utils::FilePath & newFilePath)799 bool FolderNode::canRenameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath)
800 {
801     ProjectNode *pn = managingProject();
802     if (pn)
803         return pn->canRenameFile(oldFilePath, newFilePath);
804     return false;
805 }
806 
renameFile(const Utils::FilePath & oldFilePath,const Utils::FilePath & newFilePath)807 bool FolderNode::renameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath)
808 {
809     ProjectNode *pn = managingProject();
810     if (pn)
811         return pn->renameFile(oldFilePath, newFilePath);
812     return false;
813 }
814 
addDependencies(const QStringList & dependencies)815 bool FolderNode::addDependencies(const QStringList &dependencies)
816 {
817     if (ProjectNode * const pn = managingProject())
818         return pn->addDependencies(dependencies);
819     return false;
820 }
821 
addNewInformation(const QStringList & files,Node * context) const822 FolderNode::AddNewInformation FolderNode::addNewInformation(const QStringList &files, Node *context) const
823 {
824     Q_UNUSED(files)
825     return AddNewInformation(displayName(), context == this ? 120 : 100);
826 }
827 
828 /*!
829   Adds a node specified by \a node to the internal list of nodes.
830 */
831 
addNode(std::unique_ptr<Node> && node)832 void FolderNode::addNode(std::unique_ptr<Node> &&node)
833 {
834     QTC_ASSERT(node, return);
835     QTC_ASSERT(!node->parentFolderNode(), qDebug("Node has already a parent folder"));
836     node->setParentFolderNode(this);
837     m_nodes.emplace_back(std::move(node));
838 }
839 
840 /*!
841   Return a node specified by \a node from the internal list.
842 */
843 
takeNode(Node * node)844 std::unique_ptr<Node> FolderNode::takeNode(Node *node)
845 {
846     return Utils::takeOrDefault(m_nodes, node);
847 }
848 
showInSimpleTree() const849 bool FolderNode::showInSimpleTree() const
850 {
851     return false;
852 }
853 
showWhenEmpty() const854 bool FolderNode::showWhenEmpty() const
855 {
856     return m_showWhenEmpty;
857 }
858 
859 /*!
860   \class ProjectExplorer::VirtualFolderNode
861 
862   In-memory presentation of a virtual folder.
863   Note that the node itself + all children (files and folders) are "managed" by the owning project.
864   A virtual folder does not correspond to a actual folder on the file system. See for example the
865   sources, headers and forms folder the QmakeProjectManager creates
866   VirtualFolderNodes are always sorted before FolderNodes and are sorted according to their priority.
867 
868   \sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
869 */
VirtualFolderNode(const Utils::FilePath & folderPath)870 VirtualFolderNode::VirtualFolderNode(const Utils::FilePath &folderPath) :
871     FolderNode(folderPath)
872 {
873 }
874 
875 /*!
876   \class ProjectExplorer::ProjectNode
877 
878   \brief The ProjectNode class is an in-memory presentation of a Project.
879 
880   A concrete subclass must implement the persistent data.
881 
882   \sa ProjectExplorer::FileNode, ProjectExplorer::FolderNode
883 */
884 
885 /*!
886   Creates an uninitialized project node object.
887   */
ProjectNode(const Utils::FilePath & projectFilePath)888 ProjectNode::ProjectNode(const Utils::FilePath &projectFilePath) :
889     FolderNode(projectFilePath)
890 {
891     setPriority(DefaultProjectPriority);
892     setListInProject(true);
893     setDisplayName(projectFilePath.fileName());
894 }
895 
canAddSubProject(const QString & proFilePath) const896 bool ProjectNode::canAddSubProject(const QString &proFilePath) const
897 {
898     Q_UNUSED(proFilePath)
899     return false;
900 }
901 
addSubProject(const QString & proFilePath)902 bool ProjectNode::addSubProject(const QString &proFilePath)
903 {
904     Q_UNUSED(proFilePath)
905     return false;
906 }
907 
subProjectFileNamePatterns() const908 QStringList ProjectNode::subProjectFileNamePatterns() const
909 {
910     return QStringList();
911 }
912 
removeSubProject(const QString & proFilePath)913 bool ProjectNode::removeSubProject(const QString &proFilePath)
914 {
915     Q_UNUSED(proFilePath)
916     return false;
917 }
918 
addFiles(const FilePaths & filePaths,FilePaths * notAdded)919 bool ProjectNode::addFiles(const FilePaths &filePaths, FilePaths *notAdded)
920 {
921     if (BuildSystem *bs = buildSystem())
922         return bs->addFiles(this, filePaths, notAdded);
923     return false;
924 }
925 
removeFiles(const FilePaths & filePaths,FilePaths * notRemoved)926 RemovedFilesFromProject ProjectNode::removeFiles(const FilePaths &filePaths, FilePaths *notRemoved)
927 {
928     if (BuildSystem *bs = buildSystem())
929         return bs->removeFiles(this, filePaths, notRemoved);
930     return RemovedFilesFromProject::Error;
931 }
932 
deleteFiles(const FilePaths & filePaths)933 bool ProjectNode::deleteFiles(const FilePaths &filePaths)
934 {
935     if (BuildSystem *bs = buildSystem())
936         return bs->deleteFiles(this, filePaths);
937     return false;
938 }
939 
canRenameFile(const Utils::FilePath & oldFilePath,const Utils::FilePath & newFilePath)940 bool ProjectNode::canRenameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath)
941 {
942     if (BuildSystem *bs = buildSystem())
943         return bs->canRenameFile(this, oldFilePath, newFilePath);
944     return true;
945 }
946 
renameFile(const Utils::FilePath & oldFilePath,const Utils::FilePath & newFilePath)947 bool ProjectNode::renameFile(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath)
948 {
949     if (BuildSystem *bs = buildSystem())
950         return bs->renameFile(this, oldFilePath, newFilePath);
951     return false;
952 }
953 
addDependencies(const QStringList & dependencies)954 bool ProjectNode::addDependencies(const QStringList &dependencies)
955 {
956     if (BuildSystem *bs = buildSystem())
957         return bs->addDependencies(this, dependencies);
958     return false;
959 }
960 
supportsAction(ProjectAction action,const Node * node) const961 bool ProjectNode::supportsAction(ProjectAction action, const Node *node) const
962 {
963     if (BuildSystem *bs = buildSystem())
964         return bs->supportsAction(const_cast<ProjectNode *>(this), action, node);
965     return false;
966 }
967 
deploysFolder(const QString & folder) const968 bool ProjectNode::deploysFolder(const QString &folder) const
969 {
970     Q_UNUSED(folder)
971     return false;
972 }
973 
projectNode(const Utils::FilePath & file) const974 ProjectNode *ProjectNode::projectNode(const Utils::FilePath &file) const
975 {
976     for (const std::unique_ptr<Node> &n: m_nodes) {
977         if (ProjectNode *pnode = n->asProjectNode())
978             if (pnode->filePath() == file)
979                 return pnode;
980     }
981     return nullptr;
982 }
983 
data(Utils::Id role) const984 QVariant ProjectNode::data(Utils::Id role) const
985 {
986     return m_fallbackData.value(role);
987 }
988 
setData(Utils::Id role,const QVariant & value) const989 bool ProjectNode::setData(Utils::Id role, const QVariant &value) const
990 {
991     Q_UNUSED(role)
992     Q_UNUSED(value)
993     return false;
994 }
995 
setFallbackData(Utils::Id key,const QVariant & value)996 void ProjectNode::setFallbackData(Utils::Id key, const QVariant &value)
997 {
998     m_fallbackData.insert(key, value);
999 }
1000 
buildSystem() const1001 BuildSystem *ProjectNode::buildSystem() const
1002 {
1003     Project *p = getProject();
1004     Target *t = p ? p->activeTarget() : nullptr;
1005     return t ? t->buildSystem() : nullptr;
1006 }
1007 
isEmpty() const1008 bool FolderNode::isEmpty() const
1009 {
1010     return m_nodes.size() == 0;
1011 }
1012 
handleSubTreeChanged(FolderNode * node)1013 void FolderNode::handleSubTreeChanged(FolderNode *node)
1014 {
1015     if (FolderNode *parent = parentFolderNode())
1016         parent->handleSubTreeChanged(node);
1017 }
1018 
setShowWhenEmpty(bool showWhenEmpty)1019 void FolderNode::setShowWhenEmpty(bool showWhenEmpty)
1020 {
1021     m_showWhenEmpty = showWhenEmpty;
1022 }
1023 
ContainerNode(Project * project)1024 ContainerNode::ContainerNode(Project *project)
1025     : FolderNode(project->projectDirectory()), m_project(project)
1026 {
1027 }
1028 
displayName() const1029 QString ContainerNode::displayName() const
1030 {
1031     QString name = m_project->displayName();
1032 
1033     const QFileInfo fi = m_project->projectFilePath().toFileInfo();
1034     const QString dir = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath();
1035     if (Core::IVersionControl *vc = Core::VcsManager::findVersionControlForDirectory(dir)) {
1036         QString vcsTopic = vc->vcsTopic(dir);
1037         if (!vcsTopic.isEmpty())
1038             name += " [" + vcsTopic + ']';
1039     }
1040 
1041     return name;
1042 }
1043 
supportsAction(ProjectAction action,const Node * node) const1044 bool ContainerNode::supportsAction(ProjectAction action, const Node *node) const
1045 {
1046     const Node *rootNode = m_project->rootProjectNode();
1047     return rootNode && rootNode->supportsAction(action, node);
1048 }
1049 
rootProjectNode() const1050 ProjectNode *ContainerNode::rootProjectNode() const
1051 {
1052     return m_project->rootProjectNode();
1053 }
1054 
removeAllChildren()1055 void ContainerNode::removeAllChildren()
1056 {
1057     m_nodes.clear();
1058 }
1059 
handleSubTreeChanged(FolderNode * node)1060 void ContainerNode::handleSubTreeChanged(FolderNode *node)
1061 {
1062     m_project->handleSubTreeChanged(node);
1063 }
1064 
1065 /*!
1066     \class ProjectExplorer::DirectoryIcon
1067 
1068     The DirectoryIcon class represents a directory icon with an overlay.
1069 
1070     The QIcon is created on demand and globally cached, so other DirectoryIcon
1071     instances with the same overlay share the same QIcon instance.
1072 */
1073 
1074 /*!
1075     Creates a DirectoryIcon for the specified \a overlay.
1076 */
DirectoryIcon(const QString & overlay)1077 DirectoryIcon::DirectoryIcon(const QString &overlay)
1078     : m_overlay(overlay)
1079 {}
1080 
1081 /*!
1082     Returns the icon for this DirectoryIcon. Calling this method is only safe in the UI thread.
1083 */
icon() const1084 QIcon DirectoryIcon::icon() const
1085 {
1086     QTC_CHECK(QThread::currentThread() == QCoreApplication::instance()->thread());
1087     const auto it = m_cache.find(m_overlay);
1088     if (it != m_cache.end())
1089         return it.value();
1090     const QIcon icon = Core::FileIconProvider::directoryIcon(m_overlay);
1091     m_cache.insert(m_overlay, icon);
1092     return icon;
1093 }
1094 
1095 } // namespace ProjectExplorer
1096