navigatorview.cpp 16.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
18 19 20 21 22
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
25 26 27 28

#include "navigatorview.h"
#include "navigatortreemodel.h"
#include "navigatorwidget.h"
29 30
#include "qmldesignerconstants.h"
#include "qmldesignericons.h"
31

32 33
#include "nameitemdelegate.h"
#include "iconcheckboxitemdelegate.h"
34

35
#include <bindingproperty.h>
36
#include <designmodecontext.h>
37
#include <nodeproperty.h>
38
#include <nodelistproperty.h>
39
#include <variantproperty.h>
Marco Bubke's avatar
Marco Bubke committed
40
#include <qmlitemnode.h>
41
#include <rewritingexception.h>
42

43 44 45 46 47 48 49 50 51
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>

#include <utils/icon.h>
#include <utils/utilsicons.h>

#include <QHeaderView>


52 53
static inline void setScenePos(const QmlDesigner::ModelNode &modelNode,const QPointF &pos)
{
54 55
    if (modelNode.hasParentProperty() && QmlDesigner::QmlItemNode::isValidQmlItemNode(modelNode.parentProperty().parentModelNode())) {
        QmlDesigner::QmlItemNode parentNode = modelNode.parentProperty().parentQmlObjectNode().toQmlItemNode();
56 57 58 59 60 61 62 63 64

        if (!parentNode.modelNode().metaInfo().isLayoutable()) {
            QPointF localPos = parentNode.instanceSceneTransform().inverted().map(pos);
            modelNode.variantProperty("x").setValue(localPos.toPoint().x());
            modelNode.variantProperty("y").setValue(localPos.toPoint().y());
        } else { //Items in Layouts do not have a position
            modelNode.removeProperty("x");
            modelNode.removeProperty("y");
        }
65 66
    }
}
67 68 69 70

namespace QmlDesigner {

NavigatorView::NavigatorView(QObject* parent) :
71 72 73 74
    AbstractView(parent),
    m_blockSelectionChangedSignal(false),
    m_widget(new NavigatorWidget(this)),
    m_treeModel(new NavigatorTreeModel(this))
75
{
76
#ifndef QMLDESIGNER_TEST
77 78
    Internal::NavigatorContext *navigatorContext = new Internal::NavigatorContext(m_widget.data());
    Core::ICore::addContextObject(navigatorContext);
79
#endif
80

81
    m_treeModel->setView(this);
82 83
    m_widget->setTreeModel(m_treeModel.data());

84
    connect(treeWidget()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &NavigatorView::changeSelection);
85

86 87 88 89
    connect(m_widget.data(), &NavigatorWidget::leftButtonClicked, this, &NavigatorView::leftButtonClicked);
    connect(m_widget.data(), &NavigatorWidget::rightButtonClicked, this, &NavigatorView::rightButtonClicked);
    connect(m_widget.data(), &NavigatorWidget::downButtonClicked, this, &NavigatorView::downButtonClicked);
    connect(m_widget.data(), &NavigatorWidget::upButtonClicked, this, &NavigatorView::upButtonClicked);
90

91 92
#ifndef QMLDESIGNER_TEST
    NameItemDelegate *idDelegate = new NameItemDelegate(this,  m_treeModel.data());
93 94
    IconCheckboxItemDelegate *showDelegate =
            new IconCheckboxItemDelegate(this,
Ulf Hermann's avatar
Ulf Hermann committed
95
                                         Utils::Icons::EYE_OPEN_TOOLBAR.pixmap(),
96
                                         Utils::Icons::EYE_CLOSED_TOOLBAR.pixmap());
97 98 99

    IconCheckboxItemDelegate *exportDelegate =
            new IconCheckboxItemDelegate(this,
100
                                         Icons::EXPORT_CHECKED.pixmap(),
101
                                         Icons::EXPORT_UNCHECKED.pixmap());
102 103 104

#ifdef _LOCK_ITEMS_
    IconCheckboxItemDelegate *lockDelegate = new IconCheckboxItemDelegate(this,":/qmldesigner/images/lock.png",
105
                                                                          ":/qmldesigner/images/hole.png",m_treeModel.data());
106 107 108
#endif


109
    treeWidget()->setItemDelegateForColumn(0, idDelegate);
110 111 112 113
#ifdef _LOCK_ITEMS_
    treeWidget()->setItemDelegateForColumn(1,lockDelegate);
    treeWidget()->setItemDelegateForColumn(2,showDelegate);
#else
114 115
    treeWidget()->setItemDelegateForColumn(1, exportDelegate);
    treeWidget()->setItemDelegateForColumn(2, showDelegate);
116 117
#endif

118
#endif //QMLDESIGNER_TEST
119 120 121 122 123 124 125 126
}

NavigatorView::~NavigatorView()
{
    if (m_widget && !m_widget->parent())
        delete m_widget.data();
}

127 128 129 130 131 132 133
bool NavigatorView::hasWidget() const
{
    return true;
}

WidgetInfo NavigatorView::widgetInfo()
{
134 135
    return createWidgetInfo(m_widget.data(),
                            new WidgetInfo::ToolBarWidgetDefaultFactory<NavigatorWidget>(m_widget.data()),
136
                            QStringLiteral("Navigator"),
137 138
                            WidgetInfo::LeftPane,
                            0);
139 140
}

141 142
void NavigatorView::modelAttached(Model *model)
{
Marco Bubke's avatar
Marco Bubke committed
143
    AbstractView::modelAttached(model);
144

145 146
    QTreeView *treeView = treeWidget();
    treeView->expandAll();
147

148
    treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
149 150
    treeView->header()->resizeSection(1,26);
    treeView->setIndentation(20);
151
#ifdef _LOCK_ITEMS_
152
    treeView->header()->resizeSection(2,20);
153
#endif
154 155 156 157
}

void NavigatorView::modelAboutToBeDetached(Model *model)
{
Marco Bubke's avatar
Marco Bubke committed
158
    AbstractView::modelAboutToBeDetached(model);
159 160
}

161
void NavigatorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/)
162 163 164 165
{
    treeWidget()->update();
}

166 167
void NavigatorView::bindingPropertiesChanged(const QList<BindingProperty> & propertyList, PropertyChangeFlags /*propertyChange*/)
{
168
    for (const BindingProperty &bindingProperty : propertyList) {
169 170 171
        /* If a binding property that exports an item using an alias property has
         * changed, we have to update the affected item.
         */
172

173
        if (bindingProperty.isAliasExport())
174
            m_treeModel->notifyDataChanged(modelNodeForId(bindingProperty.expression()));
175 176 177
    }
}

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
void NavigatorView::handleChangedExport(const ModelNode &modelNode, bool exported)
{
    const ModelNode rootNode = rootModelNode();
    Q_ASSERT(rootNode.isValid());
    const PropertyName modelNodeId = modelNode.id().toUtf8();
    if (rootNode.hasProperty(modelNodeId))
        rootNode.removeProperty(modelNodeId);
    if (exported) {
        try {
            RewriterTransaction transaction =
                    beginRewriterTransaction(QByteArrayLiteral("NavigatorTreeModel:exportItem"));

            QmlObjectNode qmlObjectNode(modelNode);
            qmlObjectNode.ensureAliasExport();
            transaction.commit();
        }  catch (RewritingException &exception) { //better safe than sorry! There always might be cases where we fail
            exception.showException();
        }
    }
}

199 200 201 202 203
bool NavigatorView::isNodeVisible(const ModelNode &modelNode) const
{
     return modelNode.auxiliaryData("invisible").toBool();
}

204
void NavigatorView::nodeAboutToBeRemoved(const ModelNode & /*removedNode*/)
205 206 207
{
}

208 209 210
void NavigatorView::nodeRemoved(const ModelNode &removedNode,
                                const NodeAbstractProperty & /*parentProperty*/,
                                AbstractView::PropertyChangeFlags /*propertyChange*/)
211
{
212 213
    m_treeModel->notifyModelNodesRemoved({removedNode});
}
214

215 216 217 218 219 220 221 222 223 224
void NavigatorView::nodeReparented(const ModelNode &modelNode,
                                   const NodeAbstractProperty & /*newPropertyParent*/,
                                   const NodeAbstractProperty & oldPropertyParent,
                                   AbstractView::PropertyChangeFlags /*propertyChange*/)
{
    if (!oldPropertyParent.isValid())
        m_treeModel->notifyModelNodesInserted({modelNode});
    else
        m_treeModel->notifyModelNodesMoved({modelNode});
    treeWidget()->expand(m_treeModel->indexForModelNode(modelNode));
225 226
}

227
void NavigatorView::nodeIdChanged(const ModelNode& modelNode, const QString & /*newId*/, const QString & /*oldId*/)
228
{
229
    m_treeModel->notifyDataChanged(modelNode);
230 231
}

232
void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& /*propertyList*/)
233 234 235
{
}

236 237
void NavigatorView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
{
238
    QList<ModelNode> modelNodes;
239
    for (const AbstractProperty &property : propertyList) {
240 241 242
        if (property.isNodeAbstractProperty()) {
            NodeAbstractProperty nodeAbstractProperty(property.toNodeListProperty());
            modelNodes.append(nodeAbstractProperty.directSubNodes());
243 244
        }
    }
245 246

    m_treeModel->notifyModelNodesRemoved(modelNodes);
247 248
}

249
void NavigatorView::rootNodeTypeChanged(const QString & /*type*/, int /*majorVersion*/, int /*minorVersion*/)
250
{
251
    m_treeModel->notifyDataChanged(rootModelNode());
252 253
}

254
void NavigatorView::nodeTypeChanged(const ModelNode &modelNode, const TypeName &, int , int)
255
{
256
    m_treeModel->notifyDataChanged(modelNode);
257 258
}

259 260 261
void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode,
                                         const PropertyName & /*name*/,
                                         const QVariant & /*data*/)
262
{
263
    m_treeModel->notifyDataChanged(modelNode);
264 265
}

266
void NavigatorView::instanceErrorChanged(const QVector<ModelNode> &errorNodeList)
267
{
268 269
    foreach (const ModelNode &modelNode, errorNodeList)
        m_treeModel->notifyDataChanged(modelNode);
270 271
}

272 273 274
void NavigatorView::nodeOrderChanged(const NodeListProperty & listProperty,
                                     const ModelNode & /*node*/,
                                     int /*oldIndex*/)
275
{
276
    bool blocked = blockSelectionChangedSignal(true);
277

278
    m_treeModel->notifyModelNodesMoved(listProperty.directSubNodes());
279 280 281 282 283

    // make sure selection is in sync again
    updateItemSelection();

    blockSelectionChangedSignal(blocked);
284 285
}

286 287 288
void NavigatorView::changeToComponent(const QModelIndex &index)
{
    if (index.isValid() && m_treeModel->data(index, Qt::UserRole).isValid()) {
289
        const ModelNode doubleClickNode = m_treeModel->modelNodeForIndex(index);
Marco Bubke's avatar
Marco Bubke committed
290
        if (doubleClickNode.metaInfo().isFileComponent())
Eike Ziller's avatar
Eike Ziller committed
291 292
            Core::EditorManager::openEditor(doubleClickNode.metaInfo().componentFileName(),
                                            Core::Id(), Core::EditorManager::DoNotMakeVisible);
293 294 295
    }
}

296 297 298 299 300 301 302 303
void NavigatorView::leftButtonClicked()
{
    if (selectedModelNodes().count() > 1)
        return; //Semantics are unclear for multi selection.

    bool blocked = blockSelectionChangedSignal(true);

    foreach (const ModelNode &node, selectedModelNodes()) {
304
        if (!node.isRootNode() && !node.parentProperty().parentModelNode().isRootNode()) {
305
            if (QmlItemNode::isValidQmlItemNode(node)) {
306
                QPointF scenePos = QmlItemNode(node).instanceScenePosition();
307
                node.parentProperty().parentProperty().reparentHere(node);
308 309 310
                if (!scenePos.isNull())
                    setScenePos(node, scenePos);
            } else {
311
                node.parentProperty().parentProperty().reparentHere(node);
312 313
            }
        }
314
    }
315

316 317 318 319 320 321 322 323 324 325 326
    updateItemSelection();
    blockSelectionChangedSignal(blocked);
}

void NavigatorView::rightButtonClicked()
{
    if (selectedModelNodes().count() > 1)
        return; //Semantics are unclear for multi selection.

    bool blocked = blockSelectionChangedSignal(true);
    foreach (const ModelNode &node, selectedModelNodes()) {
327 328
        if (!node.isRootNode() && node.parentProperty().isNodeListProperty() && node.parentProperty().count() > 1) {
            int index = node.parentProperty().indexOf(node);
329 330 331
            index--;
            if (index >= 0) { //for the first node the semantics are not clear enough. Wrapping would be irritating.
                ModelNode newParent = node.parentProperty().toNodeListProperty().at(index);
332

333 334 335
                if (QmlItemNode::isValidQmlItemNode(node)
                        && QmlItemNode::isValidQmlItemNode(newParent)
                        && !newParent.metaInfo().defaultPropertyIsComponent()) {
336 337 338 339 340
                    QPointF scenePos = QmlItemNode(node).instanceScenePosition();
                    newParent.nodeAbstractProperty(newParent.metaInfo().defaultPropertyName()).reparentHere(node);
                    if (!scenePos.isNull())
                        setScenePos(node, scenePos);
                } else {
341 342
                    if (newParent.metaInfo().isValid() && !newParent.metaInfo().defaultPropertyIsComponent())
                        newParent.nodeAbstractProperty(newParent.metaInfo().defaultPropertyName()).reparentHere(node);
343
                }
344 345 346 347 348 349 350 351 352 353 354 355
            }
        }
    }
    updateItemSelection();
    blockSelectionChangedSignal(blocked);
}

void NavigatorView::upButtonClicked()
{
    bool blocked = blockSelectionChangedSignal(true);
    foreach (const ModelNode &node, selectedModelNodes()) {
        if (!node.isRootNode() && node.parentProperty().isNodeListProperty()) {
356
            int oldIndex = node.parentProperty().indexOf(node);
357 358 359
            int index = oldIndex;
            index--;
            if (index < 0)
360
                index = node.parentProperty().count() - 1; //wrap around
361 362 363 364 365 366 367 368 369 370 371 372
            node.parentProperty().toNodeListProperty().slide(oldIndex, index);
        }
    }
    updateItemSelection();
    blockSelectionChangedSignal(blocked);
}

void NavigatorView::downButtonClicked()
{
    bool blocked = blockSelectionChangedSignal(true);
    foreach (const ModelNode &node, selectedModelNodes()) {
        if (!node.isRootNode() && node.parentProperty().isNodeListProperty()) {
373
            int oldIndex = node.parentProperty().indexOf(node);
374 375
            int index = oldIndex;
            index++;
376
            if (index >= node.parentProperty().count())
377 378 379 380 381 382 383 384
                index = 0; //wrap around
            node.parentProperty().toNodeListProperty().slide(oldIndex, index);
        }
    }
    updateItemSelection();
    blockSelectionChangedSignal(blocked);
}

385 386 387 388
void NavigatorView::changeSelection(const QItemSelection & /*newSelection*/, const QItemSelection &/*deselected*/)
{
    if (m_blockSelectionChangedSignal)
        return;
389

390
    QSet<ModelNode> nodeSet;
391 392 393

    foreach (const QModelIndex &index, treeWidget()->selectionModel()->selectedIndexes())
        nodeSet.insert(m_treeModel->modelNodeForIndex(index));
394 395 396 397 398 399

    bool blocked = blockSelectionChangedSignal(true);
    setSelectedModelNodes(nodeSet.toList());
    blockSelectionChangedSignal(blocked);
}

400
void NavigatorView::selectedNodesChanged(const QList<ModelNode> &/*selectedNodeList*/, const QList<ModelNode> &/*lastSelectedNodeList*/)
401 402 403 404 405 406 407 408
{
    updateItemSelection();
}

void NavigatorView::updateItemSelection()
{
    QItemSelection itemSelection;
    foreach (const ModelNode &node, selectedModelNodes()) {
409
        const QModelIndex index = m_treeModel->indexForModelNode(node);
410 411 412 413 414 415 416 417 418 419 420 421
        if (index.isValid()) {
            const QModelIndex beginIndex(m_treeModel->index(index.row(), 0, index.parent()));
            const QModelIndex endIndex(m_treeModel->index(index.row(), m_treeModel->columnCount(index.parent()) - 1, index.parent()));
            if (beginIndex.isValid() && endIndex.isValid())
                itemSelection.select(beginIndex, endIndex);
        }
    }

    bool blocked = blockSelectionChangedSignal(true);
    treeWidget()->selectionModel()->select(itemSelection, QItemSelectionModel::ClearAndSelect);
    blockSelectionChangedSignal(blocked);

422
    if (!selectedModelNodes().isEmpty())
423
        treeWidget()->scrollTo(m_treeModel->indexForModelNode(selectedModelNodes().first()));
424

425
    // make sure selected nodes a visible
426
    foreach (const QModelIndex &selectedIndex, itemSelection.indexes()) {
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
        if (selectedIndex.column() == 0)
            expandRecursively(selectedIndex);
    }
}

QTreeView *NavigatorView::treeWidget()
{
    if (m_widget)
        return m_widget->treeView();
    return 0;
}

NavigatorTreeModel *NavigatorView::treeModel()
{
    return m_treeModel.data();
}

// along the lines of QObject::blockSignals
bool NavigatorView::blockSelectionChangedSignal(bool block)
{
    bool oldValue = m_blockSelectionChangedSignal;
    m_blockSelectionChangedSignal = block;
    return oldValue;
}

void NavigatorView::expandRecursively(const QModelIndex &index)
{
    QModelIndex currentIndex = index;
    while (currentIndex.isValid()) {
        if (!treeWidget()->isExpanded(currentIndex))
            treeWidget()->expand(currentIndex);
        currentIndex = currentIndex.parent();
    }
}

} // namespace QmlDesigner