1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "propertyeditor.h"
36 #include <nodemetainfo.h>
39 #include <invalididexception.h>
40 #include <rewritingexception.h>
41 #include <invalidnodestateexception.h>
42 #include <variantproperty.h>
44 #include <bindingproperty.h>
46 #include <nodeabstractproperty.h>
47 #include <rewriterview.h>
49 #include "propertyeditorvalue.h"
50 #include "basiclayouts.h"
51 #include "basicwidgets.h"
52 #include "resetwidget.h"
53 #include "qlayoutobject.h"
54 #include <qmleditorwidgets/colorwidgets.h>
55 #include "gradientlineqmladaptor.h"
56 #include "behaviordialog.h"
57 #include "qproxylayoutitem.h"
58 #include "fontwidget.h"
59 #include "siblingcombobox.h"
60 #include "propertyeditortransaction.h"
61 #include "originwidget.h"
63 #include <QtCore/QCoreApplication>
64 #include <QtCore/QDir>
65 #include <QtCore/QFileSystemWatcher>
66 #include <QtCore/QFileInfo>
67 #include <QtCore/QDebug>
68 #include <QtCore/QTimer>
69 #include <QtDeclarative/QDeclarativeView>
70 #include <QtDeclarative/QDeclarativeContext>
71 #include <QtGui/QVBoxLayout>
72 #include <QtGui/QShortcut>
73 #include <QtGui/QStackedWidget>
74 #include <QDeclarativeEngine>
75 #include <private/qdeclarativemetatype_p.h>
76 #include <QMessageBox>
77 #include <QApplication>
78 #include <QGraphicsOpacityEffect>
85 const int collapseButtonOffset = 114;
87 namespace QmlDesigner {
89 PropertyEditor::NodeType::NodeType(PropertyEditor *propertyEditor) :
90 m_view(new DeclarativeWidgetView), m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor)), m_dummyPropertyEditorValue(new PropertyEditorValue()),
91 m_contextObject(new PropertyEditorContextObject())
93 Q_ASSERT(QFileInfo(":/images/button_normal.png").exists());
95 QDeclarativeContext *ctxt = m_view->rootContext();
96 m_view->engine()->setOutputWarningsToStandardError(debug);
97 m_dummyPropertyEditorValue->setValue("#000000");
98 ctxt->setContextProperty("dummyBackendValue", m_dummyPropertyEditorValue.data());
99 m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
100 ctxt->setContextObject(m_contextObject.data());
102 connect(&m_backendValuesPropertyMap, SIGNAL(valueChanged(const QString&, const QVariant&)), propertyEditor, SLOT(changeValue(const QString&)));
105 PropertyEditor::NodeType::~NodeType()
109 void setupPropertyEditorValue(const QString &name, QDeclarativePropertyMap *propertyMap, PropertyEditor *propertyEditor, const QString &type)
111 QString propertyName(name);
112 propertyName.replace(QLatin1Char('.'), QLatin1Char('_'));
113 PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(propertyMap->value(propertyName)));
115 valueObject = new PropertyEditorValue(propertyMap);
116 QObject::connect(valueObject, SIGNAL(valueChanged(QString, const QVariant&)), propertyMap, SIGNAL(valueChanged(QString, const QVariant&)));
117 QObject::connect(valueObject, SIGNAL(expressionChanged(QString)), propertyEditor, SLOT(changeExpression(QString)));
118 propertyMap->insert(propertyName, QVariant::fromValue(valueObject));
120 valueObject->setName(propertyName);
121 if (type == "QColor")
122 valueObject->setValue(QVariant("#000000"));
124 valueObject->setValue(QVariant(1));
128 void createPropertyEditorValue(const QmlObjectNode &fxObjectNode, const QString &name, const QVariant &value, QDeclarativePropertyMap *propertyMap, PropertyEditor *propertyEditor)
130 QString propertyName(name);
131 propertyName.replace(QLatin1Char('.'), QLatin1Char('_'));
132 PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(propertyMap->value(propertyName)));
134 valueObject = new PropertyEditorValue(propertyMap);
135 QObject::connect(valueObject, SIGNAL(valueChanged(QString, const QVariant&)), propertyMap, SIGNAL(valueChanged(QString, const QVariant&)));
136 QObject::connect(valueObject, SIGNAL(expressionChanged(QString)), propertyEditor, SLOT(changeExpression(QString)));
137 propertyMap->insert(propertyName, QVariant::fromValue(valueObject));
139 valueObject->setName(name);
140 valueObject->setModelNode(fxObjectNode);
142 if (fxObjectNode.propertyAffectedByCurrentState(name) && !(fxObjectNode.modelNode().property(name).isBindingProperty())) {
143 valueObject->setValue(fxObjectNode.modelValue(name));
146 valueObject->setValue(value);
149 if (propertyName != QLatin1String("id") &&
150 fxObjectNode.currentState().isBaseState() &&
151 fxObjectNode.modelNode().property(propertyName).isBindingProperty()) {
152 valueObject->setExpression(fxObjectNode.modelNode().bindingProperty(propertyName).expression());
154 valueObject->setExpression(fxObjectNode.instanceValue(name).toString());
158 void PropertyEditor::NodeType::setValue(const QmlObjectNode & fxObjectNode, const QString &name, const QVariant &value)
160 QString propertyName = name;
161 propertyName.replace(QLatin1Char('.'), QLatin1Char('_'));
162 PropertyEditorValue *propertyValue = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value(propertyName)));
164 propertyValue->setValue(value);
165 if (!fxObjectNode.hasBindingProperty(name))
166 propertyValue->setExpression(value.toString());
168 propertyValue->setExpression(fxObjectNode.expression(name));
172 void PropertyEditor::NodeType::setup(const QmlObjectNode &fxObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditor *propertyEditor)
174 if (!fxObjectNode.isValid()) {
178 QDeclarativeContext *ctxt = m_view->rootContext();
180 if (fxObjectNode.isValid()) {
181 foreach (const QString &propertyName, fxObjectNode.modelNode().metaInfo().propertyNames())
182 createPropertyEditorValue(fxObjectNode, propertyName, fxObjectNode.instanceValue(propertyName), &m_backendValuesPropertyMap, propertyEditor);
185 PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("className")));
187 valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap);
188 valueObject->setName("className");
189 valueObject->setModelNode(fxObjectNode.modelNode());
190 valueObject->setValue(fxObjectNode.modelNode().simplifiedTypeName());
191 QObject::connect(valueObject, SIGNAL(valueChanged(QString, const QVariant&)), &m_backendValuesPropertyMap, SIGNAL(valueChanged(QString, const QVariant&)));
192 m_backendValuesPropertyMap.insert("className", QVariant::fromValue(valueObject));
195 valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("id")));
197 valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap);
198 valueObject->setName("id");
199 valueObject->setValue(fxObjectNode.id());
200 QObject::connect(valueObject, SIGNAL(valueChanged(QString, const QVariant&)), &m_backendValuesPropertyMap, SIGNAL(valueChanged(QString, const QVariant&)));
201 m_backendValuesPropertyMap.insert("id", QVariant::fromValue(valueObject));
204 m_backendAnchorBinding.setup(QmlItemNode(fxObjectNode.modelNode()));
206 ctxt->setContextProperty("anchorBackend", &m_backendAnchorBinding);
208 ctxt->setContextProperty("transaction", m_propertyEditorTransaction.data());
210 m_contextObject->setSpecificsUrl(qmlSpecificsFile);
212 m_contextObject->setStateName(stateName);
213 QApplication::processEvents();
214 if (!fxObjectNode.isValid())
216 ctxt->setContextProperty("propertyCount", QVariant(fxObjectNode.modelNode().properties().count()));
218 m_contextObject->setIsBaseState(fxObjectNode.isInBaseState());
219 m_contextObject->setSelectionChanged(false);
221 m_contextObject->setSelectionChanged(false);
223 qWarning() << "PropertyEditor: invalid node for setup";
227 void PropertyEditor::NodeType::initialSetup(const QString &typeName, const QUrl &qmlSpecificsFile, PropertyEditor *propertyEditor)
229 QDeclarativeContext *ctxt = m_view->rootContext();
231 NodeMetaInfo metaInfo = propertyEditor->model()->metaInfo(typeName, 4, 7);
233 foreach (const QString &propertyName, metaInfo.propertyNames())
234 setupPropertyEditorValue(propertyName, &m_backendValuesPropertyMap, propertyEditor, metaInfo.propertyTypeName(propertyName));
236 PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("className")));
238 valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap);
239 valueObject->setName("className");
241 valueObject->setValue(typeName);
242 QObject::connect(valueObject, SIGNAL(valueChanged(QString, const QVariant&)), &m_backendValuesPropertyMap, SIGNAL(valueChanged(QString, const QVariant&)));
243 m_backendValuesPropertyMap.insert("className", QVariant::fromValue(valueObject));
246 valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("id")));
248 valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap);
249 valueObject->setName("id");
250 valueObject->setValue("id");
251 QObject::connect(valueObject, SIGNAL(valueChanged(QString, const QVariant&)), &m_backendValuesPropertyMap, SIGNAL(valueChanged(QString, const QVariant&)));
252 m_backendValuesPropertyMap.insert("id", QVariant::fromValue(valueObject));
254 ctxt->setContextProperty("anchorBackend", &m_backendAnchorBinding);
255 ctxt->setContextProperty("transaction", m_propertyEditorTransaction.data());
257 m_contextObject->setSpecificsUrl(qmlSpecificsFile);
259 m_contextObject->setStateName(QLatin1String("basestate"));
261 m_contextObject->setIsBaseState(true);
263 m_contextObject->setSpecificQmlData(QLatin1String(""));
265 m_contextObject->setGlobalBaseUrl(QUrl());
268 PropertyEditor::PropertyEditor(QWidget *parent) :
269 QmlModelView(parent),
273 m_stackedWidget(new StackedWidget(parent)),
276 m_setupCompleted(false),
277 m_singleShotTimer(new QTimer(this))
279 m_updateShortcut = new QShortcut(QKeySequence("F5"), m_stackedWidget);
280 connect(m_updateShortcut, SIGNAL(activated()), this, SLOT(reloadQml()));
282 QFile file(":/qmldesigner/stylesheet.css");
283 file.open(QFile::ReadOnly);
284 QString styleSheet = QLatin1String(file.readAll());
285 m_stackedWidget->setStyleSheet(styleSheet);
286 m_stackedWidget->setMinimumWidth(300);
287 m_stackedWidget->move(0, 0);
288 connect(m_stackedWidget, SIGNAL(resized()), this, SLOT(updateSize()));
290 m_stackedWidget->insertWidget(0, new QWidget(m_stackedWidget));
293 static bool declarativeTypesRegistered = false;
294 if (!declarativeTypesRegistered) {
295 declarativeTypesRegistered = true;
296 BasicWidgets::registerDeclarativeTypes();
297 BasicLayouts::registerDeclarativeTypes();
298 ResetWidget::registerDeclarativeType();
299 QLayoutObject::registerDeclarativeType();
300 QmlEditorWidgets::ColorWidgets::registerDeclarativeTypes();
301 BehaviorDialog::registerDeclarativeType();
302 QProxyLayoutItem::registerDeclarativeTypes();
303 PropertyEditorValue::registerDeclarativeTypes();
304 FontWidget::registerDeclarativeTypes();
305 SiblingComboBox::registerDeclarativeTypes();
306 OriginWidget::registerDeclarativeType();
307 GradientLineQmlAdaptor::registerDeclarativeType();
311 PropertyEditor::~PropertyEditor()
313 delete m_stackedWidget;
314 qDeleteAll(m_typeHash);
317 static inline QString fixTypeNameForPanes(const QString &typeName)
319 QString fixedTypeName = typeName;
320 fixedTypeName.replace('.', '/');
321 fixedTypeName.replace("QtQuick/", "Qt/");
322 return fixedTypeName;
325 void PropertyEditor::setupPane(const QString &typeName)
328 QUrl qmlFile = fileToUrl(locateQmlFile(QLatin1String("Qt/ItemPane.qml")));
329 QUrl qmlSpecificsFile;
331 qmlSpecificsFile = fileToUrl(locateQmlFile(fixTypeNameForPanes(typeName) + "Specifics.qml"));
332 NodeType *type = m_typeHash.value(qmlFile.toString());
335 type = new NodeType(this);
337 QDeclarativeContext *ctxt = type->m_view->rootContext();
338 ctxt->setContextProperty("finishedNotify", QVariant(false) );
339 type->initialSetup(typeName, qmlSpecificsFile, this);
340 type->m_view->setSource(qmlFile);
341 ctxt->setContextProperty("finishedNotify", QVariant(true) );
343 m_stackedWidget->addWidget(type->m_view);
344 m_typeHash.insert(qmlFile.toString(), type);
346 QDeclarativeContext *ctxt = type->m_view->rootContext();
347 ctxt->setContextProperty("finishedNotify", QVariant(false) );
349 type->initialSetup(typeName, qmlSpecificsFile, this);
350 ctxt->setContextProperty("finishedNotify", QVariant(true) );
354 void PropertyEditor::changeValue(const QString &propertyName)
356 if (propertyName.isNull())
362 if (propertyName == "type")
365 if (!m_selectedNode.isValid())
368 if (propertyName == "id") {
369 PropertyEditorValue *value = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_currentType->m_backendValuesPropertyMap.value(propertyName)));
370 const QString newId = value->value().toString();
372 if (newId == m_selectedNode.id())
375 if (m_selectedNode.isValidId(newId) && !modelNodeForId(newId).isValid() ) {
376 if (m_selectedNode.id().isEmpty() || newId.isEmpty()) { //no id
378 m_selectedNode.setId(newId);
379 } catch (InvalidIdException &e) { //better save then sorry
381 value->setValue(m_selectedNode.id());
383 QMessageBox::warning(0, tr("Invalid Id"), e.description());
385 } else { //there is already an id, so we refactor
387 rewriterView()->renameId(m_selectedNode.id(), newId);
391 value->setValue(m_selectedNode.id());
393 if (!m_selectedNode.isValidId(newId))
394 QMessageBox::warning(0, tr("Invalid Id"), tr("%1 is an invalid id").arg(newId));
396 QMessageBox::warning(0, tr("Invalid Id"), tr("%1 already exists").arg(newId));
401 //.replace(QLatin1Char('.'), QLatin1Char('_'))
402 QString underscoreName(propertyName);
403 underscoreName.replace(QLatin1Char('.'), QLatin1Char('_'));
404 PropertyEditorValue *value = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_currentType->m_backendValuesPropertyMap.value(underscoreName)));
410 QmlObjectNode fxObjectNode(m_selectedNode);
412 QVariant castedValue;
414 if (fxObjectNode.modelNode().metaInfo().isValid() && fxObjectNode.modelNode().metaInfo().hasProperty(propertyName)) {
415 castedValue = fxObjectNode.modelNode().metaInfo().propertyCastedValue(propertyName, value->value());
417 qWarning() << "PropertyEditor:" <<propertyName << "cannot be casted (metainfo)";
421 if (value->value().isValid() && !castedValue.isValid()) {
422 qWarning() << "PropertyEditor:" << propertyName << "not properly casted (metainfo)";
426 if (fxObjectNode.modelNode().metaInfo().isValid() && fxObjectNode.modelNode().metaInfo().hasProperty(propertyName))
427 if (fxObjectNode.modelNode().metaInfo().propertyTypeName(propertyName) == QLatin1String("QUrl")) { //turn absolute local file paths into relative paths
428 QString filePath = castedValue.toUrl().toString();
429 if (QFileInfo(filePath).exists() && QFileInfo(filePath).isAbsolute()) {
430 QDir fileDir(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath());
431 castedValue = QUrl(fileDir.relativeFilePath(filePath));
435 if (castedValue.type() == QVariant::Color) {
436 QColor color = castedValue.value<QColor>();
437 QColor newColor = QColor(color.name());
438 newColor.setAlpha(color.alpha());
439 castedValue = QVariant(newColor);
443 if (!value->value().isValid()) { //reset
444 fxObjectNode.removeVariantProperty(propertyName);
446 if (castedValue.isValid() && !castedValue.isNull()) {
448 fxObjectNode.setVariantProperty(propertyName, castedValue);
453 catch (RewritingException &e) {
454 QMessageBox::warning(0, "Error", e.description());
458 void PropertyEditor::changeExpression(const QString &name)
466 RewriterTransaction transaction = beginRewriterTransaction();
469 QString underscoreName(name);
470 underscoreName.replace(QLatin1Char('.'), QLatin1Char('_'));
472 QmlObjectNode fxObjectNode(m_selectedNode);
473 PropertyEditorValue *value = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_currentType->m_backendValuesPropertyMap.value(underscoreName)));
475 if (fxObjectNode.modelNode().metaInfo().isValid() && fxObjectNode.modelNode().metaInfo().hasProperty(name)) {
476 if (fxObjectNode.modelNode().metaInfo().propertyTypeName(name) == QLatin1String("QColor")) {
477 if (QColor(value->expression().remove('"')).isValid()) {
478 fxObjectNode.setVariantProperty(name, QColor(value->expression().remove('"')));
479 transaction.commit(); //committing in the try block
482 } else if (fxObjectNode.modelNode().metaInfo().propertyTypeName(name) == QLatin1String("bool")) {
483 if (value->expression().compare("false", Qt::CaseInsensitive) == 0 || value->expression().compare("true", Qt::CaseInsensitive) == 0) {
484 if (value->expression().compare("true", Qt::CaseInsensitive) == 0)
485 fxObjectNode.setVariantProperty(name, true);
487 fxObjectNode.setVariantProperty(name, false);
488 transaction.commit(); //committing in the try block
491 } else if (fxObjectNode.modelNode().metaInfo().propertyTypeName(name) == QLatin1String("int")) {
493 int intValue = value->expression().toInt(&ok);
495 fxObjectNode.setVariantProperty(name, intValue);
496 transaction.commit(); //committing in the try block
499 } else if (fxObjectNode.modelNode().metaInfo().propertyTypeName(name) == QLatin1String("qreal")) {
501 qreal realValue = value->expression().toFloat(&ok);
503 fxObjectNode.setVariantProperty(name, realValue);
504 transaction.commit(); //committing in the try block
511 qWarning() << "PropertyEditor::changeExpression no value for " << underscoreName;
515 if (fxObjectNode.expression(name) != value->expression() || !fxObjectNode.propertyAffectedByCurrentState(name))
516 fxObjectNode.setBindingProperty(name, value->expression());
518 transaction.commit(); //committing in the try block
521 catch (RewritingException &e) {
522 QMessageBox::warning(0, "Error", e.description());
526 void PropertyEditor::updateSize()
530 QWidget* frame = m_currentType->m_view->findChild<QWidget*>("propertyEditorFrame");
532 frame->resize(m_stackedWidget->size());
535 void PropertyEditor::setupPanes()
537 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
538 setupPane("QtQuick.Rectangle");
539 setupPane("QtQuick.Text");
541 m_setupCompleted = true;
542 QApplication::restoreOverrideCursor();
545 void PropertyEditor::otherPropertyChanged(const QmlObjectNode &fxObjectNode, const QString &propertyName)
547 QmlModelView::otherPropertyChanged(fxObjectNode, propertyName);
549 if (!m_selectedNode.isValid())
554 if (fxObjectNode.isValid() && m_currentType && fxObjectNode == m_selectedNode && fxObjectNode.currentState().isValid()) {
555 AbstractProperty property = fxObjectNode.modelNode().property(propertyName);
556 if (fxObjectNode == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == fxObjectNode) {
557 if ( !m_selectedNode.hasProperty(propertyName) || m_selectedNode.property(property.name()).isBindingProperty() )
558 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
560 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
567 void PropertyEditor::transformChanged(const QmlObjectNode &fxObjectNode, const QString &propertyName)
569 QmlModelView::transformChanged(fxObjectNode, propertyName);
571 if (!m_selectedNode.isValid())
574 if (fxObjectNode.isValid() && m_currentType && fxObjectNode == m_selectedNode && fxObjectNode.currentState().isValid()) {
575 AbstractProperty property = fxObjectNode.modelNode().property(propertyName);
576 if (fxObjectNode == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == fxObjectNode) {
577 if ( m_selectedNode.property(property.name()).isBindingProperty() || !m_selectedNode.hasProperty(propertyName))
578 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
580 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
585 void PropertyEditor::setQmlDir(const QString &qmlDir)
589 QFileSystemWatcher *watcher = new QFileSystemWatcher(this);
590 watcher->addPath(m_qmlDir);
591 connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(reloadQml()));
594 void PropertyEditor::delayedResetView()
597 m_timerId = startTimer(200);
600 void PropertyEditor::timerEvent(QTimerEvent *timerEvent)
602 if (m_timerId == timerEvent->timerId()) {
607 QString templateGeneration(NodeMetaInfo type, NodeMetaInfo superType, const QmlObjectNode &objectNode)
609 QString qmlTemplate = QLatin1String("import Qt 4.7\nimport Bauhaus 1.0\n");
610 qmlTemplate += QLatin1String("GroupBox {\n");
611 qmlTemplate += QString(QLatin1String("caption: \"%1\"\n")).arg(type.typeName());
612 qmlTemplate += QLatin1String("layout: VerticalLayout {\n");
614 QList<QString> orderedList;
615 orderedList = type.propertyNames();
618 foreach (const QString &name, orderedList) {
620 if (name.startsWith(QLatin1String("__")))
621 continue; //private API
622 QString properName = name;
624 properName.replace('.', '_');
626 QString typeName = type.propertyTypeName(name);
627 //alias resolution only possible with instance
628 if (typeName == QLatin1String("alias") && objectNode.isValid())
629 typeName = objectNode.instanceType(name);
631 if (!superType.hasProperty(name) && type.propertyIsWritable(name)) {
632 if (typeName == "int") {
633 qmlTemplate += QString(QLatin1String(
634 "IntEditor { backendValue: backendValues.%2\n caption: \"%1\"\nbaseStateFlag: isBaseState\nslider: false\n}"
635 )).arg(name).arg(properName);
637 if (typeName == "real" || typeName == "double" || typeName == "qreal") {
638 qmlTemplate += QString(QLatin1String(
639 "DoubleSpinBoxAlternate {\ntext: \"%1\"\nbackendValue: backendValues.%2\nbaseStateFlag: isBaseState\n}\n"
640 )).arg(name).arg(properName);
642 if (typeName == "string" || typeName == "QString" || typeName == "QUrl" || typeName == "url") {
643 qmlTemplate += QString(QLatin1String(
644 "QWidget {\nlayout: HorizontalLayout {\nLabel {\ntext: \"%1\"\ntoolTip: \"%1\"\n}\nLineEdit {\nbackendValue: backendValues.%2\nbaseStateFlag: isBaseState\n}\n}\n}\n"
645 )).arg(name).arg(properName);
647 if (typeName == "bool") {
648 qmlTemplate += QString(QLatin1String(
649 "QWidget {\nlayout: HorizontalLayout {\nLabel {\ntext: \"%1\"\ntoolTip: \"%1\"\n}\nCheckBox {text: backendValues.%2.value\nbackendValue: backendValues.%2\nbaseStateFlag: isBaseState\ncheckable: true\n}\n}\n}\n"
650 )).arg(name).arg(properName);
652 if (typeName == "color" || typeName == "QColor") {
653 qmlTemplate += QString(QLatin1String(
654 "ColorGroupBox {\ncaption: \"%1\"\nfinished: finishedNotify\nbackendColor: backendValues.%2\n}\n\n"
655 )).arg(name).arg(properName);
659 qmlTemplate += QLatin1String("}\n"); //VerticalLayout
660 qmlTemplate += QLatin1String("}\n"); //GroupBox
665 void PropertyEditor::resetView()
673 qDebug() << "________________ RELOADING PROPERTY EDITOR QML _______________________";
676 killTimer(m_timerId);
678 if (m_selectedNode.isValid() && model() != m_selectedNode.model())
679 m_selectedNode = ModelNode();
681 QString specificsClassName;
682 QUrl qmlFile(qmlForNode(m_selectedNode, specificsClassName));
683 QUrl qmlSpecificsFile;
684 if (m_selectedNode.isValid())
685 qmlSpecificsFile = fileToUrl(locateQmlFile(fixTypeNameForPanes(m_selectedNode.type()) + "Specifics.qml"));
687 QString specificQmlData;
689 if (m_selectedNode.isValid() && !QFileInfo(qmlSpecificsFile.toLocalFile()).exists() && m_selectedNode.metaInfo().isValid()) {
691 specificQmlData = templateGeneration(m_selectedNode.metaInfo(), model()->metaInfo(specificsClassName), m_selectedNode);
694 NodeType *type = m_typeHash.value(qmlFile.toString());
697 type = new NodeType(this);
699 m_stackedWidget->addWidget(type->m_view);
700 m_typeHash.insert(qmlFile.toString(), type);
702 QmlObjectNode fxObjectNode;
703 if (m_selectedNode.isValid()) {
704 fxObjectNode = QmlObjectNode(m_selectedNode);
705 Q_ASSERT(fxObjectNode.isValid());
707 QDeclarativeContext *ctxt = type->m_view->rootContext();
708 type->setup(fxObjectNode, currentState().name(), qmlSpecificsFile, this);
709 ctxt->setContextProperty("finishedNotify", QVariant(false));
710 if (specificQmlData.isEmpty())
711 type->m_contextObject->setSpecificQmlData(specificQmlData);
713 type->m_contextObject->setGlobalBaseUrl(qmlFile);
714 type->m_contextObject->setSpecificQmlData(specificQmlData);
715 type->m_view->setSource(qmlFile);
716 ctxt->setContextProperty("finishedNotify", QVariant(true));
718 QmlObjectNode fxObjectNode;
719 if (m_selectedNode.isValid()) {
720 fxObjectNode = QmlObjectNode(m_selectedNode);
722 QDeclarativeContext *ctxt = type->m_view->rootContext();
724 ctxt->setContextProperty("finishedNotify", QVariant(false));
725 if (specificQmlData.isEmpty())
726 type->m_contextObject->setSpecificQmlData(specificQmlData);
727 QString currentStateName = currentState().isValid() ? currentState().name() : QLatin1String("invalid state");
728 type->setup(fxObjectNode, currentStateName, qmlSpecificsFile, this);
729 type->m_contextObject->setGlobalBaseUrl(qmlFile);
730 type->m_contextObject->setSpecificQmlData(specificQmlData);
733 m_stackedWidget->setCurrentWidget(type->m_view);
736 QDeclarativeContext *ctxt = type->m_view->rootContext();
737 ctxt->setContextProperty("finishedNotify", QVariant(true));
738 /*ctxt->setContextProperty("selectionChanged", QVariant(false));
739 ctxt->setContextProperty("selectionChanged", QVariant(true));
740 ctxt->setContextProperty("selectionChanged", QVariant(false));*/
741 type->m_contextObject->triggerSelectionChanged();
743 m_currentType = type;
753 void PropertyEditor::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
754 const QList<ModelNode> &lastSelectedNodeList)
756 Q_UNUSED(lastSelectedNodeList);
758 if (selectedNodeList.isEmpty() || selectedNodeList.count() > 1)
760 else if (m_selectedNode != selectedNodeList.first())
761 select(selectedNodeList.first());
764 void PropertyEditor::nodeAboutToBeRemoved(const ModelNode &removedNode)
766 QmlModelView::nodeAboutToBeRemoved(removedNode);
767 if (m_selectedNode.isValid() && removedNode.isValid() && m_selectedNode == removedNode)
768 select(m_selectedNode.parentProperty().parentModelNode());
771 void PropertyEditor::modelAttached(Model *model)
773 QmlModelView::modelAttached(model);
776 qDebug() << Q_FUNC_INFO;
781 if (!m_setupCompleted) {
782 m_singleShotTimer->setSingleShot(true);
783 m_singleShotTimer->setInterval(1000);
784 connect(m_singleShotTimer, SIGNAL(timeout()), this, SLOT(setupPanes()));
785 m_singleShotTimer->start();
791 void PropertyEditor::modelAboutToBeDetached(Model *model)
793 QmlModelView::modelAboutToBeDetached(model);
794 m_currentType->m_propertyEditorTransaction->end();
800 void PropertyEditor::propertiesRemoved(const QList<AbstractProperty>& propertyList)
802 QmlModelView::propertiesRemoved(propertyList);
804 if (!m_selectedNode.isValid())
807 foreach (const AbstractProperty &property, propertyList) {
808 ModelNode node(property.parentModelNode());
809 if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) {
810 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
811 if (property.name().contains("anchor", Qt::CaseInsensitive))
812 m_currentType->m_backendAnchorBinding.invalidate(m_selectedNode);
818 void PropertyEditor::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange)
821 QmlModelView::variantPropertiesChanged(propertyList, propertyChange);
823 if (!m_selectedNode.isValid())
826 foreach (const VariantProperty &property, propertyList) {
827 ModelNode node(property.parentModelNode());
829 if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) {
830 if ( QmlObjectNode(m_selectedNode).modelNode().property(property.name()).isBindingProperty())
831 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
833 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
838 void PropertyEditor::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange)
840 QmlModelView::bindingPropertiesChanged(propertyList, propertyChange);
842 if (!m_selectedNode.isValid())
845 foreach (const BindingProperty &property, propertyList) {
846 ModelNode node(property.parentModelNode());
848 if (node == m_selectedNode || QmlObjectNode(m_selectedNode).propertyChangeForCurrentState() == node) {
849 if (property.name().contains("anchor", Qt::CaseInsensitive))
850 m_currentType->m_backendAnchorBinding.invalidate(m_selectedNode);
851 if ( QmlObjectNode(m_selectedNode).modelNode().property(property.name()).isBindingProperty())
852 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).instanceValue(property.name()));
854 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
860 void PropertyEditor::instanceInformationsChange(const QVector<ModelNode> &nodeList)
862 if (!m_selectedNode.isValid())
866 if (nodeList.contains(m_selectedNode))
867 m_currentType->m_backendAnchorBinding.setup(QmlItemNode(m_selectedNode));
871 void PropertyEditor::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId)
873 QmlModelView::nodeIdChanged(node, newId, oldId);
875 if (!m_selectedNode.isValid())
878 if (node == m_selectedNode) {
881 setValue(node, "id", newId);
886 void PropertyEditor::scriptFunctionsChanged(const ModelNode &node, const QStringList &scriptFunctionList)
888 QmlModelView::scriptFunctionsChanged(node, scriptFunctionList);
891 void PropertyEditor::select(const ModelNode &node)
893 if (QmlItemNode(node).isValid())
894 m_selectedNode = node;
896 m_selectedNode = ModelNode();
901 QWidget *PropertyEditor::createPropertiesPage()
904 return m_stackedWidget;
907 void PropertyEditor::stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState)
909 QmlModelView::stateChanged(newQmlModelState, oldQmlModelState);
910 Q_ASSERT(newQmlModelState.isValid());
912 qDebug() << Q_FUNC_INFO << newQmlModelState.name();
916 void PropertyEditor::setValue(const QmlObjectNode &fxObjectNode, const QString &name, const QVariant &value)
919 m_currentType->setValue(fxObjectNode, name, value);
923 void PropertyEditor::reloadQml()
926 while (QWidget *widget = m_stackedWidget->widget(0)) {
927 m_stackedWidget->removeWidget(widget);
935 QString PropertyEditor::qmlFileName(const NodeMetaInfo &nodeInfo) const
937 if (nodeInfo.typeName().split('.').last() == "QDeclarativeItem")
938 return "Qt/ItemPane.qml";
939 const QString fixedTypeName = fixTypeNameForPanes(nodeInfo.typeName());
940 return fixedTypeName + QLatin1String("Pane.qml");
943 QUrl PropertyEditor::fileToUrl(const QString &filePath) const {
946 if (filePath.isEmpty())
949 if (filePath.startsWith(QLatin1Char(':'))) {
950 fileUrl.setScheme("qrc");
951 QString path = filePath;
952 path.remove(0, 1); // remove trailing ':'
953 fileUrl.setPath(path);
955 fileUrl = QUrl::fromLocalFile(filePath);
961 QUrl PropertyEditor::qmlForNode(const ModelNode &modelNode, QString &className) const
963 if (modelNode.isValid()) {
964 QList<NodeMetaInfo> hierarchy;
965 hierarchy << modelNode.metaInfo();
966 hierarchy << modelNode.metaInfo().superClasses();
968 foreach (const NodeMetaInfo &info, hierarchy) {
969 QUrl fileUrl = fileToUrl(locateQmlFile(qmlFileName(info)));
970 if (fileUrl.isValid()) {
971 className = info.typeName();
976 return fileToUrl(QDir(m_qmlDir).filePath("Qt/emptyPane.qml"));
979 QString PropertyEditor::locateQmlFile(const QString &relativePath) const
981 QDir fileSystemDir(m_qmlDir);
982 static QDir resourcesDir(":/propertyeditor");
984 if (fileSystemDir.exists(relativePath))
985 return fileSystemDir.absoluteFilePath(relativePath);
986 if (resourcesDir.exists(relativePath))
987 return resourcesDir.absoluteFilePath(relativePath);