/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** 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, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "movemanipulator.h" #include "itemutilfunctions.h" #include "layeritem.h" #include "formeditoritem.h" #include "formeditorscene.h" #include #include #include #include #include #include #include #include #include #include namespace QmlDesigner { MoveManipulator::MoveManipulator(LayerItem *layerItem, FormEditorView *view) : m_layerItem(layerItem), m_view(view), m_isActive(false) { } MoveManipulator::~MoveManipulator() { deleteSnapLines(); } QPointF MoveManipulator::beginPoint() const { return m_beginPoint; } void MoveManipulator::setItem(FormEditorItem* item) { QList itemList; itemList.append(item); setItems(itemList); } void MoveManipulator::setItems(const QList &itemList) { m_itemList = itemList; if (!m_itemList.isEmpty()) { if (m_itemList.first()->parentItem()) m_snapper.setContainerFormEditorItem(m_itemList.first()->parentItem()); else m_snapper.setContainerFormEditorItem(m_itemList.first()); m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem()); } } void MoveManipulator::updateHashes() { // foreach (FormEditorItem* item, m_itemList) // m_beginItemRectHash[item] = item->mapRectToParent(item->qmlItemNode().instanceBoundingRect()); foreach (FormEditorItem* item, m_itemList) { QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item)); m_beginItemRectHash[item].translate(positionInParentSpace - m_beginPositionHash.value(item)); m_beginPositionHash.insert(item, positionInParentSpace); } } bool MoveManipulator::itemsCanReparented() const { foreach (FormEditorItem* item, m_itemList) { if (item && item->qmlItemNode().isValid() && !item->qmlItemNode().canReparent()) return false; } return true; } void MoveManipulator::begin(const QPointF &beginPoint) { m_isActive = true; m_snapper.updateSnappingLines(m_itemList); foreach (FormEditorItem* item, m_itemList) { if (item && item->qmlItemNode().isValid()) m_beginItemRectHash.insert(item, m_snapper.containerFormEditorItem()->mapRectFromItem(item, item->qmlItemNode().instanceBoundingRect())); } foreach (FormEditorItem* item, m_itemList) { if (item && item->qmlItemNode().isValid()) { QPointF positionInParentSpace(item->qmlItemNode().instancePosition()); QPointF positionInScenesSpace = m_snapper.containerFormEditorItem()->mapToScene(positionInParentSpace); m_beginPositionInSceneSpaceHash.insert(item, positionInScenesSpace); } } foreach (FormEditorItem* item, m_itemList) { if (item && item->qmlItemNode().isValid()) { QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item)); m_beginPositionHash.insert(item, positionInParentSpace); QmlAnchors anchors(item->qmlItemNode().anchors()); m_beginTopMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Top)); m_beginLeftMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Left)); m_beginRightMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Right)); m_beginBottomMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Bottom)); m_beginHorizontalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::HorizontalCenter)); m_beginVerticalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::VerticalCenter)); } } m_beginPoint = beginPoint; // setOpacityForAllElements(0.62); m_rewriterTransaction = m_view->beginRewriterTransaction(); } QPointF MoveManipulator::findSnappingOffset(const QHash &boundingRectHash) { QPointF offset; QMap verticalOffsetMap; QMap horizontalOffsetMap; QHashIterator hashIterator(boundingRectHash); while(hashIterator.hasNext()) { hashIterator.next(); FormEditorItem *formEditorItem = hashIterator.key(); QRectF boundingRect = hashIterator.value(); if (!formEditorItem || !formEditorItem->qmlItemNode().isValid()) continue; if (!formEditorItem->qmlItemNode().hasBindingProperty("x")) { double verticalOffset = m_snapper.snappedVerticalOffset(boundingRect); if (verticalOffset < std::numeric_limits::max()) verticalOffsetMap.insert(qAbs(verticalOffset), verticalOffset); } if (!formEditorItem->qmlItemNode().hasBindingProperty("y")) { double horizontalOffset = m_snapper.snappedHorizontalOffset(boundingRect); if (horizontalOffset < std::numeric_limits::max()) horizontalOffsetMap.insert(qAbs(horizontalOffset), horizontalOffset); } } if (!verticalOffsetMap.isEmpty()) offset.rx() = verticalOffsetMap.begin().value(); if (!horizontalOffsetMap.isEmpty()) offset.ry() = horizontalOffsetMap.begin().value(); return offset; } void MoveManipulator::generateSnappingLines(const QHash &boundingRectHash) { m_graphicsLineList = m_snapper.generateSnappingLines(boundingRectHash.values(), m_layerItem.data(), m_snapper.transformtionSpaceFormEditorItem()->sceneTransform()); } QHash MoveManipulator::tanslatedBoundingRects(const QHash &boundingRectHash, const QPointF& offsetVector) { QHash translatedBoundingRectHash; QHashIterator hashIterator(boundingRectHash); while(hashIterator.hasNext()) { QPointF alignedOffset(offsetVector); hashIterator.next(); FormEditorItem *formEditorItem = hashIterator.key(); QRectF boundingRect = hashIterator.value(); if (!formEditorItem || !formEditorItem->qmlItemNode().isValid()) continue; if (formEditorItem->qmlItemNode().hasBindingProperty("x")) alignedOffset.setX(0); if (formEditorItem->qmlItemNode().hasBindingProperty("y")) alignedOffset.setY(0); translatedBoundingRectHash.insert(formEditorItem, boundingRect.translated(offsetVector)); } return translatedBoundingRectHash; } /* /brief updates the position of the items. */ void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping, State stateToBeManipulated) { deleteSnapLines(); //Since they position is changed and the item is moved the snapping lines are //are obsolete. The new updated snapping lines (color and visibility) will be //calculated in snapPoint() called in moveNode() later if (m_itemList.isEmpty()) { return; } else { QPointF updatePointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(updatePoint)); QPointF beginPointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(m_beginPoint)); QPointF offsetVector(updatePointInContainerSpace - beginPointInContainerSpace); if (useSnapping == UseSnappingAndAnchoring) { } if (useSnapping == UseSnapping || useSnapping == UseSnappingAndAnchoring) { offsetVector -= findSnappingOffset(tanslatedBoundingRects(m_beginItemRectHash, offsetVector)); generateSnappingLines(tanslatedBoundingRects(m_beginItemRectHash, offsetVector)); } foreach (FormEditorItem* item, m_itemList) { QPointF positionInContainerSpace(m_beginPositionHash.value(item) + offsetVector); if (!item || !item->qmlItemNode().isValid()) continue; // don't support anchors for base state because it is not needed by the droptool if (stateToBeManipulated == UseActualState) { QmlAnchors anchors(item->qmlItemNode().anchors()); if (anchors.instanceHasAnchor(AnchorLine::Top)) { anchors.setMargin(AnchorLine::Top, m_beginTopMarginHash.value(item) + offsetVector.y()); } if (anchors.instanceHasAnchor(AnchorLine::Left)) { anchors.setMargin(AnchorLine::Left, m_beginLeftMarginHash.value(item) + offsetVector.x()); } if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { anchors.setMargin(AnchorLine::Bottom, m_beginBottomMarginHash.value(item) - offsetVector.y()); } if (anchors.instanceHasAnchor(AnchorLine::Right)) { anchors.setMargin(AnchorLine::Right, m_beginRightMarginHash.value(item) - offsetVector.x()); } if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { anchors.setMargin(AnchorLine::HorizontalCenter, m_beginHorizontalCenterHash.value(item) + offsetVector.x()); } if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { anchors.setMargin(AnchorLine::VerticalCenter, m_beginVerticalCenterHash.value(item) + offsetVector.y()); } setPosition(item->qmlItemNode(), positionInContainerSpace); } else { item->qmlItemNode().modelNode().variantProperty("x").setValue(qRound(positionInContainerSpace.x())); item->qmlItemNode().modelNode().variantProperty("y").setValue(qRound(positionInContainerSpace.y())); } } } } void MoveManipulator::clear() { deleteSnapLines(); m_beginItemRectHash.clear(); m_beginPositionHash.clear(); m_beginPositionInSceneSpaceHash.clear(); m_itemList.clear(); m_rewriterTransaction.commit(); m_beginTopMarginHash.clear(); m_beginLeftMarginHash.clear(); m_beginRightMarginHash.clear(); m_beginBottomMarginHash.clear(); m_beginHorizontalCenterHash.clear(); m_beginVerticalCenterHash.clear(); } void MoveManipulator::reparentTo(FormEditorItem *newParent) { deleteSnapLines(); if (!newParent) return; if (!itemsCanReparented()) return; foreach (FormEditorItem* item, m_itemList) { if (!item || !item->qmlItemNode().isValid()) continue; QmlItemNode parent(newParent->qmlItemNode()); if (parent.isValid()) { if (parent.hasDefaultProperty()) item->qmlItemNode().setParentProperty(parent.nodeAbstractProperty(parent.defaultProperty())); else item->qmlItemNode().setParentProperty(parent.nodeAbstractProperty("data")); } } if (m_view->model()) { m_snapper.setContainerFormEditorItem(newParent); m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem()); m_snapper.updateSnappingLines(m_itemList); updateHashes(); } } void MoveManipulator::end(const QPointF &/*endPoint*/) { m_isActive = false; deleteSnapLines(); // setOpacityForAllElements(1.0); clear(); } void MoveManipulator::moveBy(double deltaX, double deltaY) { foreach (FormEditorItem* item, m_itemList) { if (!item || !item->qmlItemNode().isValid()) continue; QmlAnchors anchors(item->qmlItemNode().anchors()); if (anchors.instanceHasAnchor(AnchorLine::Top)) { anchors.setMargin(AnchorLine::Top, anchors.instanceMargin(AnchorLine::Top) + deltaY); } if (anchors.instanceHasAnchor(AnchorLine::Left)) { anchors.setMargin(AnchorLine::Left, anchors.instanceMargin(AnchorLine::Left) + deltaX); } if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { anchors.setMargin(AnchorLine::Bottom, anchors.instanceMargin(AnchorLine::Bottom) - deltaY); } if (anchors.instanceHasAnchor(AnchorLine::Right)) { anchors.setMargin(AnchorLine::Right, anchors.instanceMargin(AnchorLine::Right) - deltaX); } if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { anchors.setMargin(AnchorLine::HorizontalCenter, anchors.instanceMargin(AnchorLine::HorizontalCenter) + deltaX); } if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { anchors.setMargin(AnchorLine::VerticalCenter, anchors.instanceMargin(AnchorLine::VerticalCenter) + deltaY); } setPosition(item->qmlItemNode(), QPointF(item->qmlItemNode().instanceValue("x").toDouble() + deltaX, item->qmlItemNode().instanceValue("y").toDouble() + deltaY)); } } void MoveManipulator::beginRewriterTransaction() { m_rewriterTransaction = m_view->beginRewriterTransaction(); } void MoveManipulator::endRewriterTransaction() { m_rewriterTransaction.commit(); } void MoveManipulator::setOpacityForAllElements(qreal opacity) { foreach (FormEditorItem* item, m_itemList) item->setOpacity(opacity); } void MoveManipulator::deleteSnapLines() { if (m_layerItem) { foreach (QGraphicsItem *item, m_graphicsLineList) { m_layerItem->scene()->removeItem(item); delete item; } } m_graphicsLineList.clear(); m_view->scene()->update(); } bool MoveManipulator::isActive() const { return m_isActive; } void MoveManipulator::setPosition(QmlItemNode itemNode, const QPointF &position) { if (!itemNode.hasBindingProperty("x")) itemNode.setVariantProperty("x", qRound(position.x())); if (!itemNode.hasBindingProperty("y")) itemNode.setVariantProperty("y", qRound(position.y())); } }