#include "NodeConnectionInteraction.hpp"
#include "AbstractNodeGeometry.hpp"
#include "BasicGraphicsScene.hpp"
#include "ConnectionGraphicsObject.hpp"
#include "ConnectionIdUtils.hpp"
#include "NodeGraphicsObject.hpp"
#include "UndoCommands.hpp"
#include <QtCore/QDebug>
#include <QUndoStack>
namespace QtNodes {
NodeConnectionInteraction::NodeConnectionInteraction(NodeGraphicsObject &ngo,
ConnectionGraphicsObject &cgo,
BasicGraphicsScene &scene)
: _ngo(ngo)
, _cgo(cgo)
, _scene(scene)
{}
bool NodeConnectionInteraction::canConnect(PortIndex *portIndex) const
{
PortType const requiredPort = _cgo.connectionState().requiredPort();
if (requiredPort == PortType::None) {
return false;
}
QPointF const connectionPoint = _cgo.sceneTransform().map(_cgo.endPoint(requiredPort));
*portIndex = nodePortIndexUnderScenePoint(requiredPort, connectionPoint);
if (*portIndex == InvalidPortIndex) {
return false;
}
AbstractGraphModel &model = _ngo.nodeScene()->graphModel();
ConnectionId connectionId = makeCompleteConnectionId(_cgo.connectionId(),
_ngo.nodeId(),
*portIndex);
return model.connectionPossible(connectionId);
}
bool NodeConnectionInteraction::tryConnect() const
{
PortIndex targetPortIndex = InvalidPortIndex;
if (!canConnect(&targetPortIndex)) {
return false;
}
ConnectionId incompleteConnectionId = _cgo.connectionId();
ConnectionId newConnectionId = makeCompleteConnectionId(incompleteConnectionId,
_ngo.nodeId(),
targetPortIndex);
_ngo.nodeScene()->resetDraftConnection();
_ngo.nodeScene()->undoStack().push(new ConnectCommand(_ngo.nodeScene(), newConnectionId));
return true;
}
bool NodeConnectionInteraction::disconnect(PortType portToDisconnect) const
{
ConnectionId connectionId = _cgo.connectionId();
_scene.undoStack().push(new DisconnectCommand(&_scene, connectionId));
AbstractNodeGeometry &geometry = _scene.nodeGeometry();
QPointF scenePos = geometry.portScenePosition(_ngo.nodeId(),
portToDisconnect,
getPortIndex(portToDisconnect, connectionId),
_ngo.sceneTransform());
ConnectionId incompleteConnectionId = makeIncompleteConnectionId(connectionId, portToDisconnect);
auto const &draftConnection =
_scene.makeDraftConnection(incompleteConnectionId);
QPointF const looseEndPos = draftConnection->mapFromScene(scenePos);
draftConnection->setEndPoint(portToDisconnect, looseEndPos);
NodeId connectedNodeId = getNodeId(oppositePort(portToDisconnect), connectionId);
_scene.nodeGraphicsObject(connectedNodeId)->update();
NodeId disconnectedNodeId = getNodeId(portToDisconnect, connectionId);
_scene.nodeGraphicsObject(disconnectedNodeId)->update();
return true;
}
PortType NodeConnectionInteraction::connectionRequiredPort() const
{
auto const &state = _cgo.connectionState();
return state.requiredPort();
}
QPointF NodeConnectionInteraction::nodePortScenePosition(PortType portType,
PortIndex portIndex) const
{
AbstractNodeGeometry &geometry = _scene.nodeGeometry();
QPointF p = geometry.portScenePosition(_ngo.nodeId(),
portType,
portIndex,
_ngo.sceneTransform());
return p;
}
PortIndex NodeConnectionInteraction::nodePortIndexUnderScenePoint(PortType portType,
QPointF const &scenePoint) const
{
AbstractNodeGeometry &geometry = _scene.nodeGeometry();
QTransform sceneTransform = _ngo.sceneTransform();
QPointF nodePoint = sceneTransform.inverted().map(scenePoint);
return geometry.checkPortHit(_ngo.nodeId(), portType, nodePoint);
}
}