2013-01-23 12:31:22 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2013-01-23 12:31:22 +01:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2016-01-15 14:57:40 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2013-01-23 12:31:22 +01:00
|
|
|
**
|
2015-09-18 11:34:48 +02:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
2016-01-15 14:57:40 +01:00
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2013-01-23 12:31:22 +01:00
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "designdocument.h"
|
|
|
|
|
#include "designdocumentview.h"
|
2013-08-02 13:25:45 +02:00
|
|
|
#include "documentmanager.h"
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
#include <metainfo.h>
|
|
|
|
|
#include <qmlobjectnode.h>
|
|
|
|
|
#include <rewritingexception.h>
|
|
|
|
|
#include <nodelistproperty.h>
|
|
|
|
|
#include <variantproperty.h>
|
|
|
|
|
#include <qmldesignerplugin.h>
|
2013-02-05 14:52:25 +01:00
|
|
|
#include <viewmanager.h>
|
2013-11-06 15:47:59 +01:00
|
|
|
#include <nodeinstanceview.h>
|
2020-03-12 15:55:30 +02:00
|
|
|
#include "qmldesignerconstants.h"
|
|
|
|
|
#include "qmlvisualnode.h"
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2014-11-19 17:58:33 +01:00
|
|
|
#include <projectexplorer/projecttree.h>
|
2013-01-23 12:31:22 +01:00
|
|
|
#include <projectexplorer/project.h>
|
|
|
|
|
#include <projectexplorer/target.h>
|
2014-03-05 15:17:43 +01:00
|
|
|
#include <projectexplorer/session.h>
|
2014-03-26 19:41:51 +01:00
|
|
|
#include <projectexplorer/kit.h>
|
2013-01-23 12:31:22 +01:00
|
|
|
#include <qtsupport/qtkitinformation.h>
|
|
|
|
|
#include <qtsupport/qtsupportconstants.h>
|
|
|
|
|
#include <qtsupport/qtversionmanager.h>
|
2020-03-12 15:55:30 +02:00
|
|
|
#include <coreplugin/icore.h>
|
2015-02-26 13:22:35 +01:00
|
|
|
#include <coreplugin/idocument.h>
|
2017-10-17 17:43:53 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2016-06-21 14:01:41 +02:00
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
|
|
|
|
|
2013-01-23 12:31:22 +01:00
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QUrl>
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
|
|
#include <QPlainTextEdit>
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
|
2015-02-01 18:44:47 +02:00
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
|
2013-01-23 12:31:22 +01:00
|
|
|
enum {
|
|
|
|
|
debug = false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
namespace QmlDesigner {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
\class QmlDesigner::DesignDocument
|
|
|
|
|
|
|
|
|
|
DesignDocument acts as a facade to a model representing a qml document,
|
|
|
|
|
and the different views/widgets accessing it.
|
|
|
|
|
*/
|
|
|
|
|
DesignDocument::DesignDocument(QObject *parent) :
|
|
|
|
|
QObject(parent),
|
|
|
|
|
m_documentModel(Model::create("QtQuick.Item", 1, 0)),
|
|
|
|
|
m_subComponentManager(new SubComponentManager(m_documentModel.data(), this)),
|
|
|
|
|
m_rewriterView (new RewriterView(RewriterView::Amend, m_documentModel.data())),
|
|
|
|
|
m_documentLoaded(false),
|
2019-10-30 08:25:31 +01:00
|
|
|
m_currentTarget(nullptr)
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:56:45 +02:00
|
|
|
DesignDocument::~DesignDocument() = default;
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
Model *DesignDocument::currentModel() const
|
|
|
|
|
{
|
2013-07-10 18:12:07 +02:00
|
|
|
if (m_inFileComponentModel)
|
|
|
|
|
return m_inFileComponentModel.data();
|
|
|
|
|
|
|
|
|
|
return m_documentModel.data();
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Model *DesignDocument::documentModel() const
|
|
|
|
|
{
|
|
|
|
|
return m_documentModel.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget *DesignDocument::centralWidget() const
|
|
|
|
|
{
|
|
|
|
|
return qobject_cast<QWidget*>(parent());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ViewManager &DesignDocument::viewManager() const
|
|
|
|
|
{
|
|
|
|
|
return QmlDesignerPlugin::instance()->viewManager();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ViewManager &DesignDocument::viewManager()
|
|
|
|
|
{
|
|
|
|
|
return QmlDesignerPlugin::instance()->viewManager();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ComponentTextModifier *createComponentTextModifier(TextModifier *originalModifier,
|
|
|
|
|
RewriterView *rewriterView,
|
|
|
|
|
const QString &componentText,
|
|
|
|
|
const ModelNode &componentNode)
|
|
|
|
|
{
|
2015-07-16 17:06:52 +02:00
|
|
|
bool explicitComponent = componentText.contains(QLatin1String("Component"));
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
ModelNode rootModelNode = rewriterView->rootModelNode();
|
|
|
|
|
|
|
|
|
|
int componentStartOffset;
|
|
|
|
|
int componentEndOffset;
|
|
|
|
|
|
|
|
|
|
int rootStartOffset = rewriterView->nodeOffset(rootModelNode);
|
|
|
|
|
|
|
|
|
|
if (explicitComponent) { //the component is explciit we have to find the first definition inside
|
|
|
|
|
componentStartOffset = rewriterView->firstDefinitionInsideOffset(componentNode);
|
|
|
|
|
componentEndOffset = componentStartOffset + rewriterView->firstDefinitionInsideLength(componentNode);
|
|
|
|
|
} else { //the component is implicit
|
|
|
|
|
componentStartOffset = rewriterView->nodeOffset(componentNode);
|
|
|
|
|
componentEndOffset = componentStartOffset + rewriterView->nodeLength(componentNode);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-19 13:48:40 +02:00
|
|
|
return new ComponentTextModifier(originalModifier, componentStartOffset, componentEndOffset, rootStartOffset);
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-04 11:16:46 +01:00
|
|
|
bool DesignDocument::loadInFileComponent(const ModelNode &componentNode)
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2017-04-26 14:32:02 +02:00
|
|
|
QString componentText = rewriterView()->extractText({componentNode}).value(componentNode);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
if (componentText.isEmpty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!componentNode.isRootNode()) {
|
|
|
|
|
//change to subcomponent model
|
2013-08-01 11:48:36 +02:00
|
|
|
changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.data(), rewriterView(), componentText, componentNode));
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-13 17:52:04 +02:00
|
|
|
AbstractView *DesignDocument::view() const
|
2013-02-05 14:52:25 +01:00
|
|
|
{
|
2013-07-31 15:45:09 +02:00
|
|
|
return viewManager().nodeInstanceView();
|
2013-02-05 14:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
2013-07-10 18:12:07 +02:00
|
|
|
Model* DesignDocument::createInFileComponentModel()
|
|
|
|
|
{
|
|
|
|
|
Model *model = Model::create("QtQuick.Item", 1, 0);
|
|
|
|
|
model->setFileUrl(m_documentModel->fileUrl());
|
|
|
|
|
|
|
|
|
|
return model;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-10 17:08:14 +01:00
|
|
|
QList<DocumentMessage> DesignDocument::qmlParseWarnings() const
|
2016-04-26 15:50:34 +02:00
|
|
|
{
|
|
|
|
|
return m_rewriterView->warnings();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DesignDocument::hasQmlParseWarnings() const
|
|
|
|
|
{
|
2016-06-29 16:00:00 +02:00
|
|
|
return !m_rewriterView->warnings().isEmpty();
|
2016-04-26 15:50:34 +02:00
|
|
|
}
|
|
|
|
|
|
2017-01-10 17:08:14 +01:00
|
|
|
QList<DocumentMessage> DesignDocument::qmlParseErrors() const
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
|
|
|
|
return m_rewriterView->errors();
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-26 15:50:34 +02:00
|
|
|
bool DesignDocument::hasQmlParseErrors() const
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2016-06-29 16:00:00 +02:00
|
|
|
return !m_rewriterView->errors().isEmpty();
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DesignDocument::displayName() const
|
|
|
|
|
{
|
2015-07-09 14:02:36 +02:00
|
|
|
return fileName().toString();
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DesignDocument::simplfiedDisplayName() const
|
|
|
|
|
{
|
2013-11-11 22:20:47 +02:00
|
|
|
if (rootModelNode().id().isEmpty())
|
2013-01-23 12:31:22 +01:00
|
|
|
return rootModelNode().id();
|
2013-11-11 22:20:47 +02:00
|
|
|
else
|
2013-01-23 12:31:22 +01:00
|
|
|
return rootModelNode().simplifiedTypeName();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
void DesignDocument::updateFileName(const Utils::FilePath & /*oldFileName*/, const Utils::FilePath &newFileName)
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
|
|
|
|
if (m_documentModel)
|
2014-12-21 21:54:30 +02:00
|
|
|
m_documentModel->setFileUrl(QUrl::fromLocalFile(newFileName.toString()));
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
if (m_inFileComponentModel)
|
2014-12-21 21:54:30 +02:00
|
|
|
m_inFileComponentModel->setFileUrl(QUrl::fromLocalFile(newFileName.toString()));
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2014-12-21 21:54:30 +02:00
|
|
|
viewManager().setItemLibraryViewResourcePath(newFileName.toFileInfo().absolutePath());
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
emit displayNameChanged(displayName());
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath DesignDocument::fileName() const
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2018-03-23 13:35:32 +01:00
|
|
|
if (editor())
|
|
|
|
|
return editor()->document()->filePath();
|
2019-05-28 13:49:26 +02:00
|
|
|
return Utils::FilePath();
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-30 08:25:31 +01:00
|
|
|
ProjectExplorer::Target *DesignDocument::currentTarget() const
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2019-10-30 08:25:31 +01:00
|
|
|
return m_currentTarget;
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DesignDocument::isDocumentLoaded() const
|
|
|
|
|
{
|
|
|
|
|
return m_documentLoaded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::resetToDocumentModel()
|
|
|
|
|
{
|
2013-07-10 18:12:07 +02:00
|
|
|
m_inFileComponentModel.reset();
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-04 15:03:46 +01:00
|
|
|
void DesignDocument::loadDocument(QPlainTextEdit *edit)
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
|
|
|
|
Q_CHECK_PTR(edit);
|
|
|
|
|
|
2017-03-19 00:19:33 +02:00
|
|
|
connect(edit, &QPlainTextEdit::undoAvailable,
|
|
|
|
|
this, &DesignDocument::undoAvailable);
|
|
|
|
|
connect(edit, &QPlainTextEdit::redoAvailable,
|
|
|
|
|
this, &DesignDocument::redoAvailable);
|
|
|
|
|
connect(edit, &QPlainTextEdit::modificationChanged,
|
|
|
|
|
this, &DesignDocument::dirtyStateChanged);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
m_documentTextModifier.reset(new BaseTextEditModifier(dynamic_cast<TextEditor::TextEditorWidget*>(plainTextEdit())));
|
2016-06-21 14:01:41 +02:00
|
|
|
|
|
|
|
|
connect(m_documentTextModifier.data(), &TextModifier::textChanged, this, &DesignDocument::updateQrcFiles);
|
|
|
|
|
|
2013-08-01 11:48:36 +02:00
|
|
|
m_documentModel->setTextModifier(m_documentTextModifier.data());
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2013-02-06 17:12:04 +01:00
|
|
|
m_inFileComponentTextModifier.reset();
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
updateFileName(Utils::FilePath(), fileName());
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2016-06-21 14:01:41 +02:00
|
|
|
updateQrcFiles();
|
|
|
|
|
|
2013-02-04 15:03:46 +01:00
|
|
|
m_documentLoaded = true;
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
2013-02-07 13:25:58 +01:00
|
|
|
void DesignDocument::changeToDocumentModel()
|
|
|
|
|
{
|
|
|
|
|
viewManager().detachRewriterView();
|
|
|
|
|
viewManager().detachViewsExceptRewriterAndComponetView();
|
|
|
|
|
|
2013-09-26 18:46:39 +02:00
|
|
|
|
2013-07-10 18:12:07 +02:00
|
|
|
m_inFileComponentModel.reset();
|
2013-02-07 13:25:58 +01:00
|
|
|
|
2013-08-01 11:48:36 +02:00
|
|
|
viewManager().attachRewriterView();
|
2013-02-07 13:25:58 +01:00
|
|
|
viewManager().attachViewsExceptRewriterAndComponetView();
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-24 16:27:45 +01:00
|
|
|
bool DesignDocument::isQtForMCUsProject() const
|
|
|
|
|
{
|
|
|
|
|
if (m_currentTarget)
|
|
|
|
|
return m_currentTarget->additionalData("CustomQtForMCUs").toBool();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-01 11:48:36 +02:00
|
|
|
void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textModifer)
|
2013-02-07 13:25:58 +01:00
|
|
|
{
|
2013-08-01 11:48:36 +02:00
|
|
|
m_inFileComponentTextModifier.reset(textModifer);
|
2013-02-07 13:25:58 +01:00
|
|
|
viewManager().detachRewriterView();
|
|
|
|
|
viewManager().detachViewsExceptRewriterAndComponetView();
|
|
|
|
|
|
2013-07-10 18:12:07 +02:00
|
|
|
m_inFileComponentModel.reset(createInFileComponentModel());
|
2013-08-01 11:48:36 +02:00
|
|
|
m_inFileComponentModel->setTextModifier(m_inFileComponentTextModifier.data());
|
2013-02-07 13:25:58 +01:00
|
|
|
|
2013-08-01 11:48:36 +02:00
|
|
|
viewManager().attachRewriterView();
|
2013-02-07 13:25:58 +01:00
|
|
|
viewManager().attachViewsExceptRewriterAndComponetView();
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-21 14:01:41 +02:00
|
|
|
void DesignDocument::updateQrcFiles()
|
|
|
|
|
{
|
|
|
|
|
ProjectExplorer::Project *currentProject = ProjectExplorer::SessionManager::projectForFile(fileName());
|
|
|
|
|
|
|
|
|
|
if (currentProject) {
|
2019-05-28 13:49:26 +02:00
|
|
|
for (const Utils::FilePath &fileName : currentProject->files(ProjectExplorer::Project::SourceFiles)) {
|
2016-06-21 14:01:41 +02:00
|
|
|
if (fileName.endsWith(".qrc"))
|
2017-12-06 10:27:27 +01:00
|
|
|
QmlJS::ModelManagerInterface::instance()->updateQrcFile(fileName.toString());
|
2016-06-21 14:01:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-12 17:30:50 +02:00
|
|
|
void DesignDocument::changeToSubComponent(const ModelNode &componentNode)
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
|
|
|
|
if (QmlDesignerPlugin::instance()->currentDesignDocument() != this)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-07-10 18:12:07 +02:00
|
|
|
if (m_inFileComponentModel)
|
2013-01-23 12:31:22 +01:00
|
|
|
changeToDocumentModel();
|
|
|
|
|
|
2013-02-04 11:16:46 +01:00
|
|
|
bool subComponentLoaded = loadInFileComponent(componentNode);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2013-02-06 17:12:04 +01:00
|
|
|
if (subComponentLoaded)
|
2013-08-01 11:48:36 +02:00
|
|
|
attachRewriterToModel();
|
2013-08-12 17:30:50 +02:00
|
|
|
|
|
|
|
|
QmlDesignerPlugin::instance()->viewManager().pushInFileComponentOnCrumbleBar(componentNode);
|
2013-09-26 18:46:39 +02:00
|
|
|
QmlDesignerPlugin::instance()->viewManager().setComponentNode(componentNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::changeToMaster()
|
|
|
|
|
{
|
|
|
|
|
if (QmlDesignerPlugin::instance()->currentDesignDocument() != this)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (m_inFileComponentModel)
|
|
|
|
|
changeToDocumentModel();
|
|
|
|
|
|
2016-10-07 14:16:49 +02:00
|
|
|
QmlDesignerPlugin::instance()->viewManager().pushFileOnCrumbleBar(fileName());
|
2013-09-26 18:46:39 +02:00
|
|
|
QmlDesignerPlugin::instance()->viewManager().setComponentNode(rootModelNode());
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
2013-08-01 11:48:36 +02:00
|
|
|
void DesignDocument::attachRewriterToModel()
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
|
|
|
|
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
|
|
|
|
Q_ASSERT(m_documentModel);
|
|
|
|
|
|
2013-08-01 11:48:36 +02:00
|
|
|
viewManager().attachRewriterView();
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
Q_ASSERT(m_documentModel);
|
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DesignDocument::isUndoAvailable() const
|
|
|
|
|
{
|
|
|
|
|
if (plainTextEdit())
|
|
|
|
|
return plainTextEdit()->document()->isUndoAvailable();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DesignDocument::isRedoAvailable() const
|
|
|
|
|
{
|
|
|
|
|
if (plainTextEdit())
|
|
|
|
|
return plainTextEdit()->document()->isRedoAvailable();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::close()
|
|
|
|
|
{
|
|
|
|
|
m_documentLoaded = false;
|
|
|
|
|
emit designDocumentClosed();
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-29 09:50:31 +01:00
|
|
|
void DesignDocument::updateSubcomponentManager()
|
|
|
|
|
{
|
|
|
|
|
Q_ASSERT(m_subComponentManager);
|
2015-07-09 14:02:36 +02:00
|
|
|
m_subComponentManager->update(QUrl::fromLocalFile(fileName().toString()), currentModel()->imports());
|
2013-01-29 09:50:31 +01:00
|
|
|
}
|
|
|
|
|
|
2013-01-23 12:31:22 +01:00
|
|
|
void DesignDocument::deleteSelected()
|
|
|
|
|
{
|
2013-07-10 18:12:07 +02:00
|
|
|
if (!currentModel())
|
2013-01-23 12:31:22 +01:00
|
|
|
return;
|
|
|
|
|
|
2019-05-31 16:49:04 +02:00
|
|
|
rewriterView()->executeInTransaction("DesignDocument::deleteSelected", [this](){
|
2013-07-31 15:45:09 +02:00
|
|
|
QList<ModelNode> toDelete = view()->selectedModelNodes();
|
2013-01-23 12:31:22 +01:00
|
|
|
foreach (ModelNode node, toDelete) {
|
2013-07-24 15:58:51 +02:00
|
|
|
if (node.isValid() && !node.isRootNode() && QmlObjectNode::isValidQmlObjectNode(node))
|
2013-01-23 12:31:22 +01:00
|
|
|
QmlObjectNode(node).destroy();
|
|
|
|
|
}
|
2019-05-31 16:49:04 +02:00
|
|
|
});
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::copySelected()
|
|
|
|
|
{
|
|
|
|
|
DesignDocumentView view;
|
|
|
|
|
|
2013-07-10 18:12:07 +02:00
|
|
|
currentModel()->attachView(&view);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2017-12-19 14:10:12 +01:00
|
|
|
DesignDocumentView::copyModelNodes(view.selectedModelNodes());
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::cutSelected()
|
|
|
|
|
{
|
|
|
|
|
copySelected();
|
|
|
|
|
deleteSelected();
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-21 20:21:14 +03:00
|
|
|
static void scatterItem(const ModelNode &pastedNode, const ModelNode &targetNode, int offset = -2000)
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2014-08-07 17:01:31 +02:00
|
|
|
if (targetNode.metaInfo().isValid() && targetNode.metaInfo().isLayoutable())
|
|
|
|
|
return;
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2020-02-12 14:56:08 +01:00
|
|
|
if (!(pastedNode.hasVariantProperty("x") && pastedNode.hasVariantProperty("y")))
|
|
|
|
|
return;
|
|
|
|
|
|
2013-01-23 12:31:22 +01:00
|
|
|
bool scatter = false;
|
2014-04-24 15:21:00 +02:00
|
|
|
foreach (const ModelNode &childNode, targetNode.directSubModelNodes()) {
|
2013-01-23 12:31:22 +01:00
|
|
|
if ((childNode.variantProperty("x").value() == pastedNode.variantProperty("x").value()) &&
|
|
|
|
|
(childNode.variantProperty("y").value() == pastedNode.variantProperty("y").value()))
|
|
|
|
|
scatter = true;
|
|
|
|
|
}
|
|
|
|
|
if (!scatter)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (offset == -2000) {
|
|
|
|
|
double x = pastedNode.variantProperty("x").value().toDouble();
|
|
|
|
|
double y = pastedNode.variantProperty("y").value().toDouble();
|
|
|
|
|
double targetWidth = 20;
|
|
|
|
|
double targetHeight = 20;
|
|
|
|
|
x = x + double(qrand()) / RAND_MAX * targetWidth - targetWidth / 2;
|
|
|
|
|
y = y + double(qrand()) / RAND_MAX * targetHeight - targetHeight / 2;
|
2013-07-25 12:34:32 +02:00
|
|
|
pastedNode.variantProperty("x").setValue(int(x));
|
|
|
|
|
pastedNode.variantProperty("y").setValue(int(y));
|
2013-01-23 12:31:22 +01:00
|
|
|
} else {
|
|
|
|
|
double x = pastedNode.variantProperty("x").value().toDouble();
|
|
|
|
|
double y = pastedNode.variantProperty("y").value().toDouble();
|
|
|
|
|
x = x + offset;
|
|
|
|
|
y = y + offset;
|
2013-07-25 12:34:32 +02:00
|
|
|
pastedNode.variantProperty("x").setValue(int(x));
|
|
|
|
|
pastedNode.variantProperty("y").setValue(int(y));
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::paste()
|
|
|
|
|
{
|
2017-12-19 14:10:12 +01:00
|
|
|
QScopedPointer<Model> pasteModel(DesignDocumentView::pasteToModel());
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
if (!pasteModel)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DesignDocumentView view;
|
|
|
|
|
pasteModel->attachView(&view);
|
|
|
|
|
ModelNode rootNode(view.rootModelNode());
|
2017-12-19 14:10:12 +01:00
|
|
|
QList<ModelNode> selectedNodes = rootNode.directSubModelNodes();
|
|
|
|
|
pasteModel->detachView(&view);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
if (rootNode.type() == "empty")
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-12 15:55:30 +02:00
|
|
|
if (rootNode.id() == "designer__Selection") { // pasting multiple objects
|
2013-07-10 18:12:07 +02:00
|
|
|
currentModel()->attachView(&view);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
ModelNode targetNode;
|
|
|
|
|
|
|
|
|
|
if (!view.selectedModelNodes().isEmpty())
|
2018-01-13 18:49:39 +01:00
|
|
|
targetNode = view.selectedModelNodes().constFirst();
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
//In case we copy and paste a selection we paste in the parent item
|
2020-03-12 15:55:30 +02:00
|
|
|
if ((view.selectedModelNodes().count() == selectedNodes.count()) && targetNode.isValid() && targetNode.hasParentProperty()) {
|
2013-01-23 12:31:22 +01:00
|
|
|
targetNode = targetNode.parentProperty().parentModelNode();
|
2020-03-12 15:55:30 +02:00
|
|
|
} else {
|
|
|
|
|
// if selection is empty and copied nodes are all 3D nodes, paste them under the active scene
|
|
|
|
|
bool all3DNodes = std::find_if(selectedNodes.begin(), selectedNodes.end(),
|
|
|
|
|
[](const ModelNode &node) { return !node.isSubclassOf("QtQuick3D.Node"); })
|
|
|
|
|
== selectedNodes.end();
|
|
|
|
|
if (all3DNodes) {
|
|
|
|
|
int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt();
|
|
|
|
|
if (activeSceneId != -1) {
|
|
|
|
|
NodeListProperty sceneNodeProperty
|
|
|
|
|
= QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId);
|
|
|
|
|
targetNode = sceneNodeProperty.parentModelNode();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
if (!targetNode.isValid())
|
|
|
|
|
targetNode = view.rootModelNode();
|
|
|
|
|
|
|
|
|
|
foreach (const ModelNode &node, selectedNodes) {
|
|
|
|
|
foreach (const ModelNode &node2, selectedNodes) {
|
|
|
|
|
if (node.isAncestorOf(node2))
|
|
|
|
|
selectedNodes.removeAll(node2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-31 16:49:04 +02:00
|
|
|
rewriterView()->executeInTransaction("DesignDocument::paste1", [this, &view, selectedNodes, targetNode](){
|
|
|
|
|
QList<ModelNode> pastedNodeList;
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
int offset = double(qrand()) / RAND_MAX * 20 - 10;
|
|
|
|
|
|
|
|
|
|
foreach (const ModelNode &node, selectedNodes) {
|
2013-03-05 12:19:19 +01:00
|
|
|
PropertyName defaultProperty(targetNode.metaInfo().defaultPropertyName());
|
2013-01-23 12:31:22 +01:00
|
|
|
ModelNode pastedNode(view.insertModel(node));
|
|
|
|
|
pastedNodeList.append(pastedNode);
|
|
|
|
|
scatterItem(pastedNode, targetNode, offset);
|
|
|
|
|
targetNode.nodeListProperty(defaultProperty).reparentHere(pastedNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
view.setSelectedModelNodes(pastedNodeList);
|
2019-05-31 16:49:04 +02:00
|
|
|
});
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2020-03-12 15:55:30 +02:00
|
|
|
} else { // pasting single object
|
|
|
|
|
rewriterView()->executeInTransaction("DesignDocument::paste1", [this, &view, selectedNodes, rootNode]() {
|
2013-07-10 18:12:07 +02:00
|
|
|
currentModel()->attachView(&view);
|
2013-01-23 12:31:22 +01:00
|
|
|
ModelNode pastedNode(view.insertModel(rootNode));
|
|
|
|
|
ModelNode targetNode;
|
|
|
|
|
|
2020-03-12 15:55:30 +02:00
|
|
|
if (!view.selectedModelNodes().isEmpty()) {
|
2018-01-13 18:49:39 +01:00
|
|
|
targetNode = view.selectedModelNodes().constFirst();
|
2020-03-12 15:55:30 +02:00
|
|
|
} else {
|
|
|
|
|
// if selection is empty and this is a 3D Node, paste it under the active scene
|
|
|
|
|
if (pastedNode.isSubclassOf("QtQuick3D.Node")) {
|
|
|
|
|
int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt();
|
|
|
|
|
if (activeSceneId != -1) {
|
|
|
|
|
NodeListProperty sceneNodeProperty
|
|
|
|
|
= QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId);
|
|
|
|
|
targetNode = sceneNodeProperty.parentModelNode();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
if (!targetNode.isValid())
|
|
|
|
|
targetNode = view.rootModelNode();
|
|
|
|
|
|
2013-07-24 15:58:51 +02:00
|
|
|
if (targetNode.hasParentProperty() &&
|
2020-03-12 15:55:30 +02:00
|
|
|
pastedNode.simplifiedTypeName() == targetNode.simplifiedTypeName() &&
|
|
|
|
|
pastedNode.variantProperty("width").value() == targetNode.variantProperty("width").value() &&
|
|
|
|
|
pastedNode.variantProperty("height").value() == targetNode.variantProperty("height").value()) {
|
2013-01-23 12:31:22 +01:00
|
|
|
targetNode = targetNode.parentProperty().parentModelNode();
|
2020-03-12 15:55:30 +02:00
|
|
|
}
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2013-03-05 12:19:19 +01:00
|
|
|
PropertyName defaultProperty(targetNode.metaInfo().defaultPropertyName());
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
scatterItem(pastedNode, targetNode);
|
2014-03-05 15:57:40 +01:00
|
|
|
if (targetNode.metaInfo().propertyIsListProperty(defaultProperty)) {
|
2013-01-23 12:31:22 +01:00
|
|
|
targetNode.nodeListProperty(defaultProperty).reparentHere(pastedNode);
|
2014-03-05 15:57:40 +01:00
|
|
|
} else {
|
|
|
|
|
qWarning() << "Cannot reparent to" << targetNode;
|
|
|
|
|
}
|
2017-04-26 14:32:02 +02:00
|
|
|
view.setSelectedModelNodes({pastedNode});
|
2019-05-31 16:49:04 +02:00
|
|
|
});
|
|
|
|
|
NodeMetaInfo::clearCache();
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::selectAll()
|
|
|
|
|
{
|
2013-07-10 18:12:07 +02:00
|
|
|
if (!currentModel())
|
2013-01-23 12:31:22 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DesignDocumentView view;
|
2013-07-10 18:12:07 +02:00
|
|
|
currentModel()->attachView(&view);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
QList<ModelNode> allNodesExceptRootNode(view.allModelNodes());
|
|
|
|
|
allNodesExceptRootNode.removeOne(view.rootModelNode());
|
|
|
|
|
view.setSelectedModelNodes(allNodesExceptRootNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RewriterView *DesignDocument::rewriterView() const
|
|
|
|
|
{
|
|
|
|
|
return m_rewriterView.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::setEditor(Core::IEditor *editor)
|
|
|
|
|
{
|
|
|
|
|
m_textEditor = editor;
|
2017-10-17 17:43:53 +02:00
|
|
|
// if the user closed the file explicit we do not want to do anything with it anymore
|
2018-04-12 17:04:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave,
|
|
|
|
|
this, [this](Core::IDocument *document) {
|
|
|
|
|
if (m_textEditor && m_textEditor->document() == document) {
|
|
|
|
|
if (m_documentModel && m_documentModel->rewriterView())
|
|
|
|
|
m_documentModel->rewriterView()->writeAuxiliaryData();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2017-10-17 17:43:53 +02:00
|
|
|
connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
|
|
|
|
|
this, [this](Core::IEditor *editor) {
|
|
|
|
|
if (m_textEditor.data() == editor)
|
|
|
|
|
m_textEditor.clear();
|
|
|
|
|
});
|
|
|
|
|
|
2014-12-21 21:54:30 +02:00
|
|
|
connect(editor->document(), &Core::IDocument::filePathChanged,
|
|
|
|
|
this, &DesignDocument::updateFileName);
|
2013-02-20 13:59:31 +01:00
|
|
|
|
2019-10-30 08:25:31 +01:00
|
|
|
updateActiveTarget();
|
|
|
|
|
updateActiveTarget();
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Core::IEditor *DesignDocument::editor() const
|
|
|
|
|
{
|
|
|
|
|
return m_textEditor.data();
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-23 19:10:38 +02:00
|
|
|
TextEditor::BaseTextEditor *DesignDocument::textEditor() const
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2014-07-23 19:10:38 +02:00
|
|
|
return qobject_cast<TextEditor::BaseTextEditor*>(editor());
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPlainTextEdit *DesignDocument::plainTextEdit() const
|
|
|
|
|
{
|
|
|
|
|
if (editor())
|
|
|
|
|
return qobject_cast<QPlainTextEdit*>(editor()->widget());
|
|
|
|
|
|
2018-07-24 23:56:45 +02:00
|
|
|
return nullptr;
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ModelNode DesignDocument::rootModelNode() const
|
|
|
|
|
{
|
|
|
|
|
return rewriterView()->rootModelNode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::undo()
|
|
|
|
|
{
|
|
|
|
|
if (rewriterView() && !rewriterView()->modificationGroupActive())
|
|
|
|
|
plainTextEdit()->undo();
|
|
|
|
|
|
|
|
|
|
viewManager().resetPropertyEditorView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DesignDocument::redo()
|
|
|
|
|
{
|
|
|
|
|
if (rewriterView() && !rewriterView()->modificationGroupActive())
|
|
|
|
|
plainTextEdit()->redo();
|
|
|
|
|
|
|
|
|
|
viewManager().resetPropertyEditorView();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-30 08:25:31 +01:00
|
|
|
static Target *getActiveTarget(DesignDocument *designDocument)
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2019-10-30 08:25:31 +01:00
|
|
|
Project *currentProject = SessionManager::projectForFile(designDocument->fileName());
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2014-03-05 15:17:43 +01:00
|
|
|
if (!currentProject)
|
2019-10-30 08:25:31 +01:00
|
|
|
currentProject = ProjectTree::currentProject();
|
2014-03-05 15:17:43 +01:00
|
|
|
|
2013-01-23 12:31:22 +01:00
|
|
|
if (!currentProject)
|
2018-07-24 23:56:45 +02:00
|
|
|
return nullptr;
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2013-02-20 13:59:31 +01:00
|
|
|
|
2015-02-01 18:44:47 +02:00
|
|
|
QObject::connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged,
|
2019-10-30 08:25:31 +01:00
|
|
|
designDocument, &DesignDocument::updateActiveTarget, Qt::UniqueConnection);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2016-06-13 12:40:54 +00:00
|
|
|
QObject::connect(currentProject, &Project::activeTargetChanged,
|
2019-10-30 08:25:31 +01:00
|
|
|
designDocument, &DesignDocument::updateActiveTarget, Qt::UniqueConnection);
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2015-02-01 18:44:47 +02:00
|
|
|
Target *target = currentProject->activeTarget();
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2019-10-30 08:25:31 +01:00
|
|
|
if (!target || !target->kit()->isValid())
|
2018-07-24 23:56:45 +02:00
|
|
|
return nullptr;
|
2013-01-23 12:31:22 +01:00
|
|
|
|
2015-02-01 18:44:47 +02:00
|
|
|
QObject::connect(target, &Target::kitChanged,
|
2019-10-30 08:25:31 +01:00
|
|
|
designDocument, &DesignDocument::updateActiveTarget, Qt::UniqueConnection);
|
2014-03-26 19:41:51 +01:00
|
|
|
|
2019-10-30 08:25:31 +01:00
|
|
|
return target;
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-30 08:25:31 +01:00
|
|
|
void DesignDocument::updateActiveTarget()
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2019-10-30 08:25:31 +01:00
|
|
|
m_currentTarget = getActiveTarget(this);
|
|
|
|
|
viewManager().setNodeInstanceViewTarget(m_currentTarget);
|
2016-09-27 12:27:51 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-28 11:58:53 +01:00
|
|
|
void DesignDocument::contextHelp(const Core::IContext::HelpCallback &callback) const
|
2013-01-23 12:31:22 +01:00
|
|
|
{
|
2014-05-13 17:53:04 +02:00
|
|
|
if (view())
|
2019-01-28 11:58:53 +01:00
|
|
|
view()->contextHelp(callback);
|
2018-01-17 16:06:13 +01:00
|
|
|
else
|
2019-01-28 13:00:03 +01:00
|
|
|
callback({});
|
2013-01-23 12:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace QmlDesigner
|