navigatortreemodel.cpp 26 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29
30
31
32
33

#include "navigatortreemodel.h"

#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
34
#include <nodeproperty.h>
35
#include <variantproperty.h>
36
#include <metainfo.h>
Marco Bubke's avatar
Marco Bubke committed
37
#include <abstractview.h>
38
#include <rewriterview.h>
39
#include <invalididexception.h>
40
#include <rewritingexception.h>
41
#include <modelnodecontextmenu.h>
Marco Bubke's avatar
Marco Bubke committed
42
#include <qmlitemnode.h>
43

44
45
#include <coreplugin/icore.h>

46
#include <QMimeData>
47
#include <QMessageBox>
48
#include <QApplication>
49
50
#include <QPointF>

51
52
#include <QtDebug>

53
54
#define DISABLE_VISIBLE_PROPERTIES

55
56
namespace QmlDesigner {

57
#ifndef DISABLE_VISIBLE_PROPERTIES
58
59
60
61
62
63
64
65
66
67
68
69
70
static PropertyNameList visibleProperties(const ModelNode &node)
{
    PropertyNameList propertyList;

    foreach (const PropertyName &propertyName, node.metaInfo().propertyNames()) {
        if (!propertyName.contains('.') //do not show any dot properties, since they are tricky and unlikely to make sense
                && node.metaInfo().propertyIsWritable(propertyName)
                && propertyName != "parent"
                && node.metaInfo().propertyTypeName(propertyName) != TypeName("Component")
                && !node.metaInfo().propertyIsEnumType(propertyName) //Some enums have the same name as Qml types (e. g. Flow)
                && !node.metaInfo().propertyIsPrivate(propertyName) //Do not show private properties
                && propertyName != node.metaInfo().defaultPropertyName()) { // TODO: ask the node instances

71
            TypeName qmlType = node.metaInfo().propertyTypeName(propertyName);
72
73
74
75
76
77
78
79
80
            if (node.model()->metaInfo(qmlType).isValid() &&
                node.model()->metaInfo(qmlType).isSubclassOf("QtQuick.Item", -1, -1)) {
                propertyList.append(propertyName);
            }
        }
    }

    return propertyList;
}
81
#endif
82
83
84
85
86
87
88
89
90

static QList<ModelNode> acceptedModelNodeChildren(const ModelNode &parentNode)
{
    QList<ModelNode> children;
    PropertyNameList properties;

    if (parentNode.metaInfo().hasDefaultProperty())
        properties.append(parentNode.metaInfo().defaultPropertyName());

91
#ifndef DISABLE_VISIBLE_PROPERTIES
92
    properties.append(visibleProperties(parentNode));
93
#endif
94
95
96
97
98
99
100
101
102
103

    foreach (const PropertyName &propertyName, properties) {
        AbstractProperty property(parentNode.property(propertyName));
        if (property.isNodeAbstractProperty())
            children.append(property.toNodeAbstractProperty().directSubNodes());
    }

    return children;
}

104
NavigatorTreeModel::NavigatorTreeModel(QObject *parent)
105
106
    : QStandardItemModel(parent),
      m_blockItemChangedSignal(false)
107
{
108
    invisibleRootItem()->setFlags(Qt::ItemIsDropEnabled);
109

Tobias Hunger's avatar
Tobias Hunger committed
110
#    ifdef _LOCK_ITEMS_
111
    setColumnCount(3);
Tobias Hunger's avatar
Tobias Hunger committed
112
#    else
113
    setColumnCount(2);
Tobias Hunger's avatar
Tobias Hunger committed
114
#    endif
115
116
117
118
119
120
121
122
123
124
125

    connect(this, SIGNAL(itemChanged(QStandardItem*)),
            this, SLOT(handleChangedItem(QStandardItem*)));
}

NavigatorTreeModel::~NavigatorTreeModel()
{
}

Qt::DropActions NavigatorTreeModel::supportedDropActions() const
{
126
    return Qt::LinkAction | Qt::MoveAction;
127
128
}

129
130
131
132
133
Qt::DropActions NavigatorTreeModel::supportedDragActions() const
{
    return Qt::LinkAction;
}

134
135
136
QStringList NavigatorTreeModel::mimeTypes() const
{
     QStringList types;
137
     types.append("application/vnd.modelnode.list");
138
139
140
     types.append("application/vnd.bauhaus.itemlibraryinfo");
     types.append("application/vnd.bauhaus.libraryresource");

141
142
143
     return types;
}

144
QByteArray encodeModelNodes(const QModelIndexList &modelIndexList)
145
{
146
147
148
149
150
151
152
153
154
155
156
157
158
    QByteArray encodedModelNodeData;
    QDataStream encodedModelNodeDataStream(&encodedModelNodeData, QIODevice::WriteOnly);
    QSet<QModelIndex> rowAlreadyUsedSet;

    foreach (const QModelIndex &modelIndex, modelIndexList) {
        if (modelIndex.isValid()) {
            QModelIndex idModelIndex = modelIndex.sibling(modelIndex.row(), 0);
            if (!rowAlreadyUsedSet.contains(idModelIndex)) {
                rowAlreadyUsedSet.insert(idModelIndex);
                encodedModelNodeDataStream << idModelIndex.data(NavigatorTreeModel::InternalIdRole).toInt();
            }
        }
    }
159

160
161
    return encodedModelNodeData;
}
162

163
164
165
QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &modelIndexList) const
{
     QMimeData *mimeData = new QMimeData();
166

167
     QByteArray encodedModelNodeData = encodeModelNodes(modelIndexList);
168

169
     mimeData->setData("application/vnd.modelnode.list", encodedModelNodeData);
170
171
172
173

     return mimeData;
}

174
static QList<ModelNode> modelNodesFromMimeData(const QMimeData *mineData, AbstractView *view)
175
{
176
177
178
179
180
181
182
183
184
185
    QByteArray encodedModelNodeData = mineData->data("application/vnd.modelnode.list");
    QDataStream modelNodeStream(&encodedModelNodeData, QIODevice::ReadOnly);

    QList<ModelNode> modelNodeList;
    while (!modelNodeStream.atEnd()) {
        qint32 internalId;
        modelNodeStream >> internalId;
        if (view->hasModelNodeForInternalId(internalId))
            modelNodeList.append(view->modelNodeForInternalId(internalId));
    }
186

187
188
    return modelNodeList;
}
189

190
191
192
193
194
195
bool fitsToTargetProperty(const NodeAbstractProperty &targetProperty,
                          const QList<ModelNode> &modelNodeList)
{
    return !(targetProperty.isNodeProperty() &&
            modelNodeList.count() > 1);
}
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
static bool computeTarget(const QModelIndex &rowModelIndex,
                          NavigatorTreeModel *navigatorTreeModel,
                          NodeAbstractProperty  *targetProperty,
                          int *targetRowNumber)
{
    QModelIndex targetItemIndex;
    PropertyName targetPropertyName;

    if (*targetRowNumber < 0 || *targetRowNumber > navigatorTreeModel->rowCount(rowModelIndex))
        *targetRowNumber = navigatorTreeModel->rowCount(rowModelIndex);

    if (navigatorTreeModel->hasNodeForIndex(rowModelIndex)) {
        targetItemIndex = rowModelIndex;
        ModelNode targetNode = navigatorTreeModel->nodeForIndex(targetItemIndex);
        if (!targetNode.metaInfo().hasDefaultProperty())
212
            return false;
213
#ifndef DISABLE_VISIBLE_PROPERTIES
214
        *targetRowNumber -= visibleProperties(targetNode).count();
215
#endif
216
        targetPropertyName = targetNode.metaInfo().defaultPropertyName();
217
    } else {
218
219
        targetItemIndex = rowModelIndex.parent();
        targetPropertyName = rowModelIndex.data(Qt::DisplayRole).toByteArray();
220
    }
221

222
    // Disallow dropping items between properties, which are listed first.
223
    if (*targetRowNumber < 0)
224
225
        return false;

226
227
    ModelNode targetNode(navigatorTreeModel->nodeForIndex(targetItemIndex));
    *targetProperty = targetNode.nodeAbstractProperty(targetPropertyName);
228

229
230
    return true;
}
231

232
233


234
bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
235
236
                                      Qt::DropAction action,
                                      int rowNumber,
Marco Bubke's avatar
Marco Bubke committed
237
                                      int /*columnNumber*/,
238
                                      const QModelIndex &dropModelIndex)
239
240
241
{
    if (action == Qt::IgnoreAction)
        return true;
242

243
244
245
246
247
248
249
250
    if (dropModelIndex.model() == this) {
        if (mimeData->hasFormat("application/vnd.bauhaus.itemlibraryinfo")) {
            handleItemLibraryItemDrop(mimeData, rowNumber, dropModelIndex);
        } else if (mimeData->hasFormat("application/vnd.bauhaus.libraryresource")) {
            handleItemLibraryImageDrop(mimeData, rowNumber, dropModelIndex);
        } else if (mimeData->hasFormat("application/vnd.modelnode.list")) {
            handleInternalDrop(mimeData, rowNumber, dropModelIndex);
        }
251
    }
252

253
254
255
    return false; // don't let the view do drag&drop on its own
}

256
257
258
259
260
static inline QString msgUnknownItem(const QString &t)
{
    return NavigatorTreeModel::tr("Unknown item: %1").arg(t);
}

261
ItemRow NavigatorTreeModel::createItemRow(const ModelNode &node)
262
263
264
{
    Q_ASSERT(node.isValid());

265
266
    const bool dropEnabled = node.metaInfo().isValid();

267
268
    QStandardItem *idItem = new QStandardItem;
    idItem->setDragEnabled(true);
269
    idItem->setDropEnabled(dropEnabled);
270
    idItem->setEditable(true);
271
    idItem->setData(node.internalId(), InternalIdRole);
272
    if (node.metaInfo().isValid())
273
        idItem->setToolTip(node.type());
274
    else
275
        idItem->setToolTip(msgUnknownItem(node.type()));
Tobias Hunger's avatar
Tobias Hunger committed
276
#    ifdef _LOCK_ITEMS_
277
278
    QStandardItem *lockItem = new QStandardItem;
    lockItem->setDragEnabled(true);
279
    lockItem->setDropEnabled(dropEnabled);
280
281
    lockItem->setEditable(false);
    lockItem->setCheckable(true);
282
    lockItem->setData(hash, NavigatorRole);
Tobias Hunger's avatar
Tobias Hunger committed
283
#    endif
284
285

    QStandardItem *visibilityItem = new QStandardItem;
286
    visibilityItem->setDropEnabled(dropEnabled);
287
    visibilityItem->setCheckable(true);
288
    visibilityItem->setEditable(false);
289
    visibilityItem->setData(node.internalId(), InternalIdRole);
290
    if (node.isRootNode())
291
        visibilityItem->setCheckable(false);
292

293
    QMap<QString, QStandardItem *> propertyItems;
294
#ifndef DISABLE_VISIBLE_PROPERTIES
Friedemann Kleint's avatar
Friedemann Kleint committed
295
    foreach (const QString &propertyName, visibleProperties(node)) {
296
297
298
299
300
301
302
303
304
        QStandardItem *propertyItem = new QStandardItem;
        propertyItem->setSelectable(false);
        propertyItem->setDragEnabled(false);
        propertyItem->setDropEnabled(dropEnabled);
        propertyItem->setEditable(false);
        propertyItem->setData(propertyName, Qt::DisplayRole);
        propertyItems.insert(propertyName, propertyItem);
        idItem->appendRow(propertyItem);
    }
305
#endif
306
307

#   ifdef _LOCK_ITEMS_
308
    ItemRow newRow =  ItemRow(idItem, lockItem, visibilityItem, propertyItems);
309
#   else
310
    ItemRow newRow = ItemRow(idItem, visibilityItem, propertyItems);
311
#   endif
312
313
314
315
316

    m_nodeItemHash.insert(node, newRow);
    updateItemRow(node, newRow);

    return newRow;
317
318
319
320
}

void NavigatorTreeModel::updateItemRow(const ModelNode &node, ItemRow items)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
321
    bool blockSignal = blockItemChangedSignal(true);
322

323
324
    items.idItem->setText(node.id());
    items.visibilityItem->setCheckState(node.auxiliaryData("invisible").toBool() ? Qt::Unchecked : Qt::Checked);
325
    if (node.metaInfo().isValid())
326
        items.idItem->setToolTip(node.type());
327
    else
328
        items.idItem->setToolTip(msgUnknownItem(node.type()));
329
330

    blockItemChangedSignal(blockSignal);
331
332
333
334
335
336
337
}

/**
  Update the information shown for a node / property
  */
void NavigatorTreeModel::updateItemRow(const ModelNode &node)
{
338
    if (!isInTree(node))
339
        return;
340

341
342
343
    updateItemRow(node, itemRowForNode(node));
}

344
static void handleWrongId(QStandardItem *item, const ModelNode &modelNode, const QString &errorTitle, const QString &errorMessage, NavigatorTreeModel *treeModel)
345
{
346
347
348
349
350
    QMessageBox::warning(Core::ICore::dialogParent(), errorTitle,  errorMessage);
    bool blockSingals = treeModel->blockItemChangedSignal(true);
    item->setText(modelNode.id());
    treeModel->blockItemChangedSignal(blockSingals);
}
351

352

353
354
355
356
357
358
359
360
361
362
363
void NavigatorTreeModel::handleChangedIdItem(QStandardItem *idItem, ModelNode &modelNode)
{
    const QString newId = idItem->text();
    if (!modelNode.isValidId(newId)) {
        handleWrongId(idItem, modelNode, tr("Invalid Id"), tr("%1 is an invalid id.").arg(newId), this);
    } else if (modelNode.view()->hasId(newId)) {
        handleWrongId(idItem, modelNode, tr("Invalid Id"), tr("%1 already exists.").arg(newId), this);
    } else  {
        modelNode.setIdWithRefactoring(newId);
    }
}
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
void NavigatorTreeModel::handleChangedVisibilityItem(QStandardItem *visibilityItem, ModelNode &modelNode)
{
    bool invisible = (visibilityItem->checkState() == Qt::Unchecked);

    if (invisible)
        modelNode.setAuxiliaryData("invisible", invisible);
    else
        modelNode.removeAuxiliaryData("invisible");
}

void NavigatorTreeModel::handleChangedItem(QStandardItem *item)
{
    QVariant internalIdVariant = data(item->index(), InternalIdRole);
    if (!m_blockItemChangedSignal && internalIdVariant.isValid()) {
        ModelNode modelNode = m_view->modelNodeForInternalId(internalIdVariant.toInt());
        ItemRow itemRow = itemRowForNode(modelNode);
        if (item == itemRow.idItem) {
            handleChangedIdItem(item, modelNode);
        } else if (item == itemRow.visibilityItem) {
            handleChangedVisibilityItem(item, modelNode);
        }
386
387
388
    }
}

389
ItemRow NavigatorTreeModel::itemRowForNode(const ModelNode &node)
390
391
392
393
394
{
    Q_ASSERT(node.isValid());
    return m_nodeItemHash.value(node);
}

Marco Bubke's avatar
Marco Bubke committed
395
void NavigatorTreeModel::setView(AbstractView *view)
396
397
{
    m_view = view;
398
    if (view)
399
        addSubTree(view->rootModelNode());
400
401
402
403
}

void NavigatorTreeModel::clearView()
{
404
    setView(0);
405
406
407
408
409
410
411
    m_view.clear();
    m_nodeItemHash.clear();
}

QModelIndex NavigatorTreeModel::indexForNode(const ModelNode &node) const
{
    Q_ASSERT(node.isValid());
412
    if (!isInTree(node))
413
414
415
416
417
418
419
        return QModelIndex();
    ItemRow row = m_nodeItemHash.value(node);
    return row.idItem->index();
}

ModelNode NavigatorTreeModel::nodeForIndex(const QModelIndex &index) const
{
420
    qint32 internalId = index.data(InternalIdRole).toInt();
421
    return m_view->modelNodeForInternalId(internalId);
422
423
}

424
425
bool NavigatorTreeModel::hasNodeForIndex(const QModelIndex &index) const
{
Marco Bubke's avatar
Marco Bubke committed
426
427
428
    QVariant internalIdVariant = index.data(InternalIdRole);
    if (internalIdVariant.isValid()) {
        qint32 internalId = internalIdVariant.toInt();
429
430
431
432
433
434
        return m_view->hasModelNodeForInternalId(internalId);
    }

    return false;
}

435
436
bool NavigatorTreeModel::isInTree(const ModelNode &node) const
{
437
    return m_nodeItemHash.contains(node);
438
439
}

440
441
bool NavigatorTreeModel::isNodeInvisible(const QModelIndex &index) const
{
442
443
444
    return isNodeInvisible(nodeForIndex(index));
}

445
446
447
448
449
450
451
452
453
454
455
456
static bool isInvisbleInHierachy(const ModelNode &modelNode)
{
    if (modelNode.auxiliaryData("invisible").toBool())
        return true;

    if (modelNode.hasParentProperty())
        return isInvisbleInHierachy(modelNode.parentProperty().parentModelNode());

    return false;
}

bool NavigatorTreeModel::isNodeInvisible(const ModelNode &modelNode) const
457
{
458
    return isInvisbleInHierachy(modelNode);
459
460
}

461
462
463
464
static bool isRootNodeOrAcceptedChild(const ModelNode &modelNode)
{
    return modelNode.isRootNode() || acceptedModelNodeChildren(modelNode.parentProperty().parentModelNode()).contains(modelNode);
}
465

466
static bool nodeCanBeHandled(const ModelNode &modelNode)
467
{
468
469
470
471
472
473
    return modelNode.metaInfo().isGraphicalItem() && isRootNodeOrAcceptedChild(modelNode);
}

static void appendNodeToEndOfTheRow(const ModelNode &modelNode, const ItemRow &newItemRow, NavigatorTreeModel *treeModel)
{
    if (modelNode.hasParentProperty()) {
474
475
476
477
478
479
480
481
        NodeAbstractProperty parentProperty(modelNode.parentProperty());
        ItemRow parentRow = treeModel->itemRowForNode(parentProperty.parentModelNode());
        if (parentRow.propertyItems.contains(parentProperty.name())) {
            QStandardItem *parentPropertyItem = parentRow.propertyItems.value(parentProperty.name());
            parentPropertyItem->appendRow(newItemRow.toList());
        } else {
            QStandardItem *parentDefaultPropertyItem = parentRow.idItem;
            parentDefaultPropertyItem->appendRow(newItemRow.toList());
482
        }
483
    } else { // root node
484
        treeModel->appendRow(newItemRow.toList());
485
486
    }
}
487
488

void NavigatorTreeModel::addSubTree(const ModelNode &modelNode)
489
{
490
491
492
    if (nodeCanBeHandled(modelNode)) {

        ItemRow newItemRow = createItemRow(modelNode);
493

494
495
496
497
498
499
500
        foreach (const ModelNode &childNode, acceptedModelNodeChildren(modelNode))
            addSubTree(childNode);

        appendNodeToEndOfTheRow(modelNode, newItemRow, this);
    }
}

501
502
503
504
static QList<QStandardItem*> takeWholeRow(const ItemRow &itemRow)
{
    if (itemRow.idItem->parent())
        return  itemRow.idItem->parent()->takeRow(itemRow.idItem->row());
505
    else if (itemRow.idItem->model())
506
        return itemRow.idItem->model()->takeRow(itemRow.idItem->row());
507
508
    else
        return itemRow.toList();
509
510
}

511
512
void NavigatorTreeModel::removeSubTree(const ModelNode &node)
{
513
514
    if (isInTree(node)) {
        ItemRow itemRow = itemRowForNode(node);
515

516
        QList<QStandardItem*> rowList = takeWholeRow(itemRow);
517

518
519
        foreach (const ModelNode &childNode, acceptedModelNodeChildren(node))
            removeSubTree(childNode);
520

521
522
        qDeleteAll(rowList);
        m_nodeItemHash.remove(node);
523
524
525
526
    }

}

527
528
529
530
531
532
533
534
535
static void removePosition(const ModelNode &node)
{
    ModelNode modelNode = node;
    if (modelNode.hasProperty("x"))
        modelNode.removeProperty("x");
    if (modelNode.hasProperty("y"))
        modelNode.removeProperty("y");
}

536
537
538
539
540
541
542
543
544
545
static void setScenePosition(const QmlDesigner::ModelNode &modelNode,const QPointF &positionInSceneSpace)
{
    if (modelNode.hasParentProperty() && QmlDesigner::QmlItemNode::isValidQmlItemNode(modelNode.parentProperty().parentModelNode())) {
        QmlDesigner::QmlItemNode parentNode = modelNode.parentProperty().parentQmlObjectNode().toQmlItemNode();
        QPointF positionInLocalSpace = parentNode.instanceSceneContentItemTransform().inverted().map(positionInSceneSpace);
        modelNode.variantProperty("x").setValue(positionInLocalSpace.toPoint().x());
        modelNode.variantProperty("y").setValue(positionInLocalSpace.toPoint().y());
    }
}

546
547
548
549
static bool removeModelNodeFromNodeProperty(NodeAbstractProperty &parentProperty, const ModelNode &modelNode)
{

    if (parentProperty.isNodeProperty()) {
550
        bool removeNodeInPropertySucceeded = false;
551
552
        ModelNode propertyNode = parentProperty.toNodeProperty().modelNode();
        // Destruction of ancestors is not allowed
553
        if (modelNode != propertyNode && !propertyNode.isAncestorOf(modelNode)) {
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
            QApplication::setOverrideCursor(Qt::ArrowCursor);

            QMessageBox::StandardButton selectedButton = QMessageBox::warning(Core::ICore::dialogParent(),
                                                                              QCoreApplication::translate("NavigatorTreeModel", "Warning"),
                                                                              QCoreApplication::translate("NavigatorTreeModel","Reparenting the component %1 here will cause the "
                                                                                                          "component %2 to be deleted. Do you want to proceed?")
                                                                              .arg(modelNode.id(), propertyNode.id()),
                                                                              QMessageBox::Ok | QMessageBox::Cancel);
            if (selectedButton == QMessageBox::Ok) {
                propertyNode.destroy();
                removeNodeInPropertySucceeded = true;
            }

            QApplication::restoreOverrideCursor();
        }

        return removeNodeInPropertySucceeded;
    }

    return true;
}

static void slideModelNodeInList(NodeAbstractProperty &parentProperty, const ModelNode &modelNode, int targetIndex)
{
    if (parentProperty.isNodeListProperty()) {
        int index = parentProperty.indexOf(modelNode);
        if (index < targetIndex) { // item is first removed from oldIndex, then inserted at new index
            --targetIndex;
        }
        if (index != targetIndex)
            parentProperty.toNodeListProperty().slide(index, targetIndex);
    }
}

static bool isInLayoutable(NodeAbstractProperty &parentProperty)
{
    return parentProperty.isDefaultProperty() && parentProperty.parentModelNode().metaInfo().isLayoutable();
}

static void reparentModelNodeToNodeProperty(NodeAbstractProperty &parentProperty, const ModelNode &modelNode)
{
595
    if (!modelNode.hasParentProperty() || parentProperty != modelNode.parentProperty()) {
596
597
        if (isInLayoutable(parentProperty)) {
            removePosition(modelNode);
598
599
            parentProperty.reparentHere(modelNode);
        } else {
600
601
602
603
604
605
606
607
            if (QmlItemNode::isValidQmlItemNode(modelNode)) {
                QPointF scenePosition = QmlItemNode(modelNode).instanceScenePosition();
                parentProperty.reparentHere(modelNode);
                if (!scenePosition.isNull())
                    setScenePosition(modelNode, scenePosition);
            } else {
                parentProperty.reparentHere(modelNode);
            }
608
609
610
611
612
        }
    }
}

void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty &parentProperty, const QList<ModelNode> &modelNodes, int targetIndex)
613
{
614
    try {
615
        TypeName propertyQmlType = parentProperty.parentModelNode().metaInfo().propertyTypeName(parentProperty.name());
616

617
        RewriterTransaction transaction = m_view->beginRewriterTransaction(QByteArrayLiteral("NavigatorTreeModel::moveNodesInteractive"));
618
619
620
621
622
623
624
625
626
627
628
629
        foreach (const ModelNode &modelNode, modelNodes) {
            if (modelNode.isValid()
                    && modelNode != parentProperty.parentModelNode()
                    && !modelNode.isAncestorOf(parentProperty.parentModelNode())
                    && (modelNode.metaInfo().isSubclassOf(propertyQmlType, -1, -1) || propertyQmlType == "alias")) {
                //### todo: allowing alias is just a heuristic
                //once the MetaInfo is part of instances we can do this right

                bool nodeCanBeMovedToParentProperty = removeModelNodeFromNodeProperty(parentProperty, modelNode);

                if (nodeCanBeMovedToParentProperty) {
                    reparentModelNodeToNodeProperty(parentProperty, modelNode);
630
                    slideModelNodeInList(parentProperty, modelNode, targetIndex);
631
                }
632
633
            }
        }
634
635
    }  catch (RewritingException &exception) { //better safe than sorry! There always might be cases where we fail
        exception.showException();
636
    }
637
638
}

639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
void NavigatorTreeModel::handleInternalDrop(const QMimeData *mimeData,
                                            int rowNumber,
                                            const QModelIndex &dropModelIndex)
{
    QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
    int targetRowNumber = rowNumber;
    NodeAbstractProperty targetProperty;

    bool foundTarget = computeTarget(rowModelIndex, this, &targetProperty, &targetRowNumber);

    if (foundTarget) {
        QList<ModelNode> modelNodeList = modelNodesFromMimeData(mimeData, m_view);

        if (fitsToTargetProperty(targetProperty, modelNodeList))
            moveNodesInteractive(targetProperty, modelNodeList, targetRowNumber);
    }
}

static ItemLibraryEntry itemLibraryEntryFromData(const QByteArray &data)
{
    QDataStream stream(data);

    ItemLibraryEntry itemLibraryEntry;
    stream >> itemLibraryEntry;

    return itemLibraryEntry;
}

void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex)
{
669
670
671
672
673
    QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
    int targetRowNumber = rowNumber;
    NodeAbstractProperty targetProperty;

    bool foundTarget = computeTarget(rowModelIndex, this, &targetProperty, &targetRowNumber);
674

675
676
    if (foundTarget) {
        ItemLibraryEntry itemLibraryEntry = itemLibraryEntryFromData(mimeData->data("application/vnd.bauhaus.itemlibraryinfo"));
677
        QmlItemNode newQmlItemNode = QmlItemNode::createQmlItemNode(m_view, itemLibraryEntry, QPointF(), targetProperty);
678

679
        if (newQmlItemNode.isValid() && targetProperty.isNodeListProperty()) {
680
681
682
683
684
685
            QList<ModelNode> newModelNodeList;
            newModelNodeList.append(newQmlItemNode);

            moveNodesInteractive(targetProperty, newModelNodeList, targetRowNumber);
        }
    }
686
687
688
689
}

void NavigatorTreeModel::handleItemLibraryImageDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex)
{
690
691
692
693
694
695
696
697
    QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0);
    int targetRowNumber = rowNumber;
    NodeAbstractProperty targetProperty;

    bool foundTarget = computeTarget(rowModelIndex, this, &targetProperty, &targetRowNumber);

    if (foundTarget) {
        QString imageFileName = QString::fromUtf8(mimeData->data("application/vnd.bauhaus.libraryresource"));
698
        QmlItemNode newQmlItemNode = QmlItemNode::createQmlItemNodeFromImage(m_view, imageFileName, QPointF(), targetProperty);
699

700
701
702
703
704
705
706
        if (newQmlItemNode.isValid()) {
            QList<ModelNode> newModelNodeList;
            newModelNodeList.append(newQmlItemNode);

            moveNodesInteractive(targetProperty, newModelNodeList, targetRowNumber);
        }
    }
707
708
}

709
710
711
712
713
714
715
716
// along the lines of QObject::blockSignals
bool NavigatorTreeModel::blockItemChangedSignal(bool block)
{
    bool oldValue = m_blockItemChangedSignal;
    m_blockItemChangedSignal = block;
    return oldValue;
}

717
718
719
720
721
722
723
724
725
726
727
728
729
730
void NavigatorTreeModel::setId(const QModelIndex &index, const QString &id)
{
    ModelNode node = nodeForIndex(index);
    ItemRow itemRow = itemRowForNode(node);
    itemRow.idItem->setText(id);
}

void NavigatorTreeModel::setVisible(const QModelIndex &index, bool visible)
{
    ModelNode node = nodeForIndex(index);
    ItemRow itemRow = itemRowForNode(node);
    itemRow.visibilityItem->setCheckState(visible ? Qt::Checked : Qt::Unchecked);
}

731
void NavigatorTreeModel::openContextMenu(const QPoint &position)
732
{
733
    ModelNodeContextMenu::showContextMenu(m_view.data(), position, QPoint(), false);
734
735
}

736
} // QmlDesigner