diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 098376f0fe9..5239837ed49 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -23,10 +23,11 @@ #include -#include -#include -#include #include +#include +#include +#include +#include #include #include @@ -419,7 +420,7 @@ public: parentNode = selectionContext().currentSingleSelectedNode().parentProperty().parentModelNode(); - if (!ModelNode::isThisOrAncestorLocked(parentNode)) { + if (!ModelUtils::isThisOrAncestorLocked(parentNode)) { ActionTemplate *selectionAction = new ActionTemplate("SELECTION", {}, &ModelNodeOperations::select); selectionAction->setParent(menu()); selectionAction->setText(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Parent"))); @@ -432,11 +433,9 @@ public: } } for (const ModelNode &node : selectionContext().view()->allModelNodes()) { - if (node != selectionContext().currentSingleSelectedNode() - && node != parentNode - && contains(node, selectionContext().scenePosition()) - && !node.isRootNode() - && !ModelNode::isThisOrAncestorLocked(node)) { + if (node != selectionContext().currentSingleSelectedNode() && node != parentNode + && contains(node, selectionContext().scenePosition()) && !node.isRootNode() + && !ModelUtils::isThisOrAncestorLocked(node)) { selectionContext().setTargetNode(node); QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node)); ActionTemplate *selectionAction = new ActionTemplate("SELECT", what, &ModelNodeOperations::select); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 50ad727fe22..5791b8ad6c6 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,7 @@ Qt::ItemFlags ConnectionModel::flags(const QModelIndex &modelIndex) const const int internalId = data(index(modelIndex.row(), TargetModelNodeRow), UserRoles::InternalIdRole).toInt(); ModelNode modelNode = m_connectionView->modelNodeForInternalId(internalId); - if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode)) + if (modelNode.isValid() && ModelUtils::isThisOrAncestorLocked(modelNode)) return Qt::ItemIsEnabled; return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index f330094b71c..c054b712ce3 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,8 @@ namespace QmlDesigner { -static inline QIcon contextIcon(const DesignerIcons::IconId &iconId) { +inline static QIcon contextIcon(const DesignerIcons::IconId &iconId) +{ return DesignerActionManager::instance().contextIcon(iconId); }; @@ -257,17 +259,16 @@ void Edit3DWidget::createContextMenu() m_contextMenu->addSeparator(); m_selectParentAction = m_contextMenu->addAction( - contextIcon(DesignerIcons::ParentIcon), - tr("Select Parent"), [&] { - ModelNode parentNode = ModelNode::lowestCommonAncestor(view()->selectedModelNodes()); - if (!parentNode.isValid()) - return; + contextIcon(DesignerIcons::ParentIcon), tr("Select Parent"), [&] { + ModelNode parentNode = ModelUtils::lowestCommonAncestor(view()->selectedModelNodes()); + if (!parentNode.isValid()) + return; - if (!parentNode.isRootNode() && view()->isSelectedModelNode(parentNode)) - parentNode = parentNode.parentProperty().parentModelNode(); + if (!parentNode.isRootNode() && view()->isSelectedModelNode(parentNode)) + parentNode = parentNode.parentProperty().parentModelNode(); - view()->setSelectedModelNode(parentNode); - }); + view()->setSelectedModelNode(parentNode); + }); QAction *defaultToggleGroupAction = view()->edit3DAction(View3DActionType::SelectionModeToggle)->action(); m_toggleGroupAction = m_contextMenu->addAction( @@ -291,7 +292,7 @@ bool Edit3DWidget::isSceneLocked() const { if (m_view && m_view->hasModelNodeForInternalId(m_canvas->activeScene())) { ModelNode node = m_view->modelNodeForInternalId(m_canvas->activeScene()); - if (ModelNode::isThisOrAncestorLocked(node)) + if (ModelUtils::isThisOrAncestorLocked(node)) return true; } return false; @@ -525,7 +526,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) // Block all drags if scene root node is locked if (m_view->hasModelNodeForInternalId(m_canvas->activeScene())) { ModelNode node = m_view->modelNodeForInternalId(m_canvas->activeScene()); - if (ModelNode::isThisOrAncestorLocked(node)) + if (ModelUtils::isThisOrAncestorLocked(node)) return; } diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp index 774b4c0d911..657fb4773e0 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -9,6 +9,8 @@ #include "modelnodecontextmenu.h" #include "qmldesignerconstants.h" +#include + #include #include #include @@ -180,7 +182,7 @@ FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &poi if (formEditorItem->parentItem() && !formEditorItem->parentItem()->isContentVisible()) continue; - if (formEditorItem && ModelNode::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode())) + if (formEditorItem && ModelUtils::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode())) continue; if (!nearestItem) diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index a955a81c0ee..1a3814f9618 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -12,10 +12,11 @@ #include "qproxystyle.h" #include +#include #include +#include #include #include -#include #include #include @@ -212,7 +213,7 @@ void NameItemDelegate::paint(QPainter *painter, } ModelNode node = getModelNode(modelIndex); - if (!ModelNode::isThisOrAncestorLocked(node)) { + if (!ModelUtils::isThisOrAncestorLocked(node)) { NavigatorWidget *widget = qobject_cast(styleOption.widget->parent()); if (widget && !widget->dragType().isEmpty()) { QByteArray dragType = widget->dragType(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 8b7f1f8a61e..6abda00dbac 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -200,7 +200,7 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; if (role == ItemOrAncestorLocked) - return ModelNode::isThisOrAncestorLocked(modelNode); + return ModelUtils::isThisOrAncestorLocked(modelNode); if (role == ModelNodeRole) return QVariant::fromValue(modelNode); @@ -273,13 +273,13 @@ Qt::ItemFlags NavigatorTreeModel::flags(const QModelIndex &index) const if (index.column() == ColumnType::Alias || index.column() == ColumnType::Visibility || index.column() == ColumnType::Lock) { - if (ModelNode::isThisOrAncestorLocked(modelNode)) + if (ModelUtils::isThisOrAncestorLocked(modelNode)) return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; else return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; } - if (ModelNode::isThisOrAncestorLocked(modelNode)) + if (ModelUtils::isThisOrAncestorLocked(modelNode)) return Qt::NoItemFlags; if (index.column() == ColumnType::Name) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp index 7a733c37291..b67c70bb0bc 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -301,7 +302,7 @@ void TimelineSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) if (event->button() == Qt::LeftButton) { event->accept(); - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } } @@ -334,7 +335,7 @@ void TimelineSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) if (m_targetNode.isValid()) m_targetNode.view()->setSelectedModelNode(m_targetNode); } else { - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } update(); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp index a5ec84fad2c..e12b6e915b6 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -349,7 +350,7 @@ void TransitionEditorSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent if (event->button() == Qt::LeftButton) { event->accept(); - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } } @@ -382,7 +383,7 @@ void TransitionEditorSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *ev if (m_targetNode.isValid()) m_targetNode.view()->setSelectedModelNode(m_targetNode); } else { - if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + if (!ModelUtils::isThisOrAncestorLocked(m_targetNode)) toggleCollapsed(); } update(); diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 676a211cd61..0517599f5be 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -223,9 +223,6 @@ public: bool locked() const; void setLocked(bool value); - static bool isThisOrAncestorLocked(const ModelNode &node); - static ModelNode lowestCommonAncestor(const QList &nodes); - qint32 internalId() const; void setNodeSource(const QString&); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 17cdffb6f7e..2a9e0e12462 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -8,12 +8,13 @@ #include "internalnode_p.h" #include "model.h" #include "model_p.h" +#include "modelutils.h" #include "nodeinstanceview.h" #include "nodelistproperty.h" #include "nodemetainfo.h" +#include "qmldesignerconstants.h" #include "qmlstate.h" #include "qmltimeline.h" -#include "qmldesignerconstants.h" #include "rewritertransaction.h" #include "variantproperty.h" @@ -412,7 +413,7 @@ void AbstractView::setSelectedModelNodes(const QList &selectedNodeLis QList unlockedNodes; for (const auto &modelNode : selectedNodeList) { - if (!ModelNode::isThisOrAncestorLocked(modelNode)) + if (!ModelUtils::isThisOrAncestorLocked(modelNode)) unlockedNodes.push_back(modelNode); } @@ -421,7 +422,7 @@ void AbstractView::setSelectedModelNodes(const QList &selectedNodeLis void AbstractView::setSelectedModelNode(const ModelNode &modelNode) { - if (ModelNode::isThisOrAncestorLocked(modelNode)) { + if (ModelUtils::isThisOrAncestorLocked(modelNode)) { clearSelectedModelNodes(); return; } diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index c285161e833..c8d9132d572 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -1230,108 +1230,6 @@ void ModelNode::setLocked(bool value) } } -bool ModelNode::isThisOrAncestorLocked(const ModelNode &node) -{ - if (!node.isValid()) - return false; - - if (node.locked()) - return true; - - if (node.isRootNode() || !node.hasParentProperty()) - return false; - - return isThisOrAncestorLocked(node.parentProperty().parentModelNode()); -} - -/*! - * \brief The lowest common ancestor node for node1 and node2. If one of the nodes (Node A) is - * the ancestor of the other node, the return value is Node A and not the parent of Node A. - * \param node1 First node - * \param node2 Second node - * \param depthOfLCA Depth of the return value - * \param depthOfNode1 Depth of node1. Use this parameter for optimization - * \param depthOfNode2 Depth of node2. Use this parameter for optimization - */ -static ModelNode lowestCommonAncestor(const ModelNode &node1, - const ModelNode &node2, - int &depthOfLCA, - const int &depthOfNode1 = -1, - const int &depthOfNode2 = -1) -{ - Q_ASSERT(node1.isValid() && node2.isValid()); - - auto depthOfNode = [](const ModelNode &node) -> int { - int depth = 0; - ModelNode parentNode = node; - while (!parentNode.isRootNode()) { - depth++; - parentNode = parentNode.parentProperty().parentModelNode(); - } - return depth; - }; - - if (node1 == node2) { - depthOfLCA = (depthOfNode1 < 0) ? ((depthOfNode2 < 0) ? depthOfNode(node1) : depthOfNode2) - : depthOfNode1; - return node1; - } - - if (node1.isRootNode()) { - depthOfLCA = 0; - return node1; - } - - if (node2.isRootNode()) { - depthOfLCA = 0; - return node2; - } - - ModelNode nodeLower = node1; - ModelNode nodeHigher = node2; - int depthLower = (depthOfNode1 < 0) ? depthOfNode(nodeLower) : depthOfNode1; - int depthHigher = (depthOfNode2 < 0) ? depthOfNode(nodeHigher) : depthOfNode2; - - if (depthLower > depthHigher) { - std::swap(depthLower, depthHigher); - std::swap(nodeLower, nodeHigher); - } - - int depthDiff = depthHigher - depthLower; - while (depthDiff--) - nodeHigher = nodeHigher.parentProperty().parentModelNode(); - - while (nodeLower != nodeHigher) { - nodeLower = nodeLower.parentProperty().parentModelNode(); - nodeHigher = nodeHigher.parentProperty().parentModelNode(); - --depthLower; - } - - depthOfLCA = depthLower; - return nodeLower; -} - -/*! - * \brief The lowest common node containing all nodes. If one of the nodes (Node A) is - * the ancestor of the other nodes, the return value is Node A and not the parent of Node A. - */ -ModelNode ModelNode::lowestCommonAncestor(const QList &nodes) -{ - if (nodes.isEmpty()) - return {}; - - ModelNode accumulatedNode = nodes.first(); - int accumulatedNodeDepth = -1; - for (const ModelNode &node : Utils::span(nodes).subspan(1)) { - accumulatedNode = QmlDesigner::lowestCommonAncestor(accumulatedNode, - node, - accumulatedNodeDepth, - accumulatedNodeDepth); - } - - return accumulatedNode; -} - void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList) { if (!isValid()) diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index e1fe3f3cadc..22f472c3e5f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -4,6 +4,7 @@ #include "modelutils.h" #include +#include #include #include #include @@ -162,4 +163,108 @@ QList allModelNodesWithId(AbstractView *view) [&](const ModelNode &node) { return node.hasId(); }); } +bool isThisOrAncestorLocked(const ModelNode &node) +{ + if (!node.isValid()) + return false; + + if (node.locked()) + return true; + + if (node.isRootNode() || !node.hasParentProperty()) + return false; + + return isThisOrAncestorLocked(node.parentProperty().parentModelNode()); +} + +/*! + * \brief The lowest common ancestor node for node1 and node2. If one of the nodes (Node A) is + * the ancestor of the other node, the return value is Node A and not the parent of Node A. + * \param node1 First node + * \param node2 Second node + * \param depthOfLCA Depth of the return value + * \param depthOfNode1 Depth of node1. Use this parameter for optimization + * \param depthOfNode2 Depth of node2. Use this parameter for optimization + */ +namespace { +ModelNode lowestCommonAncestor(const ModelNode &node1, + const ModelNode &node2, + int &depthOfLCA, + const int &depthOfNode1 = -1, + const int &depthOfNode2 = -1) +{ + Q_ASSERT(node1.isValid() && node2.isValid()); + + auto depthOfNode = [](const ModelNode &node) -> int { + int depth = 0; + ModelNode parentNode = node; + while (!parentNode.isRootNode()) { + depth++; + parentNode = parentNode.parentProperty().parentModelNode(); + } + return depth; + }; + + if (node1 == node2) { + depthOfLCA = (depthOfNode1 < 0) ? ((depthOfNode2 < 0) ? depthOfNode(node1) : depthOfNode2) + : depthOfNode1; + return node1; + } + + if (node1.isRootNode()) { + depthOfLCA = 0; + return node1; + } + + if (node2.isRootNode()) { + depthOfLCA = 0; + return node2; + } + + ModelNode nodeLower = node1; + ModelNode nodeHigher = node2; + int depthLower = (depthOfNode1 < 0) ? depthOfNode(nodeLower) : depthOfNode1; + int depthHigher = (depthOfNode2 < 0) ? depthOfNode(nodeHigher) : depthOfNode2; + + if (depthLower > depthHigher) { + std::swap(depthLower, depthHigher); + std::swap(nodeLower, nodeHigher); + } + + int depthDiff = depthHigher - depthLower; + while (depthDiff--) + nodeHigher = nodeHigher.parentProperty().parentModelNode(); + + while (nodeLower != nodeHigher) { + nodeLower = nodeLower.parentProperty().parentModelNode(); + nodeHigher = nodeHigher.parentProperty().parentModelNode(); + --depthLower; + } + + depthOfLCA = depthLower; + return nodeLower; +} +} // namespace + +/*! + * \brief The lowest common node containing all nodes. If one of the nodes (Node A) is + * the ancestor of the other nodes, the return value is Node A and not the parent of Node A. + */ +ModelNode lowestCommonAncestor(const QList &nodes) +{ + if (nodes.isEmpty()) + return {}; + + ModelNode accumulatedNode = nodes.first(); + int accumulatedNodeDepth = -1; + for (const ModelNode &node : Utils::span(nodes).subspan(1)) { + accumulatedNode = lowestCommonAncestor(accumulatedNode, + node, + accumulatedNodeDepth, + accumulatedNodeDepth); + } + + return accumulatedNode; +} + } // namespace QmlDesigner::ModelUtils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h index ee6d3e41303..26cf29aac00 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.h +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -38,4 +38,7 @@ QMLDESIGNERCORE_EXPORT QList pruneChildren(const QList &no QMLDESIGNERCORE_EXPORT QList allModelNodesWithId(AbstractView *view); +QMLDESIGNERCORE_EXPORT bool isThisOrAncestorLocked(const ModelNode &node); +QMLDESIGNERCORE_EXPORT ModelNode lowestCommonAncestor(const QList &nodes); + } // namespace QmlDesigner::ModelUtils