246 lines
7.8 KiB
C
246 lines
7.8 KiB
C
|
|
#pragma once
|
||
|
|
|
||
|
|
#include <QMap>
|
||
|
|
#include <QSet>
|
||
|
|
#include <QVector>
|
||
|
|
|
||
|
|
#include <QtCore/QJsonObject>
|
||
|
|
#include <QtCore/QObject>
|
||
|
|
#include <QtCore/QVariant>
|
||
|
|
|
||
|
|
#include "global.h"
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The central class in the Model-View approach. It delivers all kinds
|
||
|
|
* of information from the backing user data structures that represent
|
||
|
|
* the graph. The class allows to modify the graph structure: create
|
||
|
|
* and remove nodes and connections.
|
||
|
|
*
|
||
|
|
* We use two types of the unique ids for graph manipulations:
|
||
|
|
* - NodeId
|
||
|
|
* - ConnectionId
|
||
|
|
*/
|
||
|
|
class AbstractGraphModel : public QObject
|
||
|
|
{
|
||
|
|
Q_OBJECT
|
||
|
|
public:
|
||
|
|
/// Generates a new unique NodeId.
|
||
|
|
virtual NodeId newNodeId() = 0;
|
||
|
|
|
||
|
|
/// @brief Returns the full set of unique Node Ids.
|
||
|
|
/**
|
||
|
|
* Model creator is responsible for generating unique `unsigned int`
|
||
|
|
* Ids for all the nodes in the graph. From an Id it should be
|
||
|
|
* possible to trace back to the model's internal representation of
|
||
|
|
* the node.
|
||
|
|
*/
|
||
|
|
virtual QSet<NodeId> allNodeIds() const = 0;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A collection of all input and output connections for the given `nodeId`.
|
||
|
|
*/
|
||
|
|
virtual QSet<ConnectionId> allConnectionIds(NodeId const nodeId) const = 0;
|
||
|
|
|
||
|
|
/// @brief Returns all connected Node Ids for given port.
|
||
|
|
/**
|
||
|
|
* The returned set of nodes and port indices correspond to the type
|
||
|
|
* opposite to the given `portType`.
|
||
|
|
*/
|
||
|
|
virtual QSet<ConnectionId> connections(NodeId nodeId,
|
||
|
|
PortType portType,
|
||
|
|
PortIndex index) const
|
||
|
|
= 0;
|
||
|
|
|
||
|
|
/// Checks if two nodes with the given `connectionId` are connected.
|
||
|
|
virtual bool connectionExists(ConnectionId const connectionId) const = 0;
|
||
|
|
|
||
|
|
/// Creates a new node instance in the derived class.
|
||
|
|
/**
|
||
|
|
* The model is responsible for generating a unique `NodeId`.
|
||
|
|
* @param[in] nodeType is free to be used and interpreted by the
|
||
|
|
* model on its own, it helps to distinguish between possible node
|
||
|
|
* types and create a correct instance inside.
|
||
|
|
*/
|
||
|
|
virtual NodeId addNode(QString const nodeType = QString()) = 0;
|
||
|
|
|
||
|
|
/// Model decides if a conection with a given connection Id possible.
|
||
|
|
/**
|
||
|
|
* The default implementation compares corresponding data types.
|
||
|
|
*
|
||
|
|
* It is possible to override the function and connect non-equal
|
||
|
|
* data types.
|
||
|
|
*/
|
||
|
|
virtual bool connectionPossible(ConnectionId const connectionId) const = 0;
|
||
|
|
|
||
|
|
/// Defines if detaching the connection is possible.
|
||
|
|
virtual bool detachPossible(ConnectionId const) const { return true; }
|
||
|
|
|
||
|
|
/// Creates a new connection between two nodes.
|
||
|
|
/**
|
||
|
|
* Default implementation emits signal
|
||
|
|
* `connectionCreated(connectionId)`
|
||
|
|
*
|
||
|
|
* In the derived classes user must emite the signal to notify the
|
||
|
|
* scene about the changes.
|
||
|
|
*/
|
||
|
|
virtual void addConnection(ConnectionId const connectionId) = 0;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @returns `true` if there is data in the model associated with the
|
||
|
|
* given `nodeId`.
|
||
|
|
*/
|
||
|
|
virtual bool nodeExists(NodeId const nodeId) const = 0;
|
||
|
|
|
||
|
|
/// @brief Returns node-related data for requested NodeRole.
|
||
|
|
/**
|
||
|
|
* @returns Node Caption, Node Caption Visibility, Node Position etc.
|
||
|
|
*/
|
||
|
|
virtual QVariant nodeData(NodeId nodeId, NodeRole role) const = 0;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A utility function that unwraps the `QVariant` value returned from the
|
||
|
|
* standard `QVariant AbstractGraphModel::nodeData(NodeId, NodeRole)` function.
|
||
|
|
*/
|
||
|
|
template<typename T>
|
||
|
|
T nodeData(NodeId nodeId, NodeRole role) const
|
||
|
|
{
|
||
|
|
return nodeData(nodeId, role).value<T>();
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual NodeFlags nodeFlags(NodeId nodeId) const
|
||
|
|
{
|
||
|
|
Q_UNUSED(nodeId);
|
||
|
|
return NodeFlag::NoFlags;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Sets node properties.
|
||
|
|
/**
|
||
|
|
* Sets: Node Caption, Node Caption Visibility,
|
||
|
|
* Shyle, State, Node Position etc.
|
||
|
|
* @see NodeRole.
|
||
|
|
*/
|
||
|
|
virtual bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) = 0;
|
||
|
|
|
||
|
|
/// @brief Returns port-related data for requested NodeRole.
|
||
|
|
/**
|
||
|
|
* @returns Port Data Type, Port Data, Connection Policy, Port
|
||
|
|
* Caption.
|
||
|
|
*/
|
||
|
|
virtual QVariant portData(NodeId nodeId, PortType portType, PortIndex index, PortRole role) const
|
||
|
|
= 0;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A utility function that unwraps the `QVariant` value returned from the
|
||
|
|
* standard `QVariant AbstractGraphModel::portData(...)` function.
|
||
|
|
*/
|
||
|
|
template<typename T>
|
||
|
|
T portData(NodeId nodeId, PortType portType, PortIndex index, PortRole role) const
|
||
|
|
{
|
||
|
|
return portData(nodeId, portType, index, role).value<T>();
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual bool setPortData(NodeId nodeId,
|
||
|
|
PortType portType,
|
||
|
|
PortIndex index,
|
||
|
|
QVariant const &value,
|
||
|
|
PortRole role = PortRole::Data)
|
||
|
|
= 0;
|
||
|
|
|
||
|
|
virtual bool deleteConnection(ConnectionId const connectionId) = 0;
|
||
|
|
|
||
|
|
virtual bool deleteNode(NodeId const nodeId) = 0;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Reimplement the function if you want to store/restore the node's
|
||
|
|
* inner state during undo/redo node deletion operations.
|
||
|
|
*/
|
||
|
|
virtual QJsonObject saveNode(NodeId const) const { return {}; }
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Reimplement the function if you want to support:
|
||
|
|
*
|
||
|
|
* - graph save/restore operations,
|
||
|
|
* - undo/redo operations after deleting the node.
|
||
|
|
*
|
||
|
|
* QJsonObject must contain following fields:
|
||
|
|
*
|
||
|
|
*
|
||
|
|
* ```
|
||
|
|
* {
|
||
|
|
* id : 5,
|
||
|
|
* position : { x : 100, y : 200 },
|
||
|
|
* internal-data {
|
||
|
|
* "your model specific data here"
|
||
|
|
* }
|
||
|
|
* }
|
||
|
|
* ```
|
||
|
|
*
|
||
|
|
* The function must do almost exacly the same thing as the normal addNode().
|
||
|
|
* The main difference is in a model-specific `inner-data` processing.
|
||
|
|
*/
|
||
|
|
virtual void loadNode(QJsonObject const &) {}
|
||
|
|
|
||
|
|
public:
|
||
|
|
/**
|
||
|
|
* Function clears connections attached to the ports that are scheduled to be
|
||
|
|
* deleted. It must be called right before the model removes its old port data.
|
||
|
|
*
|
||
|
|
* @param nodeId Defines the node to be modified
|
||
|
|
* @param portType Is either PortType::In or PortType::Out
|
||
|
|
* @param first Index of the first port to be removed
|
||
|
|
* @param last Index of the last port to be removed
|
||
|
|
*/
|
||
|
|
void portsAboutToBeDeleted(NodeId const nodeId,
|
||
|
|
PortType const portType,
|
||
|
|
PortIndex const first,
|
||
|
|
PortIndex const last);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Signal emitted when model no longer has the old data associated with the
|
||
|
|
* given port indices and when the node must be repainted.
|
||
|
|
*/
|
||
|
|
void portsDeleted();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Signal emitted when model is about to create new ports on the given node.
|
||
|
|
* @param first Is the first index of the new port after insertion.
|
||
|
|
* @param last Is the last index of the new port after insertion.
|
||
|
|
*
|
||
|
|
* Function caches existing connections that are located after the `last` port
|
||
|
|
* index. For such connections the new "post-insertion" addresses are computed
|
||
|
|
* and stored until the function AbstractGraphModel::portsInserted is called.
|
||
|
|
*/
|
||
|
|
void portsAboutToBeInserted(NodeId const nodeId,
|
||
|
|
PortType const portType,
|
||
|
|
PortIndex const first,
|
||
|
|
PortIndex const last);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Function re-creates the connections that were shifted during the port
|
||
|
|
* insertion. After that the node is updated.
|
||
|
|
*/
|
||
|
|
void portsInserted();
|
||
|
|
|
||
|
|
Q_SIGNALS:
|
||
|
|
void connectionCreated(ConnectionId const connectionId);
|
||
|
|
|
||
|
|
void connectionDeleted(ConnectionId const connectionId);
|
||
|
|
|
||
|
|
void nodeCreated(NodeId const nodeId);
|
||
|
|
|
||
|
|
void nodeDeleted(NodeId const nodeId);
|
||
|
|
|
||
|
|
void nodeUpdated(NodeId const nodeId);
|
||
|
|
|
||
|
|
void nodeFlagsUpdated(NodeId const nodeId);
|
||
|
|
|
||
|
|
void nodePositionUpdated(NodeId const nodeId);
|
||
|
|
|
||
|
|
void modelReset();
|
||
|
|
|
||
|
|
private:
|
||
|
|
QVector<ConnectionId> _shiftedByDynamicPortsConnections;
|
||
|
|
};
|
||
|
|
|