Import doxygen generated api doc
[qanava.git] / src / qanGraphItemView.cpp
blobcf17bdd14b23a8e30c96198d272f5c9605134e2a
1 /*
2 Qanava - Graph drawing library for QT
3 Copyright (C) 2006 Benoit AUTHEMAN
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 //-----------------------------------------------------------------------------
21 // This file is a part of the Qanava software.
23 // \file canGraphItemView.cpp
24 // \author Benoit Autheman (benoit@libqanava.org)
25 // \date 2005 November 22
26 //-----------------------------------------------------------------------------
29 // Qanava headers
30 #include "./qanGraph.h"
31 #include "./qanEdge.h"
32 #include "./qanGraphItemView.h"
33 #include "./qanGraphItemModel.h"
34 #include "./qanGrid.h"
37 // STD headers
38 #include <cmath>
41 // QT headers
42 #include <QVBoxLayout>
43 #include <QTimer>
44 #include <QScrollBar>
47 //-----------------------------------------------------------------------------
48 namespace qan { // ::qan
51 /* View Construction *///------------------------------------------------------
52 /*!
53 \param graph Already available graph used in this view GraphItemModel.
54 \param backgroundColor Graph canvas background color.
55 \param autoExpand Model are fully explored when auto expansion is activated (default),
56 if false, only the first hierarchy level of the model is viewed,
57 and node must be expanded manually with method expandNode().
59 GraphItemView::GraphItemView( QWidget* parent, Graph* graph, QColor backgroundColor, QSize size, bool autoExpand ) :
60 QAbstractItemView( parent ),
61 _graphicsScene( 0 ),
62 _graphicsView( 0 ),
63 _graph( graph ),
64 _autoExpand( autoExpand ),
65 _layout( new Random( ) )
67 if ( _graph == 0 )
68 _graph = new Graph( );
70 registerGraphicNodeFactory( new NodeItem::DefaultFactory( ) );
72 _graphicsScene = new QGraphicsScene( this );
73 _graphicsView = new GraphicsView( _graphicsScene, this );
74 connect( _graphicsView, SIGNAL(itemDoubleClicked(QGraphicsItem*)), this, SLOT(graphicItemDoubleClicked(QGraphicsItem*)) );
76 QPalette palette;
77 palette.setColor( backgroundRole( ), backgroundColor );
78 viewport( )->setPalette( palette );
80 setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
81 setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
83 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
84 clear( );
85 update( );
88 GraphItemView::~GraphItemView( )
90 _styleManager.clear( );
93 /*!
94 \note Style manager is not cleared since this method original aim in GraphicsView only concern geometry.
96 void GraphItemView::clear( )
98 // Clear all mappings
99 _modelIndexNodeMap.clear( );
100 _modelIndexGraphicItemMap.clear( );
101 _graphicItemModelIndexMap.clear( );
102 _nodeGraphicItemMap.clear( );
104 // Clear all items in the graphic view
105 // FIXME: all items are removed, not just the one modelling the actual graph
106 QList< QGraphicsItem* > items = _graphicsView->scene( )->items( );
107 QList< QGraphicsItem* >::iterator itemIterator;
108 for( itemIterator = items.begin( ); itemIterator != items.end( ); ++itemIterator )
109 _graphicsView->scene( )->removeItem( *itemIterator );
111 if ( _layout != 0 )
112 delete _layout;
113 _layout = new Random( );
115 //-----------------------------------------------------------------------------
119 /* QT View Interface Management *///-------------------------------------------
120 void GraphItemView::setModel ( QAbstractItemModel* model )
122 // Clear this view (we must clear before setting the new model since model will be set to 0)
123 clear( );
124 QAbstractItemView::setModel( model );
127 QModelIndex GraphItemView::indexAt( const QPoint& p ) const
129 return QModelIndex( );
132 void GraphItemView::visitModelIndex( QAbstractItemModel& model, QModelIndex& modelIndex, QModelIndex& parentIndex, int depth )
134 if ( depth-- == 0 )
135 return;
137 if ( !modelIndex.isValid( ) )
138 return;
140 if ( GraphItemView::getModelIndexNode( modelIndex ) != 0 )
141 return;
143 mapIndex( modelIndex, parentIndex );
145 for ( int i = 0; i < model.rowCount( modelIndex ); i++ )
147 QModelIndex index = model.index( i, 0, modelIndex );
148 visitModelIndex( model, index, modelIndex, depth );
152 void GraphItemView::mapIndex( QModelIndex& index, QModelIndex& parent )
154 if ( !index.isValid( ) )
155 return;
157 // Check if the index isn't already mapped
158 if ( _modelIndexNodeMap.find( index.internalPointer( ) ) != _modelIndexNodeMap.end( ) )
159 return;
161 if ( index.internalPointer( ) != 0 && _graph != 0 )
163 // Map a model index to a graph node
164 Node* node = static_cast< Node* >( index.internalPointer( ) );
165 if ( node == 0 || !_graph->hasNode( node ) )
167 // Create a new graph node and map it
168 node = _graph->insertNode( "" );
169 updateModelIndexNode( index, node );
171 // Create an edge between this node and its parent node
172 Node* parentNode = getModelIndexNode( parent );
173 if ( node != 0 && parentNode != 0 )
174 _graph->addEdge( *parentNode, *node );
178 // Map a model index to a graphic item
179 if ( node != 0 )
181 _modelIndexNodeMap.insert( index.internalPointer( ), node );
183 // Get node type associed style
184 Style* style = _styleManager.getEmptyStyle( );
185 Style* nodeStyle = _styleManager.getStyle( node );
186 if ( nodeStyle != 0 )
187 style = nodeStyle;
188 nodeStyle = _styleManager.getStyle( node->getType( ) );
189 if ( nodeStyle != 0 )
190 style = nodeStyle;
192 AbstractNodeItem* nodeItem = 0;
193 if ( style != 0 )
194 nodeItem = createGraphicNode( *node, _styleManager, *style, 0, _graphicsScene, QPoint( 1, 1 ), node->getLabel( ) );
195 if ( nodeItem != 0 )
197 _modelIndexGraphicItemMap.insert( index.internalPointer( ), nodeItem );
198 _nodeGraphicItemMap.insert( node, nodeItem );
199 QGraphicsItem* gi = nodeItem->getGraphicsItem( );
200 _graphicItemModelIndexMap.insert( nodeItem->getGraphicsItem( ), index.internalPointer( ) );
201 _nodeModelIndexMap.insert( node, index );
204 // Generate arrows for the model
205 Edge::Set edges = Edge::Set::fromList( node->getInEdges( ) );
206 edges.unite( Edge::Set::fromList( node->getOutEdges( ) ) );
207 foreach ( Edge* edge, edges )
209 EdgeGraphicItemMap::iterator canvasEdgeIter = _edgeGraphicItemMap.find( edge );
210 if ( canvasEdgeIter == _edgeGraphicItemMap.end( ) )
212 NodeItem* src = static_cast< NodeItem* >( getNodeGraphicItem( &edge->getSrc( ) ) );
213 NodeItem* dst = static_cast< NodeItem* >( getNodeGraphicItem( &edge->getDst( ) ) );
214 if ( src != 0 && dst != 0 )
216 EdgeItem* edgeItem = new EdgeItem( 0, _graphicsScene, src, dst );
217 _edgeGraphicItemMap.insert( edge, edgeItem );
220 else
221 canvasEdgeIter.value( )->show( ); // The arrow might have been previously hiden, so set it visible again
227 void GraphItemView::updateModelIndexGraphicItem( QModelIndex& index )
229 if ( !index.isValid( ) )
230 return;
234 /*! This method is usefull when this view model is a QT model (ex QDirModel) where items are
235 not internally mapped to an existing graph node. For example, if a node has just been
236 created for a specific model index, the notification mecanism of the model has eventually
237 not been used, this method can be used to force the associed node data update).
239 \param index Item index in this view model that must be used to refresh its associed node internal data.
240 \param node Allow user to manually provide the node mapped to the given model item index.
242 void GraphItemView::updateModelIndexNode( QModelIndex& index, Node* node )
244 if ( !index.isValid( ) )
245 return;
247 if ( index.column( ) != 0 )
248 return;
250 if ( node == 0 )
251 node = getModelIndexNode( index );
252 if ( node != 0 )
254 double positionX( 0.0 );
255 double positionY( 0.0 );
256 double dimensionX( 75.0 );
257 double dimensionY( 45.0 );
259 QVariant v = model( )->data( index, Qt::DisplayRole );
260 QString content = ( v.type( ) == QVariant::String ? v.toString( ).toAscii( ) : "ERROR" );
262 v = model( )->data( index, Qt::UserRole + GraphItemModel::POSITION_X );
263 positionX = ( v.type( ) == QVariant::Double ? v.toDouble( ) : 0.0 );
264 v = model( )->data( index, Qt::UserRole + GraphItemModel::POSITION_Y );
265 positionY = ( v.type( ) == QVariant::Double ? v.toDouble( ) : 0.0 );
267 v = model( )->data( index, Qt::UserRole + GraphItemModel::DIMENSION_X );
268 dimensionX = ( v.type( ) == QVariant::Double ? v.toDouble( ) : dimensionX );
269 v = model( )->data( index, Qt::UserRole + GraphItemModel::DIMENSION_Y );
270 dimensionY = ( v.type( ) == QVariant::Double ? v.toDouble( ) : dimensionY );
272 node->setLabel( content );
273 node->setPosition( positionX, positionY );
274 node->setDimension( dimensionX, dimensionY );
276 // Update node style (if such a style is present)
277 Style* style = _styleManager.getStyle( node );
278 if ( style == 0 )
280 style = new Style( "" );
281 _styleManager.setStyle( node, *style );
284 if ( style != 0 )
286 v = model( )->data( index, Qt::BackgroundColorRole );
287 QColor c = ( v.type( ) == QVariant::Color ? v.value<QColor>( ) : QColor( ) );
288 if ( c.isValid( ) )
289 style->addColor( "backcolor", c.red( ), c.green( ), c.blue( ) );
291 v = model( )->data( index, Qt::DecorationRole );
292 QIcon i = ( v.type( ) == QVariant::Icon ? v.value<QIcon>( ) : QIcon( ) );
293 if ( !i.isNull( ) )
294 style->addIcon( "icon", i );
297 update( ); // TODO: update uniquement pour la zone graphique du node
300 void GraphItemView::expand( const Node* node )
302 if ( model( ) != 0 )
304 QModelIndex noItem;
305 QModelIndex nodeIndex = _nodeModelIndexMap.value( node, QModelIndex( ) );
306 if ( nodeIndex.isValid( ) )
308 for ( int row = 0; row < model( )->rowCount( nodeIndex ); row++ )
310 QModelIndex subNodeIndex = model( )->index( row, 0, nodeIndex );
311 if ( subNodeIndex.isValid( ) )
312 visitModelIndex( *model( ), subNodeIndex, nodeIndex, ( _autoExpand ? 5000 : 1 ) );
318 void GraphItemView::graphicItemDoubleClicked( QGraphicsItem* item )
320 Node* node = getGraphicItemNode( item );
321 if ( node != 0 )
322 emit nodeDoubleClicked( node );
325 void GraphItemView::reset( )
327 QAbstractItemView::reset( );
329 if ( model( ) != 0 )
331 // Explore model and map it items to graphical elements
332 for ( int i = 0; i < model( )->rowCount( ); i++ )
334 QModelIndex index = model( )->index( i, 0 );
335 QModelIndex noItem;
336 visitModelIndex( *model( ), index, noItem, ( _autoExpand ? 5000 : 1 ) );
339 connect( model( ), SIGNAL( rowsInserted(const QModelIndex&,int,int) ), this, SLOT( rowsInserted(const QModelIndex&,int,int) ) );
340 connect( model( ), SIGNAL( rowsAboutToBeInserted(const QModelIndex&,int,int) ), this, SLOT( rowsAboutToBeInserted(const QModelIndex&,int,int) ) );
341 connect( model( ), SIGNAL( rowsRemoved(const QModelIndex&,int,int) ), this, SLOT( rowsRemoved(const QModelIndex&,int,int) ) );
342 connect( model( ), SIGNAL( rowsAboutToBeRemoved(const QModelIndex&,int,int) ), this, SLOT( rowsAboutToBeRemoved(const QModelIndex&,int,int) ) );
346 void GraphItemView::rowsAboutToBeInserted( const QModelIndex& parent, int start, int end )
350 void GraphItemView::rowsInserted( const QModelIndex& parent, int start, int end )
352 QModelIndex index( parent );
353 QAbstractItemView::rowsInserted( parent, start, end );
355 for ( int row = start; row < end; row++ )
357 QModelIndex nodeIndex = model( )->index( row, 0, parent );
358 visitModelIndex( *model( ), nodeIndex, const_cast< QModelIndex& >( parent ), ( _autoExpand ? 5000 : 1 ) );
362 void GraphItemView::rowsAboutToBeRemoved( const QModelIndex & parent, int start, int end )
364 //QAbstractItemView::rowsAboutToBeRemoved( parent, start, end );
365 for ( int row = start; row < end; row++ )
367 QModelIndex nodeIndex = model( )->index( row, 0, parent );
368 if ( !nodeIndex.isValid( ) )
369 continue;
370 ModelIndexGraphicItemMap::iterator indexIter = _modelIndexGraphicItemMap.find( nodeIndex.internalPointer( ) );
371 if ( indexIter != _modelIndexGraphicItemMap.end( ) )
373 NodeItem* nodeItem = static_cast< NodeItem* >( indexIter.value( ) );
375 Node* node = getGraphicItemNode( nodeItem );
376 if ( node != 0 )
378 // Remove arrows
379 Edge::List edges;
380 std::copy( node->getInEdges( ).begin( ), node->getInEdges( ).end( ), std::back_insert_iterator< Edge::List >( edges ) );
381 std::copy( node->getOutEdges( ).begin( ), node->getOutEdges( ).end( ), std::back_insert_iterator< Edge::List >( edges ) );
382 for ( Edge::List::iterator edgeIter = edges.begin( ); edgeIter != edges.end( ); edgeIter++ )
384 EdgeGraphicItemMap::iterator canvasEdgeIter = _edgeGraphicItemMap.find( *edgeIter );
385 if ( canvasEdgeIter != _edgeGraphicItemMap.end( ) )
387 ( canvasEdgeIter.value( ) )->update( 0, 0 );
388 _edgeGraphicItemMap.remove( *edgeIter );
392 _nodeGraphicItemMap.remove( node );
395 _modelIndexNodeMap.remove( nodeIndex.internalPointer( ) );
396 _modelIndexGraphicItemMap.remove( nodeIndex.internalPointer( ) );
397 _graphicItemModelIndexMap.remove( nodeItem->getGraphicsItem( ) );
398 delete nodeItem;
403 void GraphItemView::rowsRemoved( const QModelIndex& parent, int first, int last )
408 void GraphItemView::dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight )
410 if ( !topLeft.isValid( ) )
411 return;
413 QModelIndex changedDataIndex( topLeft );
414 Node* topLeftNode = getModelIndexNode( changedDataIndex );
415 if ( topLeftNode != 0 )
417 NodeItem* topLeftItem = static_cast< NodeItem* >( getNodeGraphicItem( topLeftNode ) );
418 if ( topLeftItem != 0 )
420 Style* style = _styleManager.getStyle( topLeftNode );
421 if ( style == 0 )
422 style = _styleManager.getStyle( topLeftNode->getType( ) );
423 if ( style != 0 )
425 //topLeftItem->update( topLeftNode->getLabel( ), *style ); // FIXME
426 //topLeftItem->updateGeometry( *style );
428 else
430 Style emptyStyle( "empty" );
431 //topLeftItem->update( topLeftNode->getLabel( ), emptyStyle ); // FIXME
432 //topLeftItem->updateGeometry( emptyStyle );
437 update( );
439 //-----------------------------------------------------------------------------
443 /* Model Index and Graphic Item Management *///--------------------------------
444 Node* GraphItemView::getGraphicItemNode( const QGraphicsItem* item )
446 if ( item == 0 )
447 return 0;
448 GraphicItemModelIndexMap::const_iterator graphicItemIter = _graphicItemModelIndexMap.find( item );
449 if ( graphicItemIter != _graphicItemModelIndexMap.end( ) )
451 // Return the graph node associed to the model index found
452 ModelIndexNodeMap::iterator nodeIter = _modelIndexNodeMap.find( graphicItemIter.value( ) );
453 if ( _modelIndexNodeMap.contains( graphicItemIter.value( ) ) )
454 return _modelIndexNodeMap.value( graphicItemIter.value( ) );
456 return 0;
459 QGraphicsItem* GraphItemView::getNodeGraphicItem( const Node* node )
461 if ( node == 0 )
462 return 0;
463 NodeGraphicItemMap::const_iterator nodeIter = _nodeGraphicItemMap.find( node );
464 if ( nodeIter != _nodeGraphicItemMap.end( ) )
465 return ( nodeIter.value( ) )->getGraphicsItem( );
466 return 0;
469 Node* GraphItemView::getModelIndexNode( QModelIndex& index )
471 if ( !index.isValid( ) )
472 return 0;
474 if ( _modelIndexNodeMap.contains( index.internalPointer( ) ) )
475 return _modelIndexNodeMap.value( index.internalPointer( ), 0 );
476 return 0;
479 Node* GraphItemView::getCurrentNode( QPoint& p )
481 /*Item::List collisions;
482 //getCanvas( )->getCollisions( p, collisions ); // FIXME
483 if ( collisions.begin( ) != collisions.end( ) )
485 Item* item = *collisions.begin( );
486 //if ( getCanvas( ) != 0 && !getCanvas( )->isFreed( item ) ) // FIXME
487 // return getGraphicItemNode( item );
489 return 0;
491 //-----------------------------------------------------------------------------
495 /* Style Management *///-------------------------------------------------------
496 void GraphItemView::applyStyle( Node* node, Style* style )
498 if ( node != 0 && style != 0 )
500 _styleManager.setStyle( node, *style );
502 // Update node graphic item with the style content
503 NodeGraphicItemMap::const_iterator nodeIter = _nodeGraphicItemMap.find( node );
504 if ( nodeIter != _nodeGraphicItemMap.end( ) )
506 AbstractNodeItem* item = nodeIter.value( );
507 item->setStyle( style );
511 //-----------------------------------------------------------------------------
515 /* Layout Management *///------------------------------------------------------
517 \note The ownership of the given layout object is transferred to this view.
518 \param layout New layout to use with this view (must not be 0).
520 void GraphItemView::setGraphLayout( Layout* layout, QProgressDialog* progress )
522 if ( layout == 0 )
523 return;
525 if ( _layout != 0 )
527 //delete _layout;
528 _layout = 0;
530 _layout = layout;
533 void GraphItemView::layoutGraph( QProgressDialog* progress, Layout* layout, int step, Node* except )
535 if ( _graph == 0 )
536 return;
537 if ( _layout == 0 && layout == 0 )
538 return;
540 QRectF r = getGraphicsView( )->sceneRect( );
541 r.setWidth( std::max( r.width( ), ( double )width( ) ) );
542 r.setHeight( std::max( r.height( ), ( double )height( ) ) );
544 if ( layout != 0 )
545 layout->layout( *_graph, *getGraphicsView( )->getGrid( ), r, progress, step );
546 else if ( _layout != 0 && getGraphicsView( )->getGrid( ) != 0 )
547 _layout->layout( *_graph, *getGraphicsView( )->getGrid( ), r, progress, step );
549 // Update all nodes positions
551 NodeGraphicItemMap::iterator nodeIter = _nodeGraphicItemMap.begin( );
552 for ( ; nodeIter != _nodeGraphicItemMap.end( ); nodeIter++ )
554 const Node* node = nodeIter.key( );
555 AbstractNodeItem* item = nodeIter.value( );
556 if ( node != 0 && item != 0 && node != except )
557 item->getGraphicsItem( )->setPos( node->getPosition( )( 0 ), node->getPosition( )( 1 ) );
560 _graphicsView->update( );
561 update( );
564 VectorF GraphItemView::getBoundingBox( const Node::List& nodes )
566 VectorF bbox( 2 );
567 bbox( 0 ) = 0;
568 bbox( 1 ) = 0;
570 Node::List::const_iterator nodeIter = nodes.begin( );
571 for ( ; nodeIter != nodes.end( ); nodeIter++ )
573 const Node* node = *nodeIter;
574 const VectorF& position = node->getPosition( );
575 const VectorF& dimension = node->getDimension( );
577 if ( position( 0 ) + dimension( 0 ) > bbox( 0 ) )
578 bbox( 0 ) = position( 0 ) + dimension( 0 );
579 if ( position( 1 ) + dimension( 1 ) > bbox( 1 ) )
580 bbox( 1 ) = position( 1 ) + dimension( 1 );
582 return bbox;
584 //-----------------------------------------------------------------------------
588 /* ScrollArea Management *///--------------------------------------------------
589 void GraphItemView::resizeEvent( QResizeEvent* event )
591 int w = width( );
592 int h = height( );
593 _graphicsView->resize( w, h );
596 bool GraphItemView::eventFilter( QObject *o, QEvent *e )
598 return false;
600 //-----------------------------------------------------------------------------
604 /* Graphic Node Factory Management *///----------------------------------------
605 /*! Ownership for the factory is transfrerred to this graph item view.
606 \param factory Graphic node factory that must be used when generating node graphic counterpart.
608 void GraphItemView::registerGraphicNodeFactory( AbstractNodeItem::AbstractFactory* factory )
610 assert( factory != 0 );
611 if ( factory->isDefaultFactory( ) )
612 _factories.push_back( factory ); // Default factories must be tested after standard ones
613 else
614 _factories.push_front( factory );
618 \return A adequat graphic counterpart for node, or a node from a default factory, or 0 if no (default) factories are registered.
620 AbstractNodeItem* GraphItemView::createGraphicNode( Node& node, Style::Manager& styleManager, Style& style,
621 QGraphicsItem* parent, QGraphicsScene* scene,
622 QPoint origin, const QString& label )
624 AbstractNodeItem::AbstractFactory::List::iterator factoryIter = _factories.begin( );
625 for ( ; factoryIter != _factories.end( ); factoryIter++ )
627 AbstractNodeItem::AbstractFactory* factory = *factoryIter;
628 if ( factory->isDefaultFactory( ) ) // Default factories create graphic node whatever the node type is
629 return factory->create( node, styleManager, style, parent, scene, origin, label );
631 if ( node.getType( ) == factory->getNodeType( ) )
632 return factory->create( node, styleManager, style, parent, scene, origin, label );
634 return 0; // No adequat nor default factory found
636 //-----------------------------------------------------------------------------
639 } // ::qan
640 //-----------------------------------------------------------------------------