forked from qt-creator/qt-creator
ProjectExplorer: introduce workspace project
A workspace project can be utilized to open projects that either do not have a project manager or which uses a project manager we do not support natively. The workspace project basically scans the folder recursively and adds all files and folders to the project tree. The user can provide a project name in the .qtcreator/project.json project file. It is also possible to exclude files and subdirectories from the project via the project trees context menu. Change-Id: If84a4d96c6217ae024af4e2869eb600e1faad372 Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -179,6 +179,7 @@ add_qtc_plugin(ProjectExplorer
|
|||||||
vcsannotatetaskhandler.cpp vcsannotatetaskhandler.h
|
vcsannotatetaskhandler.cpp vcsannotatetaskhandler.h
|
||||||
waitforstopdialog.cpp waitforstopdialog.h
|
waitforstopdialog.cpp waitforstopdialog.h
|
||||||
windebuginterface.cpp windebuginterface.h
|
windebuginterface.cpp windebuginterface.h
|
||||||
|
workspaceproject.h workspaceproject.cpp
|
||||||
xcodebuildparser.cpp xcodebuildparser.h
|
xcodebuildparser.cpp xcodebuildparser.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -37,6 +37,13 @@
|
|||||||
" <glob pattern='*.tasks'/>",
|
" <glob pattern='*.tasks'/>",
|
||||||
" <glob pattern='*.tasks.txt'/>",
|
" <glob pattern='*.tasks.txt'/>",
|
||||||
" </mime-type>",
|
" </mime-type>",
|
||||||
|
|
||||||
|
" <mime-type type='text/x-workspace-project'>",
|
||||||
|
" <sub-class-of type='application/json'/>",
|
||||||
|
" <comment>Workspace Qt Creator Project file</comment>",
|
||||||
|
" <glob pattern='project.json' weight='100'/>",
|
||||||
|
" </mime-type>",
|
||||||
|
|
||||||
"</mime-info>"
|
"</mime-info>"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -1252,6 +1252,14 @@ void Project::addVariablesToMacroExpander(const QByteArray &prefix,
|
|||||||
return project->projectFilePath();
|
return project->projectFilePath();
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
expander->registerVariable(fullPrefix + "ProjectDirectory",
|
||||||
|
//: %1 is something like "Active project"
|
||||||
|
::PE::Tr::tr("%1: Full path to Project Directory.").arg(descriptor),
|
||||||
|
[projectGetter]() -> QString {
|
||||||
|
if (const Project *const project = projectGetter())
|
||||||
|
return project->projectDirectory().toUserOutput();
|
||||||
|
return {};
|
||||||
|
});
|
||||||
expander->registerVariable(fullPrefix + "Kit:Name",
|
expander->registerVariable(fullPrefix + "Kit:Name",
|
||||||
//: %1 is something like "Active project"
|
//: %1 is something like "Active project"
|
||||||
::PE::Tr::tr("%1: The name of the active kit.").arg(descriptor),
|
::PE::Tr::tr("%1: The name of the active kit.").arg(descriptor),
|
||||||
|
@@ -68,8 +68,8 @@ public:
|
|||||||
|
|
||||||
BuildSystem *createBuildSystem(Target *target) const;
|
BuildSystem *createBuildSystem(Target *target) const;
|
||||||
|
|
||||||
Utils::FilePath projectFilePath() const;
|
virtual Utils::FilePath projectFilePath() const;
|
||||||
Utils::FilePath projectDirectory() const;
|
virtual Utils::FilePath projectDirectory() const;
|
||||||
|
|
||||||
// This does not affect nodes, only the root path.
|
// This does not affect nodes, only the root path.
|
||||||
void changeRootProjectDirectory();
|
void changeRootProjectDirectory();
|
||||||
|
@@ -80,6 +80,7 @@
|
|||||||
#include "toolchainmanager.h"
|
#include "toolchainmanager.h"
|
||||||
#include "toolchainoptionspage.h"
|
#include "toolchainoptionspage.h"
|
||||||
#include "vcsannotatetaskhandler.h"
|
#include "vcsannotatetaskhandler.h"
|
||||||
|
#include "workspaceproject.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include "windebuginterface.h"
|
#include "windebuginterface.h"
|
||||||
@@ -197,6 +198,7 @@ const int P_MODE_SESSION = 85;
|
|||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
const char LOAD[] = "ProjectExplorer.Load";
|
const char LOAD[] = "ProjectExplorer.Load";
|
||||||
|
const char LOADWORKSPACE[] = "ProjectExplorer.LoadWorkspace";
|
||||||
const char UNLOAD[] = "ProjectExplorer.Unload";
|
const char UNLOAD[] = "ProjectExplorer.Unload";
|
||||||
const char UNLOADCM[] = "ProjectExplorer.UnloadCM";
|
const char UNLOADCM[] = "ProjectExplorer.UnloadCM";
|
||||||
const char UNLOADOTHERSCM[] = "ProjectExplorer.UnloadOthersCM";
|
const char UNLOADOTHERSCM[] = "ProjectExplorer.UnloadOthersCM";
|
||||||
@@ -479,6 +481,7 @@ public:
|
|||||||
void buildQueueFinished(bool success);
|
void buildQueueFinished(bool success);
|
||||||
|
|
||||||
void loadAction();
|
void loadAction();
|
||||||
|
void openWorkspaceAction();
|
||||||
void handleUnloadProject();
|
void handleUnloadProject();
|
||||||
void unloadProjectContextMenu();
|
void unloadProjectContextMenu();
|
||||||
void unloadOtherProjectsContextMenu();
|
void unloadOtherProjectsContextMenu();
|
||||||
@@ -543,6 +546,7 @@ public:
|
|||||||
|
|
||||||
QAction *m_newAction;
|
QAction *m_newAction;
|
||||||
QAction *m_loadAction;
|
QAction *m_loadAction;
|
||||||
|
QAction *m_loadWorkspaceAction;
|
||||||
Action *m_unloadAction;
|
Action *m_unloadAction;
|
||||||
Action *m_unloadActionContextMenu;
|
Action *m_unloadActionContextMenu;
|
||||||
Action *m_unloadOthersActionContextMenu;
|
Action *m_unloadOthersActionContextMenu;
|
||||||
@@ -1099,6 +1103,12 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
|
|||||||
cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+O")));
|
cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+O")));
|
||||||
msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
|
msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
|
||||||
|
|
||||||
|
// load workspace action
|
||||||
|
dd->m_loadWorkspaceAction = new QAction(Tr::tr("Open Workspace..."), this);
|
||||||
|
cmd = ActionManager::registerAction(dd->m_loadWorkspaceAction, Constants::LOADWORKSPACE);
|
||||||
|
mfile->addAction(cmd, Core::Constants::G_FILE_OPEN);
|
||||||
|
msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES);
|
||||||
|
|
||||||
// Default open action
|
// Default open action
|
||||||
dd->m_openFileAction = new QAction(Tr::tr("Open File"), this);
|
dd->m_openFileAction = new QAction(Tr::tr("Open File"), this);
|
||||||
cmd = ActionManager::registerAction(dd->m_openFileAction, Constants::OPENFILE,
|
cmd = ActionManager::registerAction(dd->m_openFileAction, Constants::OPENFILE,
|
||||||
@@ -1659,6 +1669,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
|
|||||||
dd, &ProjectExplorerPlugin::openNewProjectDialog);
|
dd, &ProjectExplorerPlugin::openNewProjectDialog);
|
||||||
connect(dd->m_loadAction, &QAction::triggered,
|
connect(dd->m_loadAction, &QAction::triggered,
|
||||||
dd, &ProjectExplorerPluginPrivate::loadAction);
|
dd, &ProjectExplorerPluginPrivate::loadAction);
|
||||||
|
connect(dd->m_loadWorkspaceAction, &QAction::triggered,
|
||||||
|
dd, &ProjectExplorerPluginPrivate::openWorkspaceAction);
|
||||||
connect(dd->m_buildProjectOnlyAction, &QAction::triggered, dd, [] {
|
connect(dd->m_buildProjectOnlyAction, &QAction::triggered, dd, [] {
|
||||||
BuildManager::buildProjectWithoutDependencies(ProjectManager::startupProject());
|
BuildManager::buildProjectWithoutDependencies(ProjectManager::startupProject());
|
||||||
});
|
});
|
||||||
@@ -1887,6 +1899,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
|
|||||||
|
|
||||||
DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice));
|
DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice));
|
||||||
|
|
||||||
|
setupWorkspaceProject(this);
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
addTestCreator(&createSanitizerOutputParserTest);
|
addTestCreator(&createSanitizerOutputParserTest);
|
||||||
#endif
|
#endif
|
||||||
@@ -1919,6 +1933,30 @@ void ProjectExplorerPluginPrivate::loadAction()
|
|||||||
updateActions();
|
updateActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectExplorerPluginPrivate::openWorkspaceAction()
|
||||||
|
{
|
||||||
|
FilePath dir = dd->m_lastOpenDirectory;
|
||||||
|
|
||||||
|
// for your special convenience, we preselect a pro file if it is
|
||||||
|
// the current file
|
||||||
|
if (const IDocument *document = EditorManager::currentDocument()) {
|
||||||
|
const FilePath fn = document->filePath();
|
||||||
|
const bool isProject = dd->m_profileMimeTypes.contains(document->mimeType());
|
||||||
|
dir = isProject ? fn : fn.absolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath filePath = Utils::FileUtils::getExistingDirectory(
|
||||||
|
ICore::dialogParent(), Tr::tr("Open Workspace"), dir);
|
||||||
|
if (filePath.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
OpenProjectResult result = ProjectExplorerPlugin::openProject(filePath);
|
||||||
|
if (!result)
|
||||||
|
ProjectExplorerPlugin::showOpenProjectError(result);
|
||||||
|
|
||||||
|
updateActions();
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectExplorerPluginPrivate::unloadProjectContextMenu()
|
void ProjectExplorerPluginPrivate::unloadProjectContextMenu()
|
||||||
{
|
{
|
||||||
if (Project *p = ProjectTree::currentProject())
|
if (Project *p = ProjectTree::currentProject())
|
||||||
@@ -2282,10 +2320,7 @@ OpenProjectResult ProjectExplorerPlugin::openProjects(const FilePaths &filePaths
|
|||||||
|
|
||||||
MimeType mt = Utils::mimeTypeForFile(filePath);
|
MimeType mt = Utils::mimeTypeForFile(filePath);
|
||||||
if (ProjectManager::canOpenProjectForMimeType(mt)) {
|
if (ProjectManager::canOpenProjectForMimeType(mt)) {
|
||||||
if (!filePath.isFile()) {
|
if (Project *pro = ProjectManager::openProject(mt, filePath)) {
|
||||||
appendError(errorString,
|
|
||||||
Tr::tr("Failed opening project \"%1\": Project is not a file.").arg(filePath.toUserOutput()));
|
|
||||||
} else if (Project *pro = ProjectManager::openProject(mt, filePath)) {
|
|
||||||
QString restoreError;
|
QString restoreError;
|
||||||
Project::RestoreResult restoreResult = pro->restoreSettings(&restoreError);
|
Project::RestoreResult restoreResult = pro->restoreSettings(&restoreError);
|
||||||
if (restoreResult == Project::RestoreResult::Ok) {
|
if (restoreResult == Project::RestoreResult::Ok) {
|
||||||
|
210
src/plugins/projectexplorer/workspaceproject.cpp
Normal file
210
src/plugins/projectexplorer/workspaceproject.cpp
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "workspaceproject.h"
|
||||||
|
|
||||||
|
#include "buildsystem.h"
|
||||||
|
#include "projectexplorer.h"
|
||||||
|
#include "projectexplorerconstants.h"
|
||||||
|
#include "projectexplorertr.h"
|
||||||
|
#include "projectmanager.h"
|
||||||
|
#include "projecttree.h"
|
||||||
|
#include "target.h"
|
||||||
|
#include "treescanner.h"
|
||||||
|
|
||||||
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/stringutils.h>
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
const QLatin1StringView FOLDER_MIMETYPE{"inode/directory"};
|
||||||
|
const QLatin1StringView WORKSPACE_MIMETYPE{"text/x-workspace-project"};
|
||||||
|
const QLatin1StringView WORKSPACE_PROJECT_ID{"ProjectExplorer.WorkspaceProject"};
|
||||||
|
|
||||||
|
const QLatin1StringView PROJECT_NAME_KEY{"project.name"};
|
||||||
|
const QLatin1StringView FILES_EXCLUDE_KEY{"files.exclude"};
|
||||||
|
const QLatin1StringView EXCLUDE_ACTION_ID{"ProjectExplorer.ExcludeFromWorkspace"};
|
||||||
|
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
using namespace Core;
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
|
const expected_str<QJsonObject> projectDefinition(const Project *project)
|
||||||
|
{
|
||||||
|
if (auto fileContents = project->projectFilePath().fileContents())
|
||||||
|
return QJsonDocument::fromJson(*fileContents).object();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkEnabled(FolderNode *fn)
|
||||||
|
{
|
||||||
|
if (fn->findChildFileNode([](FileNode *fn) { return fn->isEnabled(); }))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (fn->findChildFolderNode([](FolderNode *fn) { return checkEnabled(fn); }))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fn->setEnabled(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WorkspaceBuildSystem : public BuildSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WorkspaceBuildSystem(Target *t)
|
||||||
|
:BuildSystem(t)
|
||||||
|
{
|
||||||
|
connect(&m_scanner, &TreeScanner::finished, this, [this] {
|
||||||
|
auto root = std::make_unique<ProjectNode>(projectDirectory());
|
||||||
|
root->setDisplayName(target()->project()->displayName());
|
||||||
|
std::vector<std::unique_ptr<FileNode>> nodePtrs
|
||||||
|
= Utils::transform<std::vector>(m_scanner.release().allFiles, [this](FileNode *fn) {
|
||||||
|
fn->setEnabled(!Utils::anyOf(
|
||||||
|
m_filters, [path = fn->path().path()](const QRegularExpression &filter) {
|
||||||
|
return filter.match(path).hasMatch();
|
||||||
|
}));
|
||||||
|
return std::unique_ptr<FileNode>(fn);
|
||||||
|
});
|
||||||
|
root->addNestedNodes(std::move(nodePtrs));
|
||||||
|
root->forEachFolderNode(&checkEnabled);
|
||||||
|
setRootProjectNode(std::move(root));
|
||||||
|
|
||||||
|
m_parseGuard.markAsSuccess();
|
||||||
|
m_parseGuard = {};
|
||||||
|
|
||||||
|
emitBuildSystemUpdated();
|
||||||
|
});
|
||||||
|
m_scanner.setDirFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
|
||||||
|
|
||||||
|
connect(target()->project(),
|
||||||
|
&Project::projectFileIsDirty,
|
||||||
|
this,
|
||||||
|
&BuildSystem::requestDelayedParse);
|
||||||
|
|
||||||
|
requestDelayedParse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void triggerParsing() final
|
||||||
|
{
|
||||||
|
m_filters.clear();
|
||||||
|
FilePath projectPath = project()->projectDirectory();
|
||||||
|
|
||||||
|
const QJsonObject json = projectDefinition(project()).value_or(QJsonObject());
|
||||||
|
const QJsonValue projectNameValue = json.value(PROJECT_NAME_KEY);
|
||||||
|
if (projectNameValue.isString())
|
||||||
|
project()->setDisplayName(projectNameValue.toString());
|
||||||
|
const QJsonArray excludesJson = json.value(FILES_EXCLUDE_KEY).toArray();
|
||||||
|
for (QJsonValue excludeJson : excludesJson) {
|
||||||
|
if (excludeJson.isString()) {
|
||||||
|
FilePath absolute = projectPath.pathAppended(excludeJson.toString());
|
||||||
|
if (absolute.isDir())
|
||||||
|
absolute = absolute.pathAppended("*");
|
||||||
|
m_filters << QRegularExpression(
|
||||||
|
Utils::wildcardToRegularExpression(absolute.path()),
|
||||||
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_parseGuard = guardParsingRun();
|
||||||
|
m_scanner.asyncScanForFiles(target()->project()->projectDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name() const final { return QLatin1String("Workspace"); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<QRegularExpression> m_filters;
|
||||||
|
ParseGuard m_parseGuard;
|
||||||
|
TreeScanner m_scanner;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WorkspaceProject : public Project
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
WorkspaceProject(const FilePath file)
|
||||||
|
: Project(FOLDER_MIMETYPE, file.isDir() ? file / ".qtcreator" / "project.json" : file)
|
||||||
|
{
|
||||||
|
QTC_CHECK(projectFilePath().absolutePath().ensureWritableDir());
|
||||||
|
QTC_CHECK(projectFilePath().ensureExistingFile());
|
||||||
|
|
||||||
|
setId(Id::fromString(WORKSPACE_PROJECT_ID));
|
||||||
|
setDisplayName(projectDirectory().fileName());
|
||||||
|
setBuildSystemCreator([](Target *t) { return new WorkspaceBuildSystem(t); });
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath projectDirectory() const override
|
||||||
|
{
|
||||||
|
return Project::projectDirectory().parentDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveProjectDefinition(const QJsonObject &json)
|
||||||
|
{
|
||||||
|
Utils::FileSaver saver(projectFilePath());
|
||||||
|
saver.write(QJsonDocument(json).toJson());
|
||||||
|
saver.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void excludePath(const FilePath &path)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(projectFilePath().exists(), return);
|
||||||
|
if (expected_str<QJsonObject> json = projectDefinition(this)) {
|
||||||
|
QJsonArray excludes = (*json)[FILES_EXCLUDE_KEY].toArray();
|
||||||
|
const QString relative = path.relativePathFrom(projectDirectory()).path();
|
||||||
|
if (excludes.contains(relative))
|
||||||
|
return;
|
||||||
|
excludes << relative;
|
||||||
|
json->insert(FILES_EXCLUDE_KEY, excludes);
|
||||||
|
saveProjectDefinition(*json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void excludeNode(Node *node)
|
||||||
|
{
|
||||||
|
node->setEnabled(false);
|
||||||
|
if (auto fileNode = node->asFileNode()) {
|
||||||
|
excludePath(fileNode->path());
|
||||||
|
} else if (auto folderNode = node->asFolderNode()) {
|
||||||
|
folderNode->forEachNode([](Node *node) { node->setEnabled(false); });
|
||||||
|
excludePath(folderNode->path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void setupWorkspaceProject(QObject *guard)
|
||||||
|
{
|
||||||
|
ProjectManager::registerProjectType<WorkspaceProject>(FOLDER_MIMETYPE);
|
||||||
|
ProjectManager::registerProjectType<WorkspaceProject>(WORKSPACE_MIMETYPE);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
ProjectTree::instance(),
|
||||||
|
&ProjectTree::aboutToShowContextMenu,
|
||||||
|
ProjectExplorerPlugin::instance(),
|
||||||
|
[](Node *node) {
|
||||||
|
const bool enabled = node && node->isEnabled()
|
||||||
|
&& qobject_cast<WorkspaceProject *>(node->getProject());
|
||||||
|
ActionManager::command(Id::fromString(EXCLUDE_ACTION_ID))->action()->setEnabled(enabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
ActionBuilder excludeAction(guard, Id::fromString(EXCLUDE_ACTION_ID));
|
||||||
|
excludeAction.setContext(Id::fromString(WORKSPACE_PROJECT_ID));
|
||||||
|
excludeAction.setText(Tr::tr("Exclude from Project"));
|
||||||
|
excludeAction.addToContainer(Constants::M_FOLDERCONTEXT, Constants::G_FOLDER_OTHER);
|
||||||
|
excludeAction.addToContainer(Constants::M_FILECONTEXT, Constants::G_FILE_OTHER);
|
||||||
|
excludeAction.addOnTriggered([] {
|
||||||
|
Node *node = ProjectTree::currentNode();
|
||||||
|
QTC_ASSERT(node, return);
|
||||||
|
const auto project = qobject_cast<WorkspaceProject *>(node->getProject());
|
||||||
|
QTC_ASSERT(project, return);
|
||||||
|
project->excludeNode(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ProjectExplorer
|
||||||
|
|
||||||
|
#include "workspaceproject.moc"
|
16
src/plugins/projectexplorer/workspaceproject.h
Normal file
16
src/plugins/projectexplorer/workspaceproject.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qglobal.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QObject;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
|
void setupWorkspaceProject(QObject *guard);
|
||||||
|
|
||||||
|
} // namespace ProjectExplorer
|
Reference in New Issue
Block a user