OSDN Git Service

98654fccb79321076145b83e63b24ac91da483e7
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qmldesigner / components / integration / designdocumentcontroller.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 (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "designdocumentcontroller.h"
34 #include "designdocumentcontrollerview.h"
35 #include "xuifiledialog.h"
36 #include "componentview.h"
37 #include "subcomponentmanager.h"
38 #include "model/viewlogger.h"
39
40 #include <qt4projectmanager/qt4buildconfiguration.h>
41
42 #include <itemlibraryview.h>
43 #include <itemlibrarywidget.h>
44 #include <navigatorview.h>
45 #include <stateseditorview.h>
46 #include <formeditorview.h>
47 #include <propertyeditor.h>
48 #include <formeditorwidget.h>
49 #include <basetexteditmodifier.h>
50 #include <componenttextmodifier.h>
51 #include <metainfo.h>
52 #include <invalidargumentexception.h>
53 #include <componentview.h>
54 #include <componentaction.h>
55 #include <qmlobjectnode.h>
56 #include <rewriterview.h>
57 #include <rewritingexception.h>
58 #include <nodelistproperty.h>
59 #include <variantproperty.h>
60 #include <rewritingexception.h>
61 #include <model/modelnodecontextmenu.h>
62
63 #include <utils/fileutils.h>
64
65 #include <QtCore/QCoreApplication>
66 #include <QtCore/QDir>
67 #include <QtCore/QFile>
68 #include <QtCore/QFileInfo>
69 #include <QtCore/QUrl>
70 #include <QtCore/QProcess>
71 #include <QtCore/QTemporaryFile>
72 #include <QtCore/QtDebug>
73 #include <QtCore/QEvent>
74
75 #include <QtGui/QBoxLayout>
76 #include <QtGui/QComboBox>
77 #include <QtGui/QErrorMessage>
78 #include <QtGui/QFileDialog>
79 #include <QtGui/QLabel>
80 #include <QtGui/QMdiArea>
81 #include <QtGui/QMdiSubWindow>
82 #include <QtGui/QMessageBox>
83 #include <QtGui/QUndoStack>
84 #include <QtGui/QPlainTextEdit>
85 #include <QtGui/QApplication>
86
87 #include <projectexplorer/projectexplorer.h>
88 #include <qt4projectmanager/qt4project.h>
89 #include <qt4projectmanager/qt4target.h>
90 #include <qtsupport/qtversionmanager.h>
91 #include <qt4projectmanager/qt4projectmanagerconstants.h>
92 #include <qmlprojectmanager/qmlprojectrunconfiguration.h>
93
94 enum {
95     debug = false
96 };
97
98 namespace QmlDesigner {
99
100 class DesignDocumentControllerPrivate {
101 public:
102     QWeakPointer<FormEditorView> formEditorView;
103
104     QWeakPointer<ItemLibraryView> itemLibraryView;
105     QWeakPointer<NavigatorView> navigator;
106     QWeakPointer<PropertyEditor> propertyEditorView;
107     QWeakPointer<StatesEditorView> statesEditorView;
108     QWeakPointer<QStackedWidget> stackedWidget;
109     QWeakPointer<NodeInstanceView> nodeInstanceView;
110     QWeakPointer<ComponentView> componentView;
111
112     QWeakPointer<QmlDesigner::Model> model;
113     QWeakPointer<QmlDesigner::Model> subComponentModel;
114     QWeakPointer<QmlDesigner::Model> masterModel;
115     QWeakPointer<QPlainTextEdit> textEdit;
116     QWeakPointer<RewriterView> rewriterView;
117     QmlDesigner::BaseTextEditModifier *textModifier;
118     QmlDesigner::ComponentTextModifier *componentTextModifier;
119     QWeakPointer<SubComponentManager> subComponentManager;
120     QWeakPointer<Internal::ViewLogger> viewLogger;
121
122     QString fileName;
123     QUrl searchPath;
124     bool documentLoaded;
125     bool syncBlocked;
126     int qt_versionId;
127 };
128
129 DesignDocumentController *DesignDocumentController::m_this = 0;
130
131 /**
132   \class QmlDesigner::DesignDocumentController
133
134   DesignDocumentController acts as a facade to a model representing a qml document,
135   and the different views/widgets accessing it.
136   */
137 DesignDocumentController::DesignDocumentController(QObject *parent) :
138         QObject(parent),
139         d(new DesignDocumentControllerPrivate)
140 {
141     m_this = this;
142     d->documentLoaded = false;
143     d->syncBlocked = false;
144
145     ProjectExplorer::ProjectExplorerPlugin *projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
146     connect(projectExplorer, SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), this, SLOT(activeQtVersionChanged()));
147     activeQtVersionChanged();
148 }
149
150 DesignDocumentController::~DesignDocumentController()
151 {
152     m_this = 0;
153     delete d->model.data();
154     delete d->subComponentModel.data();
155
156     delete d->rewriterView.data();
157
158     if (d->componentTextModifier) //componentTextModifier might not be created
159         delete d->componentTextModifier;
160
161     delete d;
162 }
163
164 Model *DesignDocumentController::model() const
165 {
166     return d->model.data();
167 }
168
169 Model *DesignDocumentController::masterModel() const
170 {
171     return d->masterModel.data();
172 }
173
174
175 void DesignDocumentController::detachNodeInstanceView()
176 {
177     if (d->nodeInstanceView)
178         model()->detachView(d->nodeInstanceView.data());
179 }
180
181 void DesignDocumentController::attachNodeInstanceView()
182 {
183     if (d->nodeInstanceView) {
184         model()->attachView(d->nodeInstanceView.data());
185     }
186     if (d->formEditorView) {
187         d->formEditorView->resetView();
188     }
189 }
190
191 void DesignDocumentController::changeToMasterModel()
192 {
193     d->model->detachView(d->rewriterView.data());
194     d->rewriterView->setTextModifier(d->textModifier);
195     d->model = d->masterModel;
196     d->model->attachView(d->rewriterView.data());
197 }
198
199 QWidget *DesignDocumentController::centralWidget() const
200 {
201     return qobject_cast<QWidget*>(parent());
202 }
203
204 QString DesignDocumentController::pathToQt() const
205 {
206     QtSupport::BaseQtVersion *activeQtVersion = QtSupport::QtVersionManager::instance()->version(d->qt_versionId);
207     if (activeQtVersion && (activeQtVersion->qtVersion().majorVersion > 3)
208             && (activeQtVersion->supportsTargetId(Qt4ProjectManager::Constants::QT_SIMULATOR_TARGET_ID)
209                 || activeQtVersion->supportsTargetId(Qt4ProjectManager::Constants::DESKTOP_TARGET_ID)))
210         return activeQtVersion->versionInfo().value("QT_INSTALL_DATA");
211     return QString();
212 }
213
214 /*!
215   Returns whether the model is automatically updated if the text editor changes.
216   */
217 bool DesignDocumentController::isModelSyncBlocked() const
218 {
219     return d->syncBlocked;
220 }
221
222 /*!
223   Switches whether the model (and therefore the views) are updated if the text editor
224   changes.
225
226   If the synchronization is enabled again, the model is automatically resynchronized
227   with the current state of the text editor.
228   */
229 void DesignDocumentController::blockModelSync(bool block)
230 {
231     if (d->syncBlocked == block)
232         return;
233
234     d->syncBlocked = block;
235
236     if (d->textModifier) {
237         if (d->syncBlocked) {
238             detachNodeInstanceView();
239             d->textModifier->deactivateChangeSignals();
240         } else {
241             activeQtVersionChanged();
242             changeToMasterModel();
243             QmlModelState state;
244             //We go back to base state (and back again) to avoid side effects from text editing.
245             if (d->statesEditorView && d->statesEditorView->model()) {
246                 state = d->statesEditorView->currentState();
247                 d->statesEditorView->setCurrentState(d->statesEditorView->baseState());
248
249             }
250
251             d->textModifier->reactivateChangeSignals();
252
253             if (state.isValid() && d->statesEditorView)
254                 d->statesEditorView->setCurrentState(state);
255             attachNodeInstanceView();
256             if (d->propertyEditorView)
257                 d->propertyEditorView->resetView();
258             if (d->formEditorView)
259                 d->formEditorView->resetView();
260         }
261     }
262 }
263
264 /*!
265   Returns any errors that happened when parsing the latest qml file.
266   */
267 QList<RewriterView::Error> DesignDocumentController::qmlErrors() const
268 {
269     return d->rewriterView->errors();
270 }
271
272 void DesignDocumentController::setItemLibraryView(ItemLibraryView* itemLibraryView)
273 {
274     d->itemLibraryView = itemLibraryView;
275 }
276
277 void DesignDocumentController::setNavigator(NavigatorView* navigatorView)
278 {
279     d->navigator = navigatorView;
280 }
281
282 void DesignDocumentController::setPropertyEditorView(PropertyEditor *propertyEditor)
283 {
284     d->propertyEditorView = propertyEditor;
285 }
286
287 void DesignDocumentController::setStatesEditorView(StatesEditorView* statesEditorView)
288 {
289     d->statesEditorView = statesEditorView;
290 }
291
292 void DesignDocumentController::setFormEditorView(FormEditorView *formEditorView)
293 {
294     d->formEditorView = formEditorView;
295 }
296
297 void DesignDocumentController::setNodeInstanceView(NodeInstanceView *nodeInstanceView)
298 {
299     d->nodeInstanceView = nodeInstanceView;
300 }
301
302 void DesignDocumentController::setComponentView(ComponentView *componentView)
303 {
304     d->componentView = componentView;
305     connect(d->componentView->action(), SIGNAL(currentComponentChanged(ModelNode)), SLOT(changeCurrentModelTo(ModelNode)));
306 }
307
308 DesignDocumentController *DesignDocumentController::instance()
309 {
310     return m_this;
311 }
312
313 QString DesignDocumentController::displayName() const
314 {
315     if (fileName().isEmpty())
316         return tr("-New Form-");
317     else
318         return fileName();
319 }
320
321 QString DesignDocumentController::fileName() const
322 {
323     return d->fileName;
324 }
325
326 void DesignDocumentController::setFileName(const QString &fileName)
327 {
328     d->fileName = fileName;
329
330     if (QFileInfo(fileName).exists()) {
331         d->searchPath = QUrl::fromLocalFile(fileName);
332     } else {
333         d->searchPath = QUrl(fileName);
334     }
335
336     if (d->model)
337         d->model->setFileUrl(d->searchPath);
338
339     if (d->itemLibraryView)
340         d->itemLibraryView->widget()->setResourcePath(QFileInfo(fileName).absolutePath());
341     emit displayNameChanged(displayName());
342 }
343
344 QList<RewriterView::Error> DesignDocumentController::loadMaster(QPlainTextEdit *edit)
345 {
346     Q_CHECK_PTR(edit);
347
348     d->textEdit = edit;
349
350     connect(edit, SIGNAL(undoAvailable(bool)),
351             this, SIGNAL(undoAvailable(bool)));
352     connect(edit, SIGNAL(redoAvailable(bool)),
353             this, SIGNAL(redoAvailable(bool)));
354     connect(edit, SIGNAL(modificationChanged(bool)),
355             this, SIGNAL(dirtyStateChanged(bool)));
356
357     d->textModifier = new BaseTextEditModifier(dynamic_cast<TextEditor::BaseTextEditorWidget*>(d->textEdit.data()));
358
359     d->componentTextModifier = 0;
360
361     //d->masterModel = Model::create(d->textModifier, d->searchPath, errors);
362
363     d->masterModel = Model::create("QtQuick.Rectangle", 1, 0);
364
365 #if defined(VIEWLOGGER)
366     d->viewLogger = new Internal::ViewLogger(d->model.data());
367     d->masterModel->attachView(d->viewLogger.data());
368 #endif
369
370     d->masterModel->setFileUrl(d->searchPath);
371
372     d->subComponentModel = Model::create("QtQuick.Rectangle", 1, 0);
373     d->subComponentModel->setFileUrl(d->searchPath);
374
375     d->rewriterView = new RewriterView(RewriterView::Amend, d->masterModel.data());
376     d->rewriterView->setTextModifier( d->textModifier);
377     connect(d->rewriterView.data(), SIGNAL(errorsChanged(const QList<RewriterView::Error> &)),
378             this, SIGNAL(qmlErrorsChanged(const QList<RewriterView::Error> &)));
379
380     d->masterModel->attachView(d->rewriterView.data());
381     d->model = d->masterModel;
382
383     d->subComponentManager = new SubComponentManager(d->masterModel.data(), this);
384     d->subComponentManager->update(d->searchPath, d->model->imports());
385
386     loadCurrentModel();
387
388     d->masterModel->attachView(d->componentView.data());
389
390     return d->rewriterView->errors();
391 }
392
393 void DesignDocumentController::changeCurrentModelTo(const ModelNode &componentNode)
394 {
395     Q_ASSERT(d->masterModel);
396     QWeakPointer<Model> oldModel = d->model;
397     Q_ASSERT(oldModel.data());
398
399     if (d->model == d->subComponentModel) {
400         changeToMasterModel();
401     }
402
403     QString componentText = d->rewriterView->extractText(QList<ModelNode>() << componentNode).value(componentNode);
404
405     if (componentText.isEmpty())
406         return;
407
408     bool explicitComponent = false;
409     if (componentText.contains("Component")) { //explicit component
410         explicitComponent = true;
411     }
412
413     if (!componentNode.isRootNode()) {
414         Q_ASSERT(d->model == d->masterModel);
415         Q_ASSERT(componentNode.isValid());
416         //change to subcomponent model
417         ModelNode rootModelNode = componentNode.view()->rootModelNode();
418         Q_ASSERT(rootModelNode.isValid());
419         if (d->componentTextModifier)
420             delete d->componentTextModifier;
421
422
423         int componentStartOffset;
424         int componentEndOffset;
425
426         int rootStartOffset = d->rewriterView->nodeOffset(rootModelNode);
427
428         if (explicitComponent) { //the component is explciit we have to find the first definition inside
429             componentStartOffset = d->rewriterView->firstDefinitionInsideOffset(componentNode);
430             componentEndOffset = componentStartOffset + d->rewriterView->firstDefinitionInsideLength(componentNode);
431         } else { //the component is implicit
432             componentStartOffset = d->rewriterView->nodeOffset(componentNode);
433             componentEndOffset = componentStartOffset + d->rewriterView->nodeLength(componentNode);
434         }
435
436         d->componentTextModifier = new ComponentTextModifier (d->textModifier, componentStartOffset, componentEndOffset, rootStartOffset);
437
438
439         d->model->detachView(d->rewriterView.data());
440
441         d->rewriterView->setTextModifier(d->componentTextModifier);
442
443         d->subComponentModel->attachView(d->rewriterView.data());
444
445         Q_ASSERT(d->rewriterView->rootModelNode().isValid());
446
447         d->model = d->subComponentModel;
448     }
449
450     Q_ASSERT(d->masterModel);
451     Q_ASSERT(d->model);
452
453     loadCurrentModel();
454     d->componentView->setComponentNode(componentNode);
455 }
456
457 void DesignDocumentController::goIntoComponent()
458 {
459     if (!d->model)
460         return;
461
462     QList<ModelNode> selectedNodes;
463     if (d->formEditorView)
464         selectedNodes = d->formEditorView->selectedModelNodes();
465
466     if (selectedNodes.count() == 1)
467         ModelNodeAction::goIntoComponent(selectedNodes.first());
468 }
469
470 void DesignDocumentController::loadCurrentModel()
471 {
472     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
473
474     Q_ASSERT(d->masterModel);
475     Q_ASSERT(d->model);
476     d->model->setMasterModel(d->masterModel.data());
477
478     d->nodeInstanceView->setPathToQt(pathToQt());
479     d->model->attachView(d->nodeInstanceView.data());
480     d->model->attachView(d->navigator.data());
481     d->itemLibraryView->widget()->setResourcePath(QFileInfo(d->fileName).absolutePath());
482
483     d->model->attachView(d->formEditorView.data());
484     d->model->attachView(d->itemLibraryView.data());
485
486     if (!d->textEdit->parent()) // hack to prevent changing owner of external text edit
487         d->stackedWidget->addWidget(d->textEdit.data());
488
489     // Will call setCurrentState (formEditorView etc has to be constructed first)
490     d->model->attachView(d->statesEditorView.data());
491
492     d->model->attachView(d->propertyEditorView.data());
493
494     d->documentLoaded = true;
495     Q_ASSERT(d->masterModel);
496     QApplication::restoreOverrideCursor();
497 }
498
499 QList<RewriterView::Error> DesignDocumentController::loadMaster(const QByteArray &qml)
500 {
501     QPlainTextEdit *textEdit = new QPlainTextEdit;
502     textEdit->setReadOnly(true);
503     textEdit->setPlainText(QString(qml));
504     return loadMaster(textEdit);
505 }
506
507 void DesignDocumentController::saveAs(QWidget *parent)
508 {
509     QFileInfo oldFileInfo(d->fileName);
510     XUIFileDialog::runSaveFileDialog(oldFileInfo.path(), parent, this, SLOT(doRealSaveAs(QString)));
511 }
512
513 void DesignDocumentController::doRealSaveAs(const QString &fileName)
514 {
515     if (fileName.isNull())
516         return;
517
518     QFileInfo fileInfo(fileName);
519     if (fileInfo.exists() && !fileInfo.isWritable()) {
520         QMessageBox msgBox(centralWidget());
521         msgBox.setIcon(QMessageBox::Warning);
522         msgBox.setText(tr("Cannot save to file \"%1\": permission denied.").arg(fileInfo.baseName()));
523         msgBox.exec();
524         return;
525     } else if (!fileInfo.exists() && !fileInfo.dir().exists()) {
526         QMessageBox msgBox(centralWidget());
527         msgBox.setIcon(QMessageBox::Warning);
528         msgBox.setText(tr("Parent folder \"%1\" for file \"%2\" does not exist.")
529                        .arg(fileInfo.dir().dirName())
530                        .arg(fileInfo.baseName()));
531         msgBox.exec();
532         return;
533     }
534
535     setFileName(fileName);
536     save(centralWidget());
537 }
538
539 bool DesignDocumentController::isDirty() const
540 {
541     if (d->textEdit)
542         return d->textEdit->document()->isModified();
543     else
544         return false;
545 }
546
547 bool DesignDocumentController::isUndoAvailable() const
548 {
549
550     if (d->textEdit)
551         return d->textEdit->document()->isUndoAvailable();
552     return false;
553 }
554
555 bool DesignDocumentController::isRedoAvailable() const
556 {
557     if (d->textEdit)
558         return d->textEdit->document()->isRedoAvailable();
559     return false;
560 }
561
562 void DesignDocumentController::close()
563 {
564     d->documentLoaded = false;
565     emit designDocumentClosed();
566 }
567
568 void DesignDocumentController::deleteSelected()
569 {
570     if (!d->model)
571         return;
572
573     try {
574         if (d->formEditorView) {
575             RewriterTransaction transaction(d->formEditorView.data());
576             QList<ModelNode> toDelete = d->formEditorView->selectedModelNodes();
577             foreach (ModelNode node, toDelete) {
578                 if (node.isValid() && !node.isRootNode() && QmlObjectNode(node).isValid())
579                     QmlObjectNode(node).destroy();
580             }
581         }
582     } catch (RewritingException &e) {
583         QMessageBox::warning(0, tr("Error"), e.description());
584     }
585 }
586
587 void DesignDocumentController::copySelected()
588 {
589     QScopedPointer<Model> model(Model::create("QtQuick.Rectangle", 1, 0, this->model()));
590     model->setFileUrl(d->model->fileUrl());
591     model->changeImports(d->model->imports(), QList<Import>());
592
593     Q_ASSERT(model);
594
595     DesignDocumentControllerView view;
596
597     d->model->attachView(&view);
598
599     if (view.selectedModelNodes().isEmpty())
600         return;
601
602     QList<ModelNode> selectedNodes(view.selectedModelNodes());
603
604     foreach (const ModelNode &node, selectedNodes) {
605         foreach (const ModelNode &node2, selectedNodes) {
606             if (node.isAncestorOf(node2))
607                 selectedNodes.removeAll(node2);
608         }
609     }
610
611     if (selectedNodes.count() == 1) {
612         ModelNode selectedNode(selectedNodes.first());
613
614         if (!selectedNode.isValid())
615             return;
616
617         d->model->detachView(&view);
618
619         model->attachView(&view);
620         view.replaceModel(selectedNode);
621
622         Q_ASSERT(view.rootModelNode().isValid());
623         Q_ASSERT(view.rootModelNode().type() != "empty");
624
625         view.toClipboard();
626     } else { //multi items selected
627         d->model->detachView(&view);
628         model->attachView(&view);
629
630         foreach (ModelNode node, view.rootModelNode().allDirectSubModelNodes()) {
631             node.destroy();
632         }
633         view.changeRootNodeType("QtQuick.Rectangle", 1, 0);
634         view.rootModelNode().setId("designer__Selection");
635
636         foreach (const ModelNode &selectedNode, selectedNodes) {
637             ModelNode newNode(view.insertModel(selectedNode));
638             view.rootModelNode().nodeListProperty("data").reparentHere(newNode);
639         }
640
641         view.toClipboard();
642     }
643 }
644
645 void DesignDocumentController::cutSelected()
646 {
647     copySelected();
648     deleteSelected();
649 }
650
651 static void scatterItem(ModelNode pastedNode, const ModelNode targetNode, int offset = -2000)
652 {
653
654     bool scatter = false;
655     foreach (const ModelNode &childNode, targetNode.allDirectSubModelNodes()) {
656         if ((childNode.variantProperty("x").value() == pastedNode.variantProperty("x").value()) &&
657             (childNode.variantProperty("y").value() == pastedNode.variantProperty("y").value()))
658             scatter = true;
659     }
660     if (!scatter)
661         return;
662
663     if (offset == -2000) {
664         double x = pastedNode.variantProperty("x").value().toDouble();
665         double y = pastedNode.variantProperty("y").value().toDouble();
666         double targetWidth = 20;
667         double targetHeight = 20;
668         x = x + double(qrand()) / RAND_MAX * targetWidth - targetWidth / 2;
669         y = y + double(qrand()) / RAND_MAX * targetHeight - targetHeight / 2;
670         pastedNode.variantProperty("x") = int(x);
671         pastedNode.variantProperty("y") = int(y);
672     } else {
673         double x = pastedNode.variantProperty("x").value().toDouble();
674         double y = pastedNode.variantProperty("y").value().toDouble();
675         x = x + offset;
676         y = y + offset;
677         pastedNode.variantProperty("x") = int(x);
678         pastedNode.variantProperty("y") = int(y);
679     }
680 }
681
682 void DesignDocumentController::paste()
683 {
684     QScopedPointer<Model> model(Model::create("empty", 1, 0, this->model()));
685     model->setFileUrl(d->model->fileUrl());
686     model->changeImports(d->model->imports(), QList<Import>());
687
688     Q_ASSERT(model);
689
690     if (!d->model)
691         return;
692
693     DesignDocumentControllerView view;
694     model->attachView(&view);
695
696     view.fromClipboard();
697
698     ModelNode rootNode(view.rootModelNode());
699
700     if (rootNode.type() == "empty")
701         return;
702
703     if (rootNode.id() == "designer__Selection") {
704         QList<ModelNode> selectedNodes = rootNode.allDirectSubModelNodes();
705         qDebug() << rootNode;
706         qDebug() << selectedNodes;
707         model->detachView(&view);
708         d->model->attachView(&view);
709
710         ModelNode targetNode;
711
712         if (!view.selectedModelNodes().isEmpty())
713             targetNode = view.selectedModelNodes().first();
714
715         //In case we copy and paste a selection we paste in the parent item
716         if ((view.selectedModelNodes().count() == selectedNodes.count()) && targetNode.isValid() && targetNode.parentProperty().isValid()) {
717             targetNode = targetNode.parentProperty().parentModelNode();
718         }
719
720         if (!targetNode.isValid())
721             targetNode = view.rootModelNode();
722
723         foreach (const ModelNode &node, selectedNodes) {
724             foreach (const ModelNode &node2, selectedNodes) {
725                 if (node.isAncestorOf(node2))
726                     selectedNodes.removeAll(node2);
727             }
728         }
729
730         QList<ModelNode> pastedNodeList;
731
732         try {
733             RewriterTransaction transaction(d->formEditorView.data());
734
735             int offset = double(qrand()) / RAND_MAX * 20 - 10;
736
737             foreach (const ModelNode &node, selectedNodes) {
738                 QString defaultProperty(targetNode.metaInfo().defaultPropertyName());
739                 ModelNode pastedNode(view.insertModel(node));
740                 pastedNodeList.append(pastedNode);
741                 scatterItem(pastedNode, targetNode, offset);
742                 targetNode.nodeListProperty(defaultProperty).reparentHere(pastedNode);
743             }
744
745             view.setSelectedModelNodes(pastedNodeList);
746         } catch (RewritingException &e) { 
747             qWarning() << e.description(); //silent error
748         }
749     } else {
750         try {
751             RewriterTransaction transaction(d->formEditorView.data());
752
753             model->detachView(&view);
754             d->model->attachView(&view);
755             ModelNode pastedNode(view.insertModel(rootNode));
756             ModelNode targetNode;
757
758             if (!view.selectedModelNodes().isEmpty())
759                 targetNode = view.selectedModelNodes().first();
760
761             if (!targetNode.isValid())
762                 targetNode = view.rootModelNode();
763
764             if (targetNode.parentProperty().isValid() &&
765                 (pastedNode.simplifiedTypeName() == targetNode.simplifiedTypeName()) &&
766                 (pastedNode.variantProperty("width").value() == targetNode.variantProperty("width").value()) &&
767                 (pastedNode.variantProperty("height").value() == targetNode.variantProperty("height").value()))
768
769                 targetNode = targetNode.parentProperty().parentModelNode();
770
771             QString defaultProperty(targetNode.metaInfo().defaultPropertyName());
772
773             scatterItem(pastedNode, targetNode);
774             if (targetNode.nodeListProperty(defaultProperty).isValid()) {
775                 targetNode.nodeListProperty(defaultProperty).reparentHere(pastedNode);
776             }
777
778             transaction.commit();
779             NodeMetaInfo::clearCache();
780
781             view.setSelectedModelNodes(QList<ModelNode>() << pastedNode);
782         } catch (RewritingException &e) { 
783             qWarning() << e.description(); //silent error
784         }
785     }
786 }
787
788 void DesignDocumentController::selectAll()
789 {
790     if (!d->model)
791         return;
792
793     DesignDocumentControllerView view;
794     d->model->attachView(&view);
795
796
797     QList<ModelNode> allNodesExceptRootNode(view.allModelNodes());
798     allNodesExceptRootNode.removeOne(view.rootModelNode());
799     view.setSelectedModelNodes(allNodesExceptRootNode);
800 }
801
802 RewriterView *DesignDocumentController::rewriterView() const
803 {
804     return d->rewriterView.data();
805 }
806
807 void DesignDocumentController::undo()
808 {
809     if (d->rewriterView && !d->rewriterView->modificationGroupActive())
810         d->textEdit->undo();
811     d->propertyEditorView->resetView();
812 }
813
814 void DesignDocumentController::redo()
815 {
816     if (d->rewriterView && !d->rewriterView->modificationGroupActive())
817         d->textEdit->redo();
818     d->propertyEditorView->resetView();
819 }
820
821 static inline QtSupport::BaseQtVersion *getActiveQtVersion(DesignDocumentController *controller)
822 {
823     ProjectExplorer::ProjectExplorerPlugin *projectExplorer = ProjectExplorer::ProjectExplorerPlugin::instance();
824     ProjectExplorer::Project *currentProject = projectExplorer->currentProject();
825
826     if (!currentProject)
827         return 0;
828
829     controller->disconnect(controller,  SLOT(activeQtVersionChanged()));
830     controller->connect(projectExplorer, SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), controller, SLOT(activeQtVersionChanged()));
831
832     controller->connect(currentProject, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)), controller, SLOT(activeQtVersionChanged()));
833
834
835     ProjectExplorer::Target *target = currentProject->activeTarget();
836
837     if (!target)
838         return 0;
839
840     ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
841     QmlProjectManager::QmlProjectRunConfiguration *qmlRunConfiguration = qobject_cast<QmlProjectManager::QmlProjectRunConfiguration* >(runConfiguration);
842
843     if (qmlRunConfiguration) {
844         controller->connect(target, SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)), controller, SLOT(activeQtVersionChanged()));
845         return qmlRunConfiguration->qtVersion();
846     }
847
848     Qt4ProjectManager::Qt4BuildConfiguration *activeBuildConfiguration = qobject_cast<Qt4ProjectManager::Qt4BuildConfiguration *>(target->activeBuildConfiguration());
849
850     if (activeBuildConfiguration) {
851         controller->connect(target, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)), controller, SLOT(activeQtVersionChanged()));
852         return activeBuildConfiguration->qtVersion();
853     }
854
855     return 0;
856 }
857
858 void DesignDocumentController::activeQtVersionChanged()
859 {
860     QtSupport::BaseQtVersion *newQtVersion = getActiveQtVersion(this);
861
862     if (!newQtVersion ) {
863         d->qt_versionId = -1;
864         return;
865     }
866
867     if (d->qt_versionId == newQtVersion->uniqueId())
868         return;
869
870     d->qt_versionId = newQtVersion->uniqueId();
871
872     if (d->nodeInstanceView)
873         d->nodeInstanceView->setPathToQt(pathToQt());
874 }
875
876 #ifdef ENABLE_TEXT_VIEW
877 void DesignDocumentController::showText()
878 {
879     d->stackedWidget->setCurrentWidget(d->textEdit.data());
880 }
881 #endif // ENABLE_TEXT_VIEW
882
883 #ifdef ENABLE_TEXT_VIEW
884 void DesignDocumentController::showForm()
885 {
886     d->stackedWidget->setCurrentWidget(d->formEditorView->widget());
887 }
888 #endif // ENABLE_TEXT_VIEW
889
890 bool DesignDocumentController::save(QWidget *parent)
891 {
892     //    qDebug() << "Saving document to file \"" << d->fileName << "\"...";
893     //
894     if (d->fileName.isEmpty()) {
895         saveAs(parent);
896         return true;
897     }
898     Utils::FileSaver saver(d->fileName, QIODevice::Text);
899     if (d->model)
900         saver.write(d->textEdit->toPlainText().toLatin1());
901     if (!saver.finalize(parent ? parent : d->stackedWidget.data()))
902         return false;
903     if (d->model)
904         d->textEdit->setPlainText(d->textEdit->toPlainText()); // clear undo/redo history
905
906     return true;
907 }
908
909
910 QString DesignDocumentController::contextHelpId() const
911 {
912     DesignDocumentControllerView view;
913     d->model->attachView(&view);
914
915     QList<ModelNode> nodes = view.selectedModelNodes();
916     QString helpId;
917     if (!nodes.isEmpty()) {
918         helpId = nodes.first().type();
919         helpId.replace("QtQuick", "QML");
920     }
921
922     return helpId;
923 }
924
925 } // namespace QmlDesigner