navigatorview.cpp 16.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.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
#include "nameitemdelegate.h"
30
#include "iconcheckboxitemdelegate.h"
31

32
#include <coreplugin/editormanager/editormanager.h>
33
#include <coreplugin/icore.h>
34

35 36
#include <utils/themehelper.h>

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

45 46
static inline void setScenePos(const QmlDesigner::ModelNode &modelNode,const QPointF &pos)
{
47 48
    if (modelNode.hasParentProperty() && QmlDesigner::QmlItemNode::isValidQmlItemNode(modelNode.parentProperty().parentModelNode())) {
        QmlDesigner::QmlItemNode parentNode = modelNode.parentProperty().parentQmlObjectNode().toQmlItemNode();
49 50 51 52 53 54 55 56 57

        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");
        }
58 59
    }
}
60 61 62 63

namespace QmlDesigner {

NavigatorView::NavigatorView(QObject* parent) :
Marco Bubke's avatar
Marco Bubke committed
64
        AbstractView(parent),
65
        m_blockSelectionChangedSignal(false),
66
        m_widget(new NavigatorWidget(this)),
67 68
        m_treeModel(new NavigatorTreeModel(this))
{
69 70 71
    Internal::NavigatorContext *navigatorContext = new Internal::NavigatorContext(m_widget.data());
    Core::ICore::addContextObject(navigatorContext);

72 73 74
    m_widget->setTreeModel(m_treeModel.data());

    connect(treeWidget()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(changeSelection(QItemSelection,QItemSelection)));
75 76 77 78 79 80

    connect(m_widget.data(), SIGNAL(leftButtonClicked()), this, SLOT(leftButtonClicked()));
    connect(m_widget.data(), SIGNAL(rightButtonClicked()), this, SLOT(rightButtonClicked()));
    connect(m_widget.data(), SIGNAL(downButtonClicked()), this, SLOT(downButtonClicked()));
    connect(m_widget.data(), SIGNAL(upButtonClicked()), this, SLOT(upButtonClicked()));

81
    treeWidget()->setIndentation(treeWidget()->indentation() * 0.5);
82

83 84
    NameItemDelegate *idDelegate = new NameItemDelegate(this,
                                                        m_treeModel.data());
85 86 87 88 89 90 91 92 93 94 95
    IconCheckboxItemDelegate *showDelegate =
            new IconCheckboxItemDelegate(this,
                                         Utils::ThemeHelper::themedIconPixmap(QLatin1String(":/navigator/icon/eye_open.png")),
                                         Utils::ThemeHelper::themedIconPixmap(QLatin1String(":/navigator/icon/eye_closed.png")),
                                         m_treeModel.data());

    IconCheckboxItemDelegate *exportDelegate =
            new IconCheckboxItemDelegate(this,
                                         Utils::ThemeHelper::themedIconPixmap(QLatin1String(":/navigator/icon/export_checked.png")),
                                         Utils::ThemeHelper::themedIconPixmap(QLatin1String(":/navigator/icon/export_unchecked.png")),
                                         m_treeModel.data());
96 97 98 99 100 101 102 103 104 105 106 107

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


    treeWidget()->setItemDelegateForColumn(0,idDelegate);
#ifdef _LOCK_ITEMS_
    treeWidget()->setItemDelegateForColumn(1,lockDelegate);
    treeWidget()->setItemDelegateForColumn(2,showDelegate);
#else
108 109
    treeWidget()->setItemDelegateForColumn(1,exportDelegate);
    treeWidget()->setItemDelegateForColumn(2,showDelegate);
110 111
#endif

112 113 114 115 116 117 118 119
}

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

120 121 122 123 124 125 126
bool NavigatorView::hasWidget() const
{
    return true;
}

WidgetInfo NavigatorView::widgetInfo()
{
127 128
    return createWidgetInfo(m_widget.data(),
                            new WidgetInfo::ToolBarWidgetDefaultFactory<NavigatorWidget>(m_widget.data()),
129
                            QStringLiteral("Navigator"),
130 131
                            WidgetInfo::LeftPane,
                            0);
132 133
}

134 135
void NavigatorView::modelAttached(Model *model)
{
Marco Bubke's avatar
Marco Bubke committed
136
    AbstractView::modelAttached(model);
137 138 139

    m_treeModel->setView(this);

140 141
    QTreeView *treeView = treeWidget();
    treeView->expandAll();
142

143
    treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
144 145 146
    treeView->header()->resizeSection(1,26);
    treeView->setRootIsDecorated(false);
    treeView->setIndentation(20);
147
#ifdef _LOCK_ITEMS_
148
    treeView->header()->resizeSection(2,20);
149
#endif
150 151 152 153
}

void NavigatorView::modelAboutToBeDetached(Model *model)
{
154
    m_treeModel->removeSubTree(rootModelNode());
155
    m_treeModel->clearView();
Marco Bubke's avatar
Marco Bubke committed
156
    AbstractView::modelAboutToBeDetached(model);
157 158
}

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

164 165 166 167 168 169 170 171 172 173 174
void NavigatorView::bindingPropertiesChanged(const QList<BindingProperty> & propertyList, PropertyChangeFlags /*propertyChange*/)
{
    foreach (const BindingProperty &bindingProperty, propertyList) {
        /* If a binding property that exports an item using an alias property has
         * changed, we have to update the affected item.
         */
        if (bindingProperty.isAliasExport())
            m_treeModel->updateItemRow(modelNodeForId(bindingProperty.expression()));
    }
}

175 176
void NavigatorView::nodeAboutToBeRemoved(const ModelNode &removedNode)
{
Marco Bubke's avatar
Marco Bubke committed
177
    m_treeModel->removeSubTree(removedNode);
178 179
}

180
void NavigatorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty & newPropertyParent, const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
181 182 183
{
    bool blocked = blockSelectionChangedSignal(true);

Marco Bubke's avatar
Marco Bubke committed
184 185
    m_treeModel->removeSubTree(node);

186 187 188 189 190 191
    if (node.isInHierarchy())
        m_treeModel->addSubTree(node);

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

192 193 194 195 196
    if (newPropertyParent.parentModelNode().isValid()) {
        QModelIndex index = m_treeModel->indexForNode(newPropertyParent.parentModelNode());
        treeWidget()->expand(index);
    }

197 198 199
    blockSelectionChangedSignal(blocked);
}

200
void NavigatorView::nodeIdChanged(const ModelNode& node, const QString & /*newId*/, const QString & /*oldId*/)
201 202 203 204 205 206 207 208
{
    if (m_treeModel->isInTree(node))
        m_treeModel->updateItemRow(node);
}

void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList)
{
    foreach (const AbstractProperty &property, propertyList) {
209 210 211 212
        if (property.isNodeAbstractProperty()) {
            NodeAbstractProperty nodeAbstractProperty(property.toNodeListProperty());
            foreach (const ModelNode &childNode, nodeAbstractProperty.directSubNodes()) {
                m_treeModel->removeSubTree(childNode);
213 214 215 216 217
            }
        }
    }
}

218
void NavigatorView::rootNodeTypeChanged(const QString & /*type*/, int /*majorVersion*/, int /*minorVersion*/)
219
{
220 221
    if (m_treeModel->isInTree(rootModelNode()))
        m_treeModel->updateItemRow(rootModelNode());
222 223
}

224
void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode, const PropertyName & name, const QVariant & /*data*/)
225
{
226
    if (name == "invisible" && m_treeModel->isInTree(modelNode))
227 228
    {
        // update model
229
        m_treeModel->updateItemRow(modelNode);
230 231

        // repaint row (id and icon)
232 233 234 235 236
        foreach (const ModelNode &currentModelNode, modelNode.allSubModelNodesAndThisNode()) {
            QModelIndex index = m_treeModel->indexForNode(currentModelNode);
            treeWidget()->update(index);
            treeWidget()->update(index.sibling(index.row(),index.column()+1));
        }
237
    }
238 239
}

240 241 242 243 244 245
void NavigatorView::instanceErrorChange(const QVector<ModelNode> &errorNodeList)
{
    foreach (const ModelNode &currentModelNode, errorNodeList)
        m_treeModel->updateItemRow(currentModelNode);
}

246
void NavigatorView::nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &node, int /*oldIndex*/)
247
{
248 249 250 251 252 253
    if (m_treeModel->isInTree(node)) {
        m_treeModel->removeSubTree(listProperty.parentModelNode());

        if (node.isInHierarchy())
            m_treeModel->addSubTree(listProperty.parentModelNode());

254 255 256 257
        if (listProperty.parentModelNode().isValid()) {
            QModelIndex index = m_treeModel->indexForNode(listProperty.parentModelNode());
            treeWidget()->expand(index);
        }
258
    }
259 260
}

261 262 263 264
void NavigatorView::changeToComponent(const QModelIndex &index)
{
    if (index.isValid() && m_treeModel->data(index, Qt::UserRole).isValid()) {
        ModelNode doubleClickNode = m_treeModel->nodeForIndex(index);
Marco Bubke's avatar
Marco Bubke committed
265
        if (doubleClickNode.metaInfo().isFileComponent())
Eike Ziller's avatar
Eike Ziller committed
266 267
            Core::EditorManager::openEditor(doubleClickNode.metaInfo().componentFileName(),
                                            Core::Id(), Core::EditorManager::DoNotMakeVisible);
268 269 270
    }
}

271 272 273 274 275 276 277 278
void NavigatorView::leftButtonClicked()
{
    if (selectedModelNodes().count() > 1)
        return; //Semantics are unclear for multi selection.

    bool blocked = blockSelectionChangedSignal(true);

    foreach (const ModelNode &node, selectedModelNodes()) {
279
        if (!node.isRootNode() && !node.parentProperty().parentModelNode().isRootNode()) {
280
            if (QmlItemNode::isValidQmlItemNode(node)) {
281
                QPointF scenePos = QmlItemNode(node).instanceScenePosition();
282
                node.parentProperty().parentProperty().reparentHere(node);
283 284 285
                if (!scenePos.isNull())
                    setScenePos(node, scenePos);
            } else {
286
                node.parentProperty().parentProperty().reparentHere(node);
287 288
            }
        }
289 290 291 292 293 294 295 296 297 298 299 300
    }
    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()) {
301 302
        if (!node.isRootNode() && node.parentProperty().isNodeListProperty() && node.parentProperty().count() > 1) {
            int index = node.parentProperty().indexOf(node);
303 304 305
            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);
306

307 308 309
                if (QmlItemNode::isValidQmlItemNode(node)
                        && QmlItemNode::isValidQmlItemNode(newParent)
                        && !newParent.metaInfo().defaultPropertyIsComponent()) {
310 311 312 313 314
                    QPointF scenePos = QmlItemNode(node).instanceScenePosition();
                    newParent.nodeAbstractProperty(newParent.metaInfo().defaultPropertyName()).reparentHere(node);
                    if (!scenePos.isNull())
                        setScenePos(node, scenePos);
                } else {
315 316
                    if (newParent.metaInfo().isValid() && !newParent.metaInfo().defaultPropertyIsComponent())
                        newParent.nodeAbstractProperty(newParent.metaInfo().defaultPropertyName()).reparentHere(node);
317
                }
318 319 320 321 322 323 324 325 326 327 328 329
            }
        }
    }
    updateItemSelection();
    blockSelectionChangedSignal(blocked);
}

void NavigatorView::upButtonClicked()
{
    bool blocked = blockSelectionChangedSignal(true);
    foreach (const ModelNode &node, selectedModelNodes()) {
        if (!node.isRootNode() && node.parentProperty().isNodeListProperty()) {
330
            int oldIndex = node.parentProperty().indexOf(node);
331 332 333
            int index = oldIndex;
            index--;
            if (index < 0)
334
                index = node.parentProperty().count() - 1; //wrap around
335 336 337 338 339 340 341 342 343 344 345 346
            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()) {
347
            int oldIndex = node.parentProperty().indexOf(node);
348 349
            int index = oldIndex;
            index++;
350
            if (index >= node.parentProperty().count())
351 352 353 354 355 356 357 358
                index = 0; //wrap around
            node.parentProperty().toNodeListProperty().slide(oldIndex, index);
        }
    }
    updateItemSelection();
    blockSelectionChangedSignal(blocked);
}

359 360 361 362 363 364
void NavigatorView::changeSelection(const QItemSelection & /*newSelection*/, const QItemSelection &/*deselected*/)
{
    if (m_blockSelectionChangedSignal)
        return;
    QSet<ModelNode> nodeSet;
    foreach (const QModelIndex &index, treeWidget()->selectionModel()->selectedIndexes()) {
365 366
        if (m_treeModel->data(index, Qt::UserRole).isValid())
            nodeSet.insert(m_treeModel->nodeForIndex(index));
367 368 369 370 371 372 373
    }

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

374
void NavigatorView::selectedNodesChanged(const QList<ModelNode> &/*selectedNodeList*/, const QList<ModelNode> &/*lastSelectedNodeList*/)
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
{
    updateItemSelection();
}

void NavigatorView::updateItemSelection()
{
    QItemSelection itemSelection;
    foreach (const ModelNode &node, selectedModelNodes()) {
        const QModelIndex index = m_treeModel->indexForNode(node);
        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);

396 397 398
    if (!selectedModelNodes().isEmpty())
        treeWidget()->scrollTo(m_treeModel->indexForNode(selectedModelNodes().first()));

399
    // make sure selected nodes a visible
400
    foreach (const QModelIndex &selectedIndex, itemSelection.indexes()) {
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
        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