-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QGraphicsScene>
-#include <QGraphicsSceneMouseEvent>
-#include <QPainter>
-#include <QStyleOption>
-
-#include "Edge.h"
#include "Node.h"
-#include "GraphWidget.h"
+#include "Edge.h"
+#include "GraphCanvas.h"
+
+#include <QFontMetricsF>
+#include <QGraphicsWidget>
+#include <QGraphicsSceneHoverEvent>
-Node::Node(GraphWidget *graphWidget, const std::string& label)
- : graph(graphWidget),
- m_Label( label )
+#include <cpPlugins/Interface/Object.h>
+#include <cpPlugins/Interface/DataObject.h>
+#include <cpPlugins/Interface/ProcessObject.h>
+
+#define PORT_SIZE 15
+
+// -------------------------------------------------------------------------
+PipelineEditor::Node::
+Node( GraphCanvas* canvas, cpPlugins::Interface::Object* object )
+ : QGraphicsItem( NULL ),
+ m_Canvas( canvas ),
+ m_Object( object ),
+ m_SelectedPort( NULL ),
+ m_DraggingPort( false )
{
- setFlag(ItemIsMovable);
- setFlag(ItemSendsGeometryChanges);
- setCacheMode(DeviceCoordinateCache);
- setZValue(-1);
- this->setToolTip( this->m_Label.c_str( ) );
+ this->setFlag( QGraphicsItem::ItemIsMovable, true );
+ this->setFlag( QGraphicsItem::ItemSendsGeometryChanges, true );
+ this->setCacheMode( QGraphicsItem::DeviceCoordinateCache );
+ this->setAcceptHoverEvents( true );
+ this->setAcceptDrops( true );
+ this->setZValue( -1 );
+ this->updateRepresentation( );
}
-void Node::addEdge(Edge *edge)
+// -------------------------------------------------------------------------
+PipelineEditor::Node::
+~Node( )
{
- edgeList << edge;
- edge->adjust();
}
-QList<Edge *> Node::edges() const
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+addEdge( PipelineEditor::Edge* edge )
{
- return edgeList;
+ this->m_Edges << edge;
+ edge->adjust( );
}
-void Node::calculateForces()
+// -------------------------------------------------------------------------
+QList< PipelineEditor::Edge* > PipelineEditor::Node::
+edges( ) const
{
- if (!scene() || scene()->mouseGrabberItem() == this) {
- newPos = pos();
- return;
- }
+ return( this->m_Edges );
+}
+
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+updateRepresentation( )
+{
+ typedef cpPlugins::Interface::DataObject _TData;
+ typedef cpPlugins::Interface::ProcessObject _TFilter;
+
+ if( this->m_Object == NULL )
+ return;
+
+ // Try to infere type
+ _TData* d = dynamic_cast< _TData* >( this->m_Object );
+ _TFilter* f = dynamic_cast< _TFilter* >( this->m_Object );
+ if( d == NULL && f == NULL )
+ return;
+
+ // Label and its bounds
+ QFontMetricsF fm( this->m_Canvas->font( ) );
+ this->m_Label = this->m_Object->GetName( );
+ this->m_Label += "\n";
+ this->m_Label += this->m_Object->GetClassName( ).c_str( );
+ this->m_Bounds = fm.boundingRect( this->m_Label );
+
+ // Create ports representation
+ this->m_Inputs.clear( );
+ this->m_Outputs.clear( );
+ this->m_InputPorts.clear( );
+ this->m_OutputPorts.clear( );
+ if( f != NULL )
+ {
+ // Get filter's inputs and outputs
+ f->GetInputsNames( this->m_Inputs );
+ f->GetOutputsNames( this->m_Outputs );
- // Sum up all forces pushing this item away
- qreal xvel = 0;
- qreal yvel = 0;
- foreach (QGraphicsItem *item, scene()->items()) {
- Node *node = qgraphicsitem_cast<Node *>(item);
- if (!node)
- continue;
+ // Correct height
+ unsigned int nIn = this->m_Inputs.size( );
+ unsigned int nOut = this->m_Outputs.size( );
+ qreal n =
+ qreal( ( ( ( ( nIn > nOut )? nIn: nOut ) << 1 ) + 1 ) * PORT_SIZE );
+ qreal h = this->m_Bounds.height( );
+ if( n > h )
+ this->m_Bounds.setHeight( n );
- QPointF vec = mapToItem(node, 0, 0);
- qreal dx = vec.x();
- qreal dy = vec.y();
- double l = 2.0 * (dx * dx + dy * dy);
- if (l > 0) {
- xvel += (dx * 150.0) / l;
- yvel += (dy * 150.0) / l;
- }
- }
+ // Get bounds values
+ qreal rt = this->m_Bounds.top( ) - qreal( PORT_SIZE );
+ qreal rb = this->m_Bounds.bottom( ) + qreal( PORT_SIZE );
+ qreal rl = this->m_Bounds.left( ) - qreal( PORT_SIZE );
+ qreal rr = this->m_Bounds.right( ) + qreal( PORT_SIZE );
- // Now subtract all forces pulling items together
- double weight = (edgeList.size() + 1) * 10;
- foreach (Edge *edge, edgeList) {
- QPointF vec;
- if (edge->sourceNode() == this)
- vec = mapToItem(edge->destNode(), 0, 0);
+ // Add some space to the geometry
+ this->m_Bounds.setTop( rt );
+ this->m_Bounds.setBottom( rb );
+ this->m_Bounds.setLeft( rl );
+ this->m_Bounds.setRight( rr );
+ qreal rh = this->m_Bounds.height( );
+
+ // Create ports
+ QSizeF ps( qreal( PORT_SIZE ), qreal( PORT_SIZE ) );
+ std::set< std::string >* ports[] =
+ { &( this->m_Inputs ), &( this->m_Outputs ) };
+ for( unsigned int pId = 0; pId < 2; ++pId )
+ {
+ qreal h = qreal( ( ( ports[ pId ]->size( ) << 1 ) + 1 ) * PORT_SIZE );
+ qreal off = qreal( PORT_SIZE );
+ if( rh > h )
+ off += ( rh - h ) / qreal( 2 );
+ for( auto i = ports[ pId ]->begin( ); i != ports[ pId ]->end( ); ++i )
+ {
+ if( pId == 0 )
+ this->m_InputPorts[ *i ] =
+ QRectF( QPointF( rl, rt + off ), ps );
else
- vec = mapToItem(edge->sourceNode(), 0, 0);
- xvel -= vec.x() / weight;
- yvel -= vec.y() / weight;
- }
+ this->m_OutputPorts[ *i ] =
+ QRectF( QPointF( rr - qreal( PORT_SIZE ), rt + off ), ps );
+ off += qreal( PORT_SIZE < 1 );
+
+ } // rof
- if (qAbs(xvel) < 0.1 && qAbs(yvel) < 0.1)
- xvel = yvel = 0;
+ } // rof
- QRectF sceneRect = scene()->sceneRect();
- newPos = pos() + QPointF(xvel, yvel);
- newPos.setX(qMin(qMax(newPos.x(), sceneRect.left() + 10), sceneRect.right() - 10));
- newPos.setY(qMin(qMax(newPos.y(), sceneRect.top() + 10), sceneRect.bottom() - 10));
+ } // fi
- newPos = pos(); // + QPointF(xvel, yvel);
+ // Some other initializations
+ this->m_SelectedPort = NULL;
}
-bool Node::advance()
+// -------------------------------------------------------------------------
+QRectF PipelineEditor::Node::
+boundingRect( ) const
{
- if (newPos == pos())
- return false;
+ return( this->m_Bounds );
+}
- setPos(newPos);
- return true;
+// -------------------------------------------------------------------------
+QPainterPath PipelineEditor::Node::
+shape( ) const
+{
+ QPainterPath path;
+ path.addRect( this->m_Bounds );
+ return( path );
}
-QRectF Node::boundingRect() const
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+paint(
+ QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget
+ )
{
-#if defined(Q_OS_SYMBIAN) || defined(Q_WS_MAEMO_5)
- // Add some extra space around the circle for easier touching with finger
- qreal adjust = 30;
- return QRectF( -10 - adjust, -10 - adjust,
- 20 + adjust * 2, 20 + adjust * 2);
-#else
- qreal adjust = 2;
- return QRectF( -10 - adjust, -10 - adjust,
- 23 + adjust, 23 + adjust);
-#endif
+ // Draw main box
+ QRectF rect = this->boundingRect( );
+ painter->drawRect( rect );
+ painter->drawText( rect, Qt::AlignCenter, this->m_Label );
+
+ // Draw ports
+ std::map< std::string, QRectF >* ports[] =
+ { &( this->m_InputPorts ), &( this->m_OutputPorts ) };
+ for( unsigned int pId = 0; pId < 2; ++pId )
+ for( auto i = ports[ pId ]->begin( ); i != ports[ pId ]->end( ); ++i )
+ painter->drawRect( i->second );
+
+ // Draw clicked port
+ if( this->m_SelectedPort != NULL )
+ {
+ painter->setBrush( Qt::green );
+ painter->drawEllipse( *( this->m_SelectedPort ) );
+
+ } // fi
}
-QPainterPath Node::shape() const
+// -------------------------------------------------------------------------
+QVariant PipelineEditor::Node::
+itemChange( GraphicsItemChange change, const QVariant& value )
{
- QPainterPath path;
-#if defined(Q_OS_SYMBIAN) || defined(Q_WS_MAEMO_5)
- // Add some extra space around the circle for easier touching with finger
- path.addEllipse( -40, -40, 80, 80);
-#else
- path.addEllipse(-10, -10, 20, 20);
-#endif
- return path;
+ switch( change )
+ {
+ case QGraphicsItem::ItemPositionHasChanged:
+ foreach( Edge* edge, this->m_Edges )
+ edge->update( );
+ this->m_Canvas->itemMoved( );
+ break;
+ default:
+ break;
+ } // hctiws
+ return( this->QGraphicsItem::itemChange( change, value ) );
}
-void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+mousePressEvent( QGraphicsSceneMouseEvent* event )
{
- painter->setPen(Qt::NoPen);
- painter->setBrush(Qt::darkGray);
- painter->drawEllipse(-7, -7, 20, 20);
+ if( this->m_SelectedPort != NULL && this->m_Canvas != NULL )
+ {
+ if( event->button( ) == Qt::LeftButton )
+ {
+ QDrag* drag = new QDrag( this->m_Canvas );
+ QMimeData* mimeData = new QMimeData( );
- QPointF text_point = this->boundingRect( ).bottomRight( ) + this->pos( );
- painter->drawText( text_point, this->m_Label.c_str( ) );
- std::cout << text_point.x( ) << " " << text_point.y( ) << std::endl;
+ // mimeData->setText( "drag_data" );
+ qulonglong address = reinterpret_cast< qulonglong >( this );
+ QByteArray ba;
+ ba.setNum( address );
+ mimeData->setData( "source_node", ba );
+ drag->setMimeData( mimeData );
+ // TODO: drag->setPixmap( iconPixmap );
- QRadialGradient gradient(-3, -3, 10);
- if (option->state & QStyle::State_Sunken) {
- gradient.setCenter(3, 3);
- gradient.setFocalPoint(3, 3);
- gradient.setColorAt(1, QColor(Qt::yellow).light(120));
- gradient.setColorAt(0, QColor(Qt::darkYellow).light(120));
- } else {
- gradient.setColorAt(0, Qt::yellow);
- gradient.setColorAt(1, Qt::darkYellow);
- }
- painter->setBrush(gradient);
+ this->m_DraggingPort = true;
+ Qt::DropAction dropAction = drag->exec( );
+ this->m_DraggingPort = false;
- painter->setPen(QPen(Qt::black, 0));
- painter->drawEllipse(-10, -10, 20, 20);
+ } // fi
+ }
+ else
+ {
+ } // fi
+
+ /* TODO
+ Qt::MouseButton btn = event->button( );
+ if( btn == Qt::LeftButton )
+ {
+ std::string name = this->toolTip( ).toStdString( );
+ if( name != this->m_Object->GetName( ) )
+ {
+ // Get clicked port, if any
+ QPointF pos = event->buttonDownPos( btn );
+ auto iIt = this->m_InputPorts.find( name );
+ auto oIt = this->m_OutputPorts.find( name );
+ this->m_SelectedPort = NULL;
+ if( iIt != this->m_InputPorts.end( ) )
+ if( iIt->second.contains( pos ) )
+ this->m_SelectedPort = &( iIt->second );
+ if( this->m_SelectedPort == NULL && oIt != this->m_OutputPorts.end( ) )
+ if( oIt->second.contains( pos ) )
+ this->m_SelectedPort = &( oIt->second );
+
+ } // fi
+
+ } // fi
+ */
+ this->update( );
+ this->QGraphicsItem::mousePressEvent( event );
}
-QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
{
- switch (change) {
- case ItemPositionHasChanged:
- foreach (Edge *edge, edgeList)
- edge->adjust();
- graph->itemMoved();
- break;
- default:
- break;
- };
+ this->update( );
+ this->QGraphicsItem::mouseReleaseEvent( event );
+}
- return QGraphicsItem::itemChange(change, value);
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event )
+{
}
-void Node::mousePressEvent(QGraphicsSceneMouseEvent *event)
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+hoverMoveEvent( QGraphicsSceneHoverEvent* event )
{
- update();
- QGraphicsItem::mousePressEvent(event);
+ this->_selectPort( event->pos( ) );
}
-void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+hoverLeaveEvent( QGraphicsSceneHoverEvent* event )
{
- update();
- QGraphicsItem::mouseReleaseEvent(event);
+ this->_deselectPort( );
+}
+
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+dragMoveEvent( QGraphicsSceneDragDropEvent* event )
+{
+ this->_selectPort( event->pos( ) );
+}
+
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+dragLeaveEvent( QGraphicsSceneDragDropEvent* event )
+{
+ this->_deselectPort( );
+}
+
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+dropEvent( QGraphicsSceneDragDropEvent* event )
+{
+ // Get vertices and directionality
+ bool ok;
+ qulonglong address =
+ event->mimeData( )->data( "source_node" ).toULongLong( &ok );
+ Node* src = reinterpret_cast< Node* >( address );
+ if( src == NULL )
+ return;
+ Node* des = this;
+ if( src->m_SelectedPortIsInput )
+ {
+ des = src;
+ src = this;
+
+ } // fi
+
+ // Discard if a loop is detected
+ if( src == des )
+ return;
+
+ // Get edge data
+ const QRectF* srcPort = src->m_SelectedPort;
+ const QRectF* desPort = des->m_SelectedPort;
+ std::string srcName = src->m_Object->GetName( );
+ std::string desName = des->m_Object->GetName( );
+ std::string srcPortName = src->toolTip( ).toStdString( );
+ std::string desPortName = des->toolTip( ).toStdString( );
+
+ Edge* e = new Edge( src, des, srcPort, desPort );
+ src->addEdge( e );
+ des->addEdge( e );
+ if( this->m_Canvas != NULL )
+ this->m_Canvas->scene( )->addItem( e );
+}
+
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+_selectPort( const QPointF& pos )
+{
+ if( this->m_DraggingPort )
+ return;
+
+ const QRectF* prevPort = this->m_SelectedPort;
+
+ // Check ports
+ std::map< std::string, QRectF >* ports[] =
+ { &( this->m_InputPorts ), &( this->m_OutputPorts ) };
+ bool found = false;
+ for( unsigned int pId = 0; pId < 2 && !found; ++pId )
+ {
+ for(
+ auto i = ports[ pId ]->begin( );
+ i != ports[ pId ]->end( ) && !found;
+ ++i
+ )
+ {
+ if( i->second.contains( pos ) )
+ {
+ this->setToolTip( i->first.c_str( ) );
+ this->m_SelectedPort = &( i->second );
+ this->m_SelectedPortIsInput = ( pId == 0 );
+ found = true;
+
+ } // fi
+
+ } // rof
+
+ } // rof
+ if( !found )
+ {
+ this->setToolTip( this->m_Object->GetName( ) );
+ this->m_SelectedPort = NULL;
+
+ } // fi
+ if( prevPort != this->m_SelectedPort )
+ this->update( );
+}
+
+// -------------------------------------------------------------------------
+void PipelineEditor::Node::
+_deselectPort( )
+{
+ if( !( this->m_DraggingPort ) )
+ {
+ this->m_SelectedPort = NULL;
+ this->update( );
+
+ } // fi
}
+// eof - $RCSfile$