OSDN Git Service

dbdd027603a93816d357b7fa52a77b159734d351
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qmldesigner / components / propertyeditor / propertyeditor.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "propertyeditor.h"
35
36 #include <nodemetainfo.h>
37 #include <metainfo.h>
38
39 #include <invalididexception.h>
40 #include <rewritingexception.h>
41 #include <invalidnodestateexception.h>
42 #include <variantproperty.h>
43
44 #include <bindingproperty.h>
45
46 #include <nodeabstractproperty.h>
47 #include <rewriterview.h>
48
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"
62
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>
79 #include <QToolBar>
80
81 enum {
82     debug = false
83 };
84
85 const int collapseButtonOffset = 114;
86
87 namespace QmlDesigner {
88
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())
92 {
93     Q_ASSERT(QFileInfo(":/images/button_normal.png").exists());
94
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());
101
102     connect(&m_backendValuesPropertyMap, SIGNAL(valueChanged(const QString&, const QVariant&)), propertyEditor, SLOT(changeValue(const QString&)));
103 }
104
105 PropertyEditor::NodeType::~NodeType()
106 {
107 }
108
109 void setupPropertyEditorValue(const QString &name, QDeclarativePropertyMap *propertyMap, PropertyEditor *propertyEditor, const QString &type)
110 {
111     QString propertyName(name);
112     propertyName.replace(QLatin1Char('.'), QLatin1Char('_'));
113     PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(propertyMap->value(propertyName)));
114     if (!valueObject) {
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));
119     }
120     valueObject->setName(propertyName);
121     if (type == "QColor")
122         valueObject->setValue(QVariant("#000000"));
123     else
124         valueObject->setValue(QVariant(1));
125
126 }
127
128 void createPropertyEditorValue(const QmlObjectNode &fxObjectNode, const QString &name, const QVariant &value, QDeclarativePropertyMap *propertyMap, PropertyEditor *propertyEditor)
129 {
130     QString propertyName(name);
131     propertyName.replace(QLatin1Char('.'), QLatin1Char('_'));
132     PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(propertyMap->value(propertyName)));
133     if (!valueObject) {
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));
138     }
139     valueObject->setName(name);
140     valueObject->setModelNode(fxObjectNode);
141
142     if (fxObjectNode.propertyAffectedByCurrentState(name) && !(fxObjectNode.modelNode().property(name).isBindingProperty())) {
143         valueObject->setValue(fxObjectNode.modelValue(name));
144
145     } else {
146         valueObject->setValue(value);
147     }
148
149     if (propertyName != QLatin1String("id") &&
150         fxObjectNode.currentState().isBaseState() &&
151         fxObjectNode.modelNode().property(propertyName).isBindingProperty()) {
152         valueObject->setExpression(fxObjectNode.modelNode().bindingProperty(propertyName).expression());
153     } else {
154         valueObject->setExpression(fxObjectNode.instanceValue(name).toString());
155     }
156 }
157
158 void PropertyEditor::NodeType::setValue(const QmlObjectNode & fxObjectNode, const QString &name, const QVariant &value)
159 {
160     QString propertyName = name;
161     propertyName.replace(QLatin1Char('.'), QLatin1Char('_'));
162     PropertyEditorValue *propertyValue = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value(propertyName)));
163     if (propertyValue) {
164         propertyValue->setValue(value);
165         if (!fxObjectNode.hasBindingProperty(name))
166             propertyValue->setExpression(value.toString());
167         else
168             propertyValue->setExpression(fxObjectNode.expression(name));
169     }
170 }
171
172 void PropertyEditor::NodeType::setup(const QmlObjectNode &fxObjectNode, const QString &stateName, const QUrl &qmlSpecificsFile, PropertyEditor *propertyEditor)
173 {
174     if (!fxObjectNode.isValid()) {
175         return;
176     }
177
178     QDeclarativeContext *ctxt = m_view->rootContext();
179
180     if (fxObjectNode.isValid()) {
181         foreach (const QString &propertyName, fxObjectNode.modelNode().metaInfo().propertyNames())
182             createPropertyEditorValue(fxObjectNode, propertyName, fxObjectNode.instanceValue(propertyName), &m_backendValuesPropertyMap, propertyEditor);
183
184         // className
185         PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("className")));
186         if (!valueObject)
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));
193
194         // id
195         valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("id")));
196         if (!valueObject)
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));
202
203         // anchors
204         m_backendAnchorBinding.setup(QmlItemNode(fxObjectNode.modelNode()));
205
206         ctxt->setContextProperty("anchorBackend", &m_backendAnchorBinding);
207         
208         ctxt->setContextProperty("transaction", m_propertyEditorTransaction.data());
209         
210         m_contextObject->setSpecificsUrl(qmlSpecificsFile);
211         
212         m_contextObject->setStateName(stateName);
213         QApplication::processEvents();
214         if (!fxObjectNode.isValid())
215             return;
216         ctxt->setContextProperty("propertyCount", QVariant(fxObjectNode.modelNode().properties().count()));
217
218         m_contextObject->setIsBaseState(fxObjectNode.isInBaseState());
219         m_contextObject->setSelectionChanged(false);
220
221         m_contextObject->setSelectionChanged(false);
222     } else {
223         qWarning() << "PropertyEditor: invalid node for setup";
224     }
225 }
226
227 void PropertyEditor::NodeType::initialSetup(const QString &typeName, const QUrl &qmlSpecificsFile, PropertyEditor *propertyEditor)
228 {
229     QDeclarativeContext *ctxt = m_view->rootContext();
230
231     NodeMetaInfo metaInfo = propertyEditor->model()->metaInfo(typeName, 4, 7);
232
233     foreach (const QString &propertyName, metaInfo.propertyNames())
234         setupPropertyEditorValue(propertyName, &m_backendValuesPropertyMap, propertyEditor, metaInfo.propertyTypeName(propertyName));
235
236     PropertyEditorValue *valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("className")));
237     if (!valueObject)
238         valueObject = new PropertyEditorValue(&m_backendValuesPropertyMap);
239     valueObject->setName("className");
240
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));
244
245     // id
246     valueObject = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_backendValuesPropertyMap.value("id")));
247     if (!valueObject)
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));
253
254     ctxt->setContextProperty("anchorBackend", &m_backendAnchorBinding);
255     ctxt->setContextProperty("transaction", m_propertyEditorTransaction.data());
256
257     m_contextObject->setSpecificsUrl(qmlSpecificsFile);
258
259     m_contextObject->setStateName(QLatin1String("basestate"));
260
261     m_contextObject->setIsBaseState(true);
262
263     m_contextObject->setSpecificQmlData(QLatin1String(""));
264
265     m_contextObject->setGlobalBaseUrl(QUrl());
266 }
267
268 PropertyEditor::PropertyEditor(QWidget *parent) :
269         QmlModelView(parent),
270         m_parent(parent),
271         m_updateShortcut(0),
272         m_timerId(0),
273         m_stackedWidget(new StackedWidget(parent)),
274         m_currentType(0),
275         m_locked(false),
276         m_setupCompleted(false),
277         m_singleShotTimer(new QTimer(this))
278 {
279     m_updateShortcut = new QShortcut(QKeySequence("F5"), m_stackedWidget);
280     connect(m_updateShortcut, SIGNAL(activated()), this, SLOT(reloadQml()));
281
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()));
289
290     m_stackedWidget->insertWidget(0, new QWidget(m_stackedWidget));
291
292
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();
308     }
309 }
310
311 PropertyEditor::~PropertyEditor()
312 {
313     delete m_stackedWidget;
314     qDeleteAll(m_typeHash);
315 }
316
317 static inline QString fixTypeNameForPanes(const QString &typeName)
318 {
319     QString fixedTypeName = typeName;
320     fixedTypeName.replace('.', '/');
321     fixedTypeName.replace("QtQuick/", "Qt/");
322     return fixedTypeName;
323 }
324
325 void PropertyEditor::setupPane(const QString &typeName)
326 {
327
328     QUrl qmlFile = fileToUrl(locateQmlFile(QLatin1String("Qt/ItemPane.qml")));
329     QUrl qmlSpecificsFile;
330
331     qmlSpecificsFile = fileToUrl(locateQmlFile(fixTypeNameForPanes(typeName) + "Specifics.qml"));
332     NodeType *type = m_typeHash.value(qmlFile.toString());
333
334     if (!type) {
335         type = new NodeType(this);
336
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) );
342
343         m_stackedWidget->addWidget(type->m_view);
344         m_typeHash.insert(qmlFile.toString(), type);
345     } else {
346         QDeclarativeContext *ctxt = type->m_view->rootContext();
347         ctxt->setContextProperty("finishedNotify", QVariant(false) );
348
349         type->initialSetup(typeName, qmlSpecificsFile, this);
350         ctxt->setContextProperty("finishedNotify", QVariant(true) );
351     }
352 }
353
354 void PropertyEditor::changeValue(const QString &propertyName)
355 {
356     if (propertyName.isNull())
357         return;
358
359     if (m_locked)
360         return;
361
362     if (propertyName == "type")
363         return;
364
365     if (!m_selectedNode.isValid())
366         return;
367
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();
371
372         if (newId == m_selectedNode.id())
373             return;
374
375         if (m_selectedNode.isValidId(newId)  && !modelNodeForId(newId).isValid() ) {
376             if (m_selectedNode.id().isEmpty() || newId.isEmpty()) { //no id
377                 try {
378                     m_selectedNode.setId(newId);
379                 } catch (InvalidIdException &e) { //better save then sorry
380                     m_locked = true;
381                     value->setValue(m_selectedNode.id());
382                     m_locked = false;
383                     QMessageBox::warning(0, tr("Invalid Id"), e.description());
384                 }
385             } else { //there is already an id, so we refactor
386                 if (rewriterView())
387                     rewriterView()->renameId(m_selectedNode.id(), newId);
388             }
389         } else {
390             m_locked = true;
391             value->setValue(m_selectedNode.id());
392             m_locked = false;
393             if (!m_selectedNode.isValidId(newId))
394                 QMessageBox::warning(0, tr("Invalid Id"),  tr("%1 is an invalid id").arg(newId));
395             else
396                 QMessageBox::warning(0, tr("Invalid Id"),  tr("%1 already exists").arg(newId));
397         }
398         return;
399     }
400
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)));
405
406     if (value ==0) {
407         return;
408     }
409
410     QmlObjectNode fxObjectNode(m_selectedNode);
411
412     QVariant castedValue;
413
414     if (fxObjectNode.modelNode().metaInfo().isValid() && fxObjectNode.modelNode().metaInfo().hasProperty(propertyName)) {
415         castedValue = fxObjectNode.modelNode().metaInfo().propertyCastedValue(propertyName, value->value());
416     } else {
417         qWarning() << "PropertyEditor:" <<propertyName << "cannot be casted (metainfo)";
418         return ;
419     }
420
421     if (value->value().isValid() && !castedValue.isValid()) {
422         qWarning() << "PropertyEditor:" << propertyName << "not properly casted (metainfo)";
423         return ;
424     }
425
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));
432         }
433     }
434
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);
440         }
441
442         try {
443             if (!value->value().isValid()) { //reset
444                 fxObjectNode.removeVariantProperty(propertyName);
445             } else {
446                 if (castedValue.isValid() && !castedValue.isNull()) {
447                     m_locked = true;
448                     fxObjectNode.setVariantProperty(propertyName, castedValue);
449                     m_locked = false;
450                 }
451             }
452         }
453         catch (RewritingException &e) {
454             QMessageBox::warning(0, "Error", e.description());
455         }
456 }
457
458 void PropertyEditor::changeExpression(const QString &name)
459 {
460     if (name.isNull())
461         return;
462
463     if (m_locked)
464         return;
465
466     RewriterTransaction transaction = beginRewriterTransaction();
467
468     try {
469         QString underscoreName(name);
470         underscoreName.replace(QLatin1Char('.'), QLatin1Char('_'));
471
472         QmlObjectNode fxObjectNode(m_selectedNode);
473         PropertyEditorValue *value = qobject_cast<PropertyEditorValue*>(QDeclarativeMetaType::toQObject(m_currentType->m_backendValuesPropertyMap.value(underscoreName)));
474
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
480                     return;
481                 }
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);
486                     else
487                         fxObjectNode.setVariantProperty(name, false);
488                     transaction.commit(); //committing in the try block
489                     return;
490                 }
491             } else if (fxObjectNode.modelNode().metaInfo().propertyTypeName(name) == QLatin1String("int")) {
492                 bool ok;
493                 int intValue = value->expression().toInt(&ok);
494                 if (ok) {
495                     fxObjectNode.setVariantProperty(name, intValue);
496                     transaction.commit(); //committing in the try block
497                     return;
498                 }
499             } else if (fxObjectNode.modelNode().metaInfo().propertyTypeName(name) == QLatin1String("qreal")) {
500                 bool ok;
501                 qreal realValue = value->expression().toFloat(&ok);
502                 if (ok) {
503                     fxObjectNode.setVariantProperty(name, realValue);
504                     transaction.commit(); //committing in the try block
505                     return;
506                 }
507             }
508         }
509
510         if (!value) {
511             qWarning() << "PropertyEditor::changeExpression no value for " << underscoreName;
512             return;
513         }
514
515         if (fxObjectNode.expression(name) != value->expression() || !fxObjectNode.propertyAffectedByCurrentState(name))
516             fxObjectNode.setBindingProperty(name, value->expression());
517
518         transaction.commit(); //committing in the try block
519     }
520
521     catch (RewritingException &e) {
522         QMessageBox::warning(0, "Error", e.description());
523     }
524 }
525
526 void PropertyEditor::updateSize()
527 {
528     if (!m_currentType)
529         return;
530     QWidget* frame = m_currentType->m_view->findChild<QWidget*>("propertyEditorFrame");
531     if (frame)
532         frame->resize(m_stackedWidget->size());
533 }
534
535 void PropertyEditor::setupPanes()
536 {
537     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
538     setupPane("QtQuick.Rectangle");
539     setupPane("QtQuick.Text");
540     resetView();
541     m_setupCompleted = true;
542     QApplication::restoreOverrideCursor();
543 }
544
545 void PropertyEditor::otherPropertyChanged(const QmlObjectNode &fxObjectNode, const QString &propertyName)
546 {
547     QmlModelView::otherPropertyChanged(fxObjectNode, propertyName);
548
549     if (!m_selectedNode.isValid())
550         return;
551
552     m_locked = true;
553
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()));
559             else
560                 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
561         }
562     }
563
564     m_locked = false;
565 }
566
567 void PropertyEditor::transformChanged(const QmlObjectNode &fxObjectNode, const QString &propertyName)
568 {
569     QmlModelView::transformChanged(fxObjectNode, propertyName);
570
571     if (!m_selectedNode.isValid())
572         return;
573
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()));
579             else
580                 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
581         }
582     }
583 }
584
585 void PropertyEditor::setQmlDir(const QString &qmlDir)
586 {
587     m_qmlDir = qmlDir;
588
589     QFileSystemWatcher *watcher = new QFileSystemWatcher(this);
590     watcher->addPath(m_qmlDir);
591     connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(reloadQml()));
592 }
593
594 void PropertyEditor::delayedResetView()
595 {
596     if (m_timerId == 0)
597         m_timerId = startTimer(200);
598 }
599
600 void PropertyEditor::timerEvent(QTimerEvent *timerEvent)
601 {
602     if (m_timerId == timerEvent->timerId()) {
603         resetView();
604     }
605 }
606
607 QString templateGeneration(NodeMetaInfo type, NodeMetaInfo superType, const QmlObjectNode &objectNode)
608 {
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");
613
614     QList<QString> orderedList;
615     orderedList = type.propertyNames();
616     qSort(orderedList);
617
618     foreach (const QString &name, orderedList) {
619
620         if (name.startsWith(QLatin1String("__")))
621             continue; //private API
622         QString properName = name;
623
624         properName.replace('.', '_');
625
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);
630
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);
636             }
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);
641             }
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);
646             }
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);
651             }
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);
656             }
657         }
658     }
659     qmlTemplate += QLatin1String("}\n"); //VerticalLayout
660     qmlTemplate += QLatin1String("}\n"); //GroupBox
661
662     return qmlTemplate;
663 }
664
665 void PropertyEditor::resetView()
666 {
667     if (model() == 0)
668         return;
669
670     m_locked = true;
671
672     if (debug)
673         qDebug() << "________________ RELOADING PROPERTY EDITOR QML _______________________";
674
675     if (m_timerId)
676         killTimer(m_timerId);
677
678     if (m_selectedNode.isValid() && model() != m_selectedNode.model())
679         m_selectedNode = ModelNode();
680
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"));
686
687     QString specificQmlData;
688
689     if (m_selectedNode.isValid() && !QFileInfo(qmlSpecificsFile.toLocalFile()).exists() && m_selectedNode.metaInfo().isValid()) {
690         //do magic !!
691         specificQmlData = templateGeneration(m_selectedNode.metaInfo(), model()->metaInfo(specificsClassName), m_selectedNode);
692     }
693
694     NodeType *type = m_typeHash.value(qmlFile.toString());
695
696     if (!type) {
697         type = new NodeType(this);
698
699         m_stackedWidget->addWidget(type->m_view);
700         m_typeHash.insert(qmlFile.toString(), type);
701
702         QmlObjectNode fxObjectNode;
703         if (m_selectedNode.isValid()) {
704             fxObjectNode = QmlObjectNode(m_selectedNode);
705             Q_ASSERT(fxObjectNode.isValid());
706         }
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);
712             
713         type->m_contextObject->setGlobalBaseUrl(qmlFile);
714         type->m_contextObject->setSpecificQmlData(specificQmlData);
715         type->m_view->setSource(qmlFile);
716         ctxt->setContextProperty("finishedNotify", QVariant(true));
717     } else {
718         QmlObjectNode fxObjectNode;
719         if (m_selectedNode.isValid()) {
720             fxObjectNode = QmlObjectNode(m_selectedNode);
721         }
722         QDeclarativeContext *ctxt = type->m_view->rootContext();
723         
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);
731     }
732
733     m_stackedWidget->setCurrentWidget(type->m_view);
734
735
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();
742
743     m_currentType = type;
744
745     m_locked = false;
746
747     if (m_timerId)
748         m_timerId = 0;
749
750     updateSize();
751 }
752
753 void PropertyEditor::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
754                                           const QList<ModelNode> &lastSelectedNodeList)
755 {
756     Q_UNUSED(lastSelectedNodeList);
757
758     if (selectedNodeList.isEmpty() || selectedNodeList.count() > 1)
759         select(ModelNode());
760     else if (m_selectedNode != selectedNodeList.first())
761         select(selectedNodeList.first());
762 }
763
764 void PropertyEditor::nodeAboutToBeRemoved(const ModelNode &removedNode)
765 {
766     QmlModelView::nodeAboutToBeRemoved(removedNode);
767     if (m_selectedNode.isValid() && removedNode.isValid() && m_selectedNode == removedNode)
768         select(m_selectedNode.parentProperty().parentModelNode());
769 }
770
771 void PropertyEditor::modelAttached(Model *model)
772 {
773     QmlModelView::modelAttached(model);
774
775     if (debug)
776         qDebug() << Q_FUNC_INFO;
777
778     m_locked = true;
779
780     resetView();
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();
786     }
787
788     m_locked = false;
789 }
790
791 void PropertyEditor::modelAboutToBeDetached(Model *model)
792 {
793     QmlModelView::modelAboutToBeDetached(model);
794     m_currentType->m_propertyEditorTransaction->end();
795
796     resetView();
797 }
798
799
800 void PropertyEditor::propertiesRemoved(const QList<AbstractProperty>& propertyList)
801 {
802     QmlModelView::propertiesRemoved(propertyList);
803
804     if (!m_selectedNode.isValid())
805         return;
806
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);
813         }
814     }
815 }
816
817
818 void PropertyEditor::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange)
819 {
820
821     QmlModelView::variantPropertiesChanged(propertyList, propertyChange);
822
823     if (!m_selectedNode.isValid())
824         return;
825
826     foreach (const VariantProperty &property, propertyList) {
827         ModelNode node(property.parentModelNode());
828
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()));
832             else
833                 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
834         }
835     }
836 }
837
838 void PropertyEditor::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange)
839 {
840     QmlModelView::bindingPropertiesChanged(propertyList, propertyChange);
841
842     if (!m_selectedNode.isValid())
843         return;
844
845     foreach (const BindingProperty &property, propertyList) {
846         ModelNode node(property.parentModelNode());
847
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()));
853             else
854                 setValue(m_selectedNode, property.name(), QmlObjectNode(m_selectedNode).modelValue(property.name()));
855         }
856     }
857 }
858
859
860 void PropertyEditor::instanceInformationsChange(const QVector<ModelNode> &nodeList)
861 {
862     if (!m_selectedNode.isValid())
863         return;
864
865     m_locked = true;
866     if (nodeList.contains(m_selectedNode))
867         m_currentType->m_backendAnchorBinding.setup(QmlItemNode(m_selectedNode));
868     m_locked = false;
869 }
870
871 void PropertyEditor::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId)
872 {
873     QmlModelView::nodeIdChanged(node, newId, oldId);
874
875     if (!m_selectedNode.isValid())
876         return;
877
878     if (node == m_selectedNode) {
879
880         if (m_currentType) {
881             setValue(node, "id", newId);
882         }
883     }
884 }
885
886 void PropertyEditor::scriptFunctionsChanged(const ModelNode &node, const QStringList &scriptFunctionList)
887 {
888     QmlModelView::scriptFunctionsChanged(node, scriptFunctionList);
889 }
890
891 void PropertyEditor::select(const ModelNode &node)
892 {
893     if (QmlItemNode(node).isValid())
894         m_selectedNode = node;
895     else
896         m_selectedNode = ModelNode();
897
898     delayedResetView();
899 }
900
901 QWidget *PropertyEditor::createPropertiesPage()
902 {
903     delayedResetView();
904     return m_stackedWidget;
905 }
906
907 void PropertyEditor::stateChanged(const QmlModelState &newQmlModelState, const QmlModelState &oldQmlModelState)
908 {
909     QmlModelView::stateChanged(newQmlModelState, oldQmlModelState);
910     Q_ASSERT(newQmlModelState.isValid());
911     if (debug)
912         qDebug() << Q_FUNC_INFO << newQmlModelState.name();
913     delayedResetView();
914 }
915
916 void PropertyEditor::setValue(const QmlObjectNode &fxObjectNode, const QString &name, const QVariant &value)
917 {
918     m_locked = true;
919     m_currentType->setValue(fxObjectNode, name, value);
920     m_locked = false;
921 }
922
923 void PropertyEditor::reloadQml()
924 {
925     m_typeHash.clear();
926     while (QWidget *widget = m_stackedWidget->widget(0)) {
927         m_stackedWidget->removeWidget(widget);
928         delete widget;
929     }
930     m_currentType = 0;
931
932     delayedResetView();
933 }
934
935 QString PropertyEditor::qmlFileName(const NodeMetaInfo &nodeInfo) const
936 {
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");
941 }
942
943 QUrl PropertyEditor::fileToUrl(const QString &filePath) const {
944     QUrl fileUrl;
945
946     if (filePath.isEmpty())
947         return fileUrl;
948
949     if (filePath.startsWith(QLatin1Char(':'))) {
950         fileUrl.setScheme("qrc");
951         QString path = filePath;
952         path.remove(0, 1); // remove trailing ':'
953         fileUrl.setPath(path);
954     } else {
955         fileUrl = QUrl::fromLocalFile(filePath);
956     }
957
958     return fileUrl;
959 }
960
961 QUrl PropertyEditor::qmlForNode(const ModelNode &modelNode, QString &className) const
962 {
963     if (modelNode.isValid()) {
964         QList<NodeMetaInfo> hierarchy;
965         hierarchy << modelNode.metaInfo();
966         hierarchy << modelNode.metaInfo().superClasses();
967
968         foreach (const NodeMetaInfo &info, hierarchy) {
969             QUrl fileUrl = fileToUrl(locateQmlFile(qmlFileName(info)));
970             if (fileUrl.isValid()) {
971                 className = info.typeName();
972                 return fileUrl;
973             }
974         }
975     }
976     return fileToUrl(QDir(m_qmlDir).filePath("Qt/emptyPane.qml"));
977 }
978
979 QString PropertyEditor::locateQmlFile(const QString &relativePath) const
980 {
981     QDir fileSystemDir(m_qmlDir);
982     static QDir resourcesDir(":/propertyeditor");
983
984     if (fileSystemDir.exists(relativePath))
985         return fileSystemDir.absoluteFilePath(relativePath);
986     if (resourcesDir.exists(relativePath))
987         return resourcesDir.absoluteFilePath(relativePath);
988
989     return QString();
990 }
991
992
993 } //QmlDesigner
994