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