1 /****************************************************************************
2 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
4 ** This file is part of the KD Gantt library.
6 ** This file may be used under the terms of the GNU General Public
7 ** License versions 2.0 or 3.0 as published by the Free Software
8 ** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
9 ** included in the packaging of this file. Alternatively you may (at
10 ** your option) use any later version of the GNU General Public
11 ** License if such license has been publicly approved by
12 ** Klarälvdalens Datakonsult AB (or its successors, if any).
14 ** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
15 ** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
16 ** A PARTICULAR PURPOSE. Klarälvdalens Datakonsult AB reserves all rights
17 ** not expressly granted herein.
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 **********************************************************************/
23 #include "kdganttgraphicsscene.h"
24 #include "kdganttgraphicsscene_p.h"
25 #include "kdganttgraphicsitem.h"
26 #include "kdganttconstraint.h"
27 #include "kdganttconstraintgraphicsitem.h"
28 #include "kdganttitemdelegate.h"
29 #include "kdganttabstractrowcontroller.h"
30 #include "kdganttdatetimegrid.h"
31 #include "kdganttsummaryhandlingproxymodel.h"
32 #include "kdganttgraphicsview.h"
35 #include <QGraphicsSceneHelpEvent>
36 #include <QTextDocument>
44 /*!\class KDGantt::GraphicsScene
48 using namespace KDGantt
;
50 GraphicsScene::Private::Private( GraphicsScene
* _q
)
53 itemDelegate( new ItemDelegate( _q
) ),
55 grid( &default_grid
),
57 summaryHandlingModel( new SummaryHandlingProxyModel( _q
) ),
60 default_grid
.setStartDateTime( QDateTime::currentDateTime().addDays( -1 ) );
63 void GraphicsScene::Private::resetConstraintItems()
65 q
->clearConstraintItems();
66 if ( constraintModel
.isNull() ) return;
67 QList
<Constraint
> clst
= constraintModel
->constraints();
68 Q_FOREACH( Constraint c
, clst
) {
69 createConstraintItem( c
);
74 void GraphicsScene::Private::createConstraintItem( const Constraint
& c
)
76 GraphicsItem
* sitem
= q
->findItem( summaryHandlingModel
->mapFromSource( c
.startIndex() ) );
77 GraphicsItem
* eitem
= q
->findItem( summaryHandlingModel
->mapFromSource( c
.endIndex() ) );
79 if ( sitem
&& eitem
) {
80 ConstraintGraphicsItem
* citem
= new ConstraintGraphicsItem( c
);
81 sitem
->addStartConstraint( citem
);
82 eitem
->addEndConstraint( citem
);
88 //q->insertConstraintItem( c, citem );
91 // Delete the constraint item, and clean up pointers in the start- and end item
92 void GraphicsScene::Private::deleteConstraintItem( ConstraintGraphicsItem
*citem
)
94 //qDebug()<<"GraphicsScene::Private::deleteConstraintItem citem="<<(void*)citem;
98 Constraint c
= citem
->constraint();
99 GraphicsItem
* item
= items
.value( summaryHandlingModel
->mapFromSource( c
.startIndex() ), 0 );
101 //qDebug()<<"GraphicsScene::Private::deleteConstraintItem startConstraints"<<item<<(void*)citem;
102 item
->removeStartConstraint( citem
);
103 } //else qDebug()<<"GraphicsScene::Private::deleteConstraintItem"<<c.startIndex()<<"start item not found";
104 item
= items
.value( summaryHandlingModel
->mapFromSource( c
.endIndex() ), 0 );
106 //qDebug()<<"GraphicsScene::Private::deleteConstraintItem endConstraints"<<item<<(void*)citem;
107 item
->removeEndConstraint( citem
);
108 } //else qDebug()<<"GraphicsScene::Private::deleteConstraintItem"<<c.endIndex()<<"end item not found";
109 //qDebug()<<"GraphicsScene::Private::deleteConstraintItem"<<citem<<"deleted";
113 void GraphicsScene::Private::deleteConstraintItem( const Constraint
& c
)
115 //qDebug()<<"GraphicsScene::Private::deleteConstraintItem c="<<c;
116 deleteConstraintItem( findConstraintItem( c
) );
120 /* Very useful functional to compose functions.
121 * Unfortunately an SGI extension and not standard
123 template<typename Operation1
,typename Operation2
>
124 struct unary_compose
: public std::unary_function
<typename
Operation1::result_type
,typename
Operation2::argument_type
> {
125 unary_compose( const Operation1
& f
, const Operation2
& g
) : _f( f
), _g( g
) {}
127 inline typename
Operation1::result_type
operator()( const typename
Operation2::argument_type
& arg
) {
128 return _f( _g( arg
) );
135 template<typename Operation1
,typename Operation2
>
136 inline unary_compose
<Operation1
,Operation2
> compose1( const Operation1
& f
, const Operation2
& g
)
138 return unary_compose
<Operation1
,Operation2
>( f
, g
);
142 ConstraintGraphicsItem
* GraphicsScene::Private::findConstraintItem( const Constraint
& c
) const
144 GraphicsItem
* item
= items
.value( summaryHandlingModel
->mapFromSource( c
.startIndex() ), 0 );
146 QList
<ConstraintGraphicsItem
*> clst
= item
->startConstraints();
147 QList
<ConstraintGraphicsItem
*>::iterator it
= clst
.begin();
148 //qDebug()<<"GraphicsScene::Private::findConstraintItem start:"<<c<<item<<clst;
149 for( ; it
!= clst
.end() ; ++it
)
150 if ((*it
)->constraint() == c
)
152 if ( it
!= clst
.end() ) {
156 item
= items
.value( summaryHandlingModel
->mapFromSource( c
.endIndex() ), 0 );
158 QList
<ConstraintGraphicsItem
*> clst
= item
->endConstraints();
159 QList
<ConstraintGraphicsItem
*>::iterator it
= clst
.begin();
160 //qDebug()<<"GraphicsScene::Private::findConstraintItem end:"<<c<<item<<clst;
161 for( ; it
!= clst
.end() ; ++it
)
162 if ((*it
)->constraint() == c
)
164 if ( it
!= clst
.end() ) {
168 //qDebug()<<"GraphicsScene::Private::findConstraintItem No item or constraintitem"<<c;
172 GraphicsScene::GraphicsScene( QObject
* parent
)
173 : QGraphicsScene( parent
), _d( new Private( this ) )
178 GraphicsScene::~GraphicsScene()
180 clearConstraintItems();
186 void GraphicsScene::init()
188 setConstraintModel( new ConstraintModel( this ) );
189 connect( d
->grid
, SIGNAL( gridChanged() ), this, SLOT( slotGridChanged() ) );
192 /* NOTE: The delegate should really be a property
193 * of the view, but that doesn't really fit at
196 void GraphicsScene::setItemDelegate( ItemDelegate
* delegate
)
198 if ( !d
->itemDelegate
.isNull() && d
->itemDelegate
->parent()==this ) delete d
->itemDelegate
;
199 d
->itemDelegate
= delegate
;
203 ItemDelegate
* GraphicsScene::itemDelegate() const
205 return d
->itemDelegate
;
208 QAbstractItemModel
* GraphicsScene::model() const
210 assert(!d
->summaryHandlingModel
.isNull());
211 return d
->summaryHandlingModel
->sourceModel();
214 void GraphicsScene::setModel( QAbstractItemModel
* model
)
216 assert(!d
->summaryHandlingModel
.isNull());
217 d
->summaryHandlingModel
->setSourceModel(model
);
218 d
->grid
->setModel( d
->summaryHandlingModel
);
219 setSelectionModel( new QItemSelectionModel( model
, this ) );
222 QAbstractProxyModel
* GraphicsScene::summaryHandlingModel() const
224 return d
->summaryHandlingModel
;
227 void GraphicsScene::setSummaryHandlingModel( QAbstractProxyModel
* proxyModel
)
229 proxyModel
->setSourceModel( model() );
230 d
->summaryHandlingModel
= proxyModel
;
233 void GraphicsScene::setRootIndex( const QModelIndex
& idx
)
235 d
->grid
->setRootIndex( idx
);
238 QModelIndex
GraphicsScene::rootIndex() const
240 return d
->grid
->rootIndex();
243 ConstraintModel
* GraphicsScene::constraintModel() const
245 return d
->constraintModel
;
248 void GraphicsScene::setConstraintModel( ConstraintModel
* cm
)
250 if ( !d
->constraintModel
.isNull() ) {
251 disconnect( d
->constraintModel
);
253 d
->constraintModel
= cm
;
255 connect( cm
, SIGNAL( constraintAdded( const Constraint
& ) ), this, SLOT( slotConstraintAdded( const Constraint
& ) ) );
256 connect( cm
, SIGNAL( constraintRemoved( const Constraint
& ) ), this, SLOT( slotConstraintRemoved( const Constraint
& ) ) );
257 d
->resetConstraintItems();
260 void GraphicsScene::setSelectionModel( QItemSelectionModel
* smodel
)
262 d
->selectionModel
= smodel
;
263 // TODO: update selection from model and connect signals
266 QItemSelectionModel
* GraphicsScene::selectionModel() const
268 return d
->selectionModel
;
271 void GraphicsScene::setRowController( AbstractRowController
* rc
)
273 d
->rowController
= rc
;
276 AbstractRowController
* GraphicsScene::rowController() const
278 return d
->rowController
;
281 void GraphicsScene::setGrid( AbstractGrid
* grid
)
283 QAbstractItemModel
* model
= d
->grid
->model();
284 if ( grid
== 0 ) grid
= &d
->default_grid
;
285 if ( d
->grid
) disconnect( d
->grid
);
287 connect( d
->grid
, SIGNAL( gridChanged() ), this, SLOT( slotGridChanged() ) );
288 d
->grid
->setModel( model
);
292 AbstractGrid
* GraphicsScene::grid() const
297 void GraphicsScene::setReadOnly( bool ro
)
302 bool GraphicsScene::isReadOnly() const
307 /* Returns the index with column=0 fromt the
308 * same row as idx and with the same parent.
309 * This is used to traverse the tree-structure
312 QModelIndex
GraphicsScene::mainIndex( const QModelIndex
& idx
)
315 if ( idx
.isValid() ) {
316 return idx
.model()->index( idx
.row(), 0,idx
.parent() );
318 return QModelIndex();
325 /*! Returns the index pointing to the last column
326 * in the same row as idx. This can be thought of
327 * as in "inverse" of mainIndex()
329 QModelIndex
GraphicsScene::dataIndex( const QModelIndex
& idx
)
332 if ( idx
.isValid() ) {
333 const QAbstractItemModel
* model
= idx
.model();
334 return model
->index( idx
.row(), model
->columnCount( idx
.parent() )-1,idx
.parent() );
336 return QModelIndex();
343 /*! Creates a new item of type type.
344 * TODO: If the user should be allowed to override
345 * this in any way, it needs to be in View!
347 GraphicsItem
* GraphicsScene::createItem( ItemType type
) const
351 case TypeEvent
: return 0;
352 case TypeTask
: return new TaskItem
;
353 case TypeSummary
: return new SummaryItem
;
357 //qDebug() << "GraphicsScene::createItem("<<type<<")";
359 return new GraphicsItem
;
362 void GraphicsScene::updateRow( const QModelIndex
& rowidx
)
364 //qDebug() << "GraphicsScene::updateRow("<<rowidx<<")";
365 if ( !rowidx
.isValid() ) return;
366 const QAbstractItemModel
* model
= rowidx
.model(); // why const?
368 assert( rowController() );
369 assert( model
== summaryHandlingModel() );
371 const QModelIndex sidx
= summaryHandlingModel()->mapToSource( rowidx
);
372 const Span rg
=rowController()->rowGeometry( sidx
);
373 for ( int col
= 0; col
< summaryHandlingModel()->columnCount( rowidx
.parent() ); ++col
) {
374 const QModelIndex idx
= summaryHandlingModel()->index( rowidx
.row(), col
, rowidx
.parent() );
375 const int itemtype
= summaryHandlingModel()->data( idx
, ItemTypeRole
).toInt();
376 if ( itemtype
== TypeNone
) {
380 if ( itemtype
== TypeMulti
) {
383 while ( ( child
= idx
.child( cr
, 0 ) ).isValid() ) {
384 GraphicsItem
* item
= findItem( child
);
386 item
= createItem( static_cast<ItemType
>( itemtype
) );
387 item
->setIndex( child
);
388 insertItem( child
, item
);
390 item
->updateItem( rg
, child
);
391 setSceneRect( sceneRect().united( item
->boundingRect() ) );
396 if ( summaryHandlingModel()->data( rowidx
.parent(), ItemTypeRole
).toInt() == TypeMulti
) continue;
398 GraphicsItem
* item
= findItem( idx
);
400 item
= createItem( static_cast<ItemType
>( itemtype
) );
401 item
->setIndex( idx
);
402 insertItem(idx
, item
);
404 item
->updateItem( rg
, idx
);
405 setSceneRect( sceneRect().united( item
->boundingRect() ) );
410 void GraphicsScene::insertItem( const QPersistentModelIndex
& idx
, GraphicsItem
* item
)
412 if ( !d
->constraintModel
.isNull() ) {
413 // Create items for constraints
414 const QModelIndex sidx
= summaryHandlingModel()->mapToSource( idx
);
415 const QList
<Constraint
> clst
= d
->constraintModel
->constraintsForIndex( sidx
);
416 Q_FOREACH( Constraint c
, clst
) {
417 QModelIndex other_idx
;
418 if ( c
.startIndex() == sidx
) {
419 other_idx
= c
.endIndex();
420 GraphicsItem
* other_item
= d
->items
.value(summaryHandlingModel()->mapFromSource( other_idx
),0);
421 if ( !other_item
) continue;
422 ConstraintGraphicsItem
* citem
= new ConstraintGraphicsItem( c
);
423 item
->addStartConstraint( citem
);
424 other_item
->addEndConstraint( citem
);
426 } else if ( c
.endIndex() == sidx
) {
427 other_idx
= c
.startIndex();
428 GraphicsItem
* other_item
= d
->items
.value(summaryHandlingModel()->mapFromSource( other_idx
),0);
429 if ( !other_item
) continue;
430 ConstraintGraphicsItem
* citem
= new ConstraintGraphicsItem( c
);
431 other_item
->addStartConstraint( citem
);
432 item
->addEndConstraint( citem
);
435 assert( 0 ); // Impossible
439 d
->items
.insert( idx
, item
);
443 void GraphicsScene::removeItem( const QModelIndex
& idx
)
445 //qDebug() << "GraphicsScene::removeItem("<<idx<<")";
446 QHash
<QPersistentModelIndex
,GraphicsItem
*>::iterator it
= d
->items
.find( idx
);
447 if ( it
!= d
->items
.end() ) {
448 GraphicsItem
* item
= *it
;
449 // We have to remove the item from the list first because
450 // there is a good chance there will be reentrant calls
451 d
->items
.erase( it
);
452 //qDebug() << "GraphicsScene::removeItem item="<<item;
454 // Remove any constraintitems starting here
455 const QList
<ConstraintGraphicsItem
*> clst
= item
->startConstraints();
456 //qDebug()<<"GraphicsScene::removeItem start:"<<clst;
457 Q_FOREACH( ConstraintGraphicsItem
* citem
, clst
) {
458 //qDebug()<<"GraphicsScene::removeItem start citem="<<citem;
459 d
->deleteConstraintItem( citem
);
462 {// Remove any constraintitems ending here
463 const QList
<ConstraintGraphicsItem
*> clst
= item
->endConstraints();
464 //qDebug()<<"GraphicsScene::removeItem end:"<<clst;
465 Q_FOREACH( ConstraintGraphicsItem
* citem
, clst
) {
466 //qDebug()<<"GraphicsScene::removeItem end citem="<<citem;
467 d
->deleteConstraintItem( citem
);
470 // Get rid of the item
475 GraphicsItem
* GraphicsScene::findItem( const QModelIndex
& idx
) const
477 if ( !idx
.isValid() ) return 0;
478 assert( idx
.model() == summaryHandlingModel() );
479 QHash
<QPersistentModelIndex
,GraphicsItem
*>::const_iterator it
= d
->items
.find( idx
);
480 return ( it
!= d
->items
.end() )?*it
:0;
483 GraphicsItem
* GraphicsScene::findItem( const QPersistentModelIndex
& idx
) const
485 if ( !idx
.isValid() ) return 0;
486 assert( idx
.model() == summaryHandlingModel() );
487 QHash
<QPersistentModelIndex
,GraphicsItem
*>::const_iterator it
= d
->items
.find( idx
);
488 return ( it
!= d
->items
.end() )?*it
:0;
491 void GraphicsScene::clearItems()
494 qDeleteAll( items() );
498 void GraphicsScene::updateItems()
500 for ( QHash
<QPersistentModelIndex
,GraphicsItem
*>::iterator it
= d
->items
.begin();
501 it
!= d
->items
.end(); ++it
) {
502 GraphicsItem
* const item
= it
.value();
503 const QPersistentModelIndex
& idx
= it
.key();
504 item
->updateItem( Span( item
->pos().y(), item
->rect().height() ), idx
);
508 void GraphicsScene::deleteSubtree( const QModelIndex
& _idx
)
510 QModelIndex idx
= dataIndex( _idx
);
512 for ( int i
= 0; i
< summaryHandlingModel()->rowCount( _idx
); ++i
) {
513 deleteSubtree( summaryHandlingModel()->index( i
, summaryHandlingModel()->columnCount(_idx
)-1, _idx
) );
518 ConstraintGraphicsItem
* GraphicsScene::findConstraintItem( const Constraint
& c
) const
520 return d
->findConstraintItem( c
);
523 void GraphicsScene::clearConstraintItems()
526 // d->constraintItems.clearConstraintItems();
529 void GraphicsScene::slotConstraintAdded( const Constraint
& c
)
531 d
->createConstraintItem( c
);
534 void GraphicsScene::slotConstraintRemoved( const Constraint
& c
)
536 d
->deleteConstraintItem( c
);
539 void GraphicsScene::slotGridChanged()
546 void GraphicsScene::helpEvent( QGraphicsSceneHelpEvent
*helpEvent
)
548 #ifndef QT_NO_TOOLTIP
549 QGraphicsItem
*item
= itemAt( helpEvent
->scenePos() );
550 if ( GraphicsItem
* gitem
= qgraphicsitem_cast
<GraphicsItem
*>( item
) ) {
551 QToolTip::showText(helpEvent
->screenPos(), gitem
->ganttToolTip());
552 } else if ( ConstraintGraphicsItem
* citem
= qgraphicsitem_cast
<ConstraintGraphicsItem
*>( item
) ) {
553 QToolTip::showText(helpEvent
->screenPos(), citem
->ganttToolTip());
555 QGraphicsScene::helpEvent( helpEvent
);
557 #endif /* QT_NO_TOOLTIP */
560 void GraphicsScene::drawBackground( QPainter
* painter
, const QRectF
& rect
)
562 d
->grid
->paintGrid( painter
, sceneRect(), rect
, d
->rowController
);
565 void GraphicsScene::itemEntered( const QModelIndex
& idx
)
570 void GraphicsScene::itemPressed( const QModelIndex
& idx
)
575 void GraphicsScene::itemClicked( const QModelIndex
& idx
)
580 void GraphicsScene::itemDoubleClicked( const QModelIndex
& idx
)
582 emit
doubleClicked( idx
);
585 void GraphicsScene::setDragSource( GraphicsItem
* item
)
587 d
->dragSource
= item
;
590 GraphicsItem
* GraphicsScene::dragSource() const
592 return d
->dragSource
;
595 QRectF
GraphicsScene::printRect(bool drawRowLabels
)
597 assert(rowController());
599 qreal leftEdge
= sceneRect().left();
600 QVector
<QGraphicsTextItem
*> labelItems
;
602 labelItems
.reserve(d
->items
.size());
603 qreal textWidth
= 0.;
604 qreal rowHeight
= 0.;
605 {Q_FOREACH( GraphicsItem
* item
, d
->items
) {
606 QModelIndex sidx
= summaryHandlingModel()->mapToSource( item
->index() );
607 if( sidx
.parent().isValid() && sidx
.parent().data( ItemTypeRole
).toInt() == TypeMulti
) {
610 const Span rg
=rowController()->rowGeometry( sidx
);
611 const QString txt
= item
->index().data( Qt::DisplayRole
).toString();
612 QGraphicsTextItem
* ti
= new QGraphicsTextItem(txt
,0,this);
613 ti
->setPos( 0, rg
.start() );
614 if( ti
->document()->size().width() > textWidth
) textWidth
= ti
->document()->size().width();
615 if( rg
.length() > rowHeight
) rowHeight
= rg
.length();
618 {Q_FOREACH( QGraphicsTextItem
* item
, labelItems
) {
619 item
->setPos( leftEdge
-textWidth
-rowHeight
, item
->pos().y() );
623 QRectF res
= itemsBoundingRect();
624 qDeleteAll(labelItems
);
625 //qDebug()<<"printRect()"<<res;
629 void GraphicsScene::print( QPainter
* painter
, const QRectF
& target
, const QRectF
& source
, bool drawRowLabels
, GraphicsView
*view
)
631 QRectF
targetRect(target
);
633 assert(rowController());
635 QVector
<QGraphicsTextItem
*> labelItems
;
637 labelItems
.reserve(d
->items
.size());
638 qreal leftEdge
= sceneRect().left();
639 qreal textWidth
= 0.;
640 qreal rowHeight
= 0.;
641 {Q_FOREACH( GraphicsItem
* item
, d
->items
) {
642 QModelIndex sidx
= summaryHandlingModel()->mapToSource( item
->index() );
643 if( sidx
.parent().isValid() && sidx
.parent().data( ItemTypeRole
).toInt() == TypeMulti
) {
646 const Span rg
=rowController()->rowGeometry( sidx
);
647 const QString txt
= item
->index().data( Qt::DisplayRole
).toString();
648 QGraphicsTextItem
* ti
= new QGraphicsTextItem(txt
,0,this);
649 ti
->setPos( 0, rg
.start() );
650 if( ti
->document()->size().width() > textWidth
) textWidth
= ti
->document()->size().width();
651 if( rg
.length() > rowHeight
) rowHeight
= rg
.length();
654 {Q_FOREACH( QGraphicsTextItem
* item
, labelItems
) {
655 item
->setPos( leftEdge
-textWidth
-rowHeight
, item
->pos().y() );
659 QRectF
oldSceneRect( sceneRect() );
660 setSceneRect( itemsBoundingRect() );
661 QRectF sourceRect
= source
;
662 //qDebug()<<"GraphicsScene::print() 1"<<sceneRect()<<targetRect<<sourceRect;
664 Qt::AspectRatioMode mode
= Qt::KeepAspectRatio
;
665 QRectF s
= sourceRect
;
666 s
.setHeight( view
->headerHeight() );
667 qreal xr
= targetRect
.width() / sourceRect
.width();
668 qreal yr
= (qreal
)1.0;
669 if ( sourceRect
.height() > s
.height() ) {
670 yr
= targetRect
.height() / sourceRect
.height();
672 xr
= yr
= qMin( xr
, yr
);
673 QRectF t
= targetRect
;
674 t
.setHeight( s
.height() * yr
);
675 view
->renderHeader( painter
, t
, s
, mode
);
676 targetRect
.translate( (qreal
)0.0, t
.height() );
677 //qDebug()<<"GraphicsScene::print() 2"<<"t="<<t<<"s="<<s<<"yr="<<yr<<(s.height() * yr)<<"targetRect="<<targetRect;
679 //qDebug()<<"GraphicsScene::print() 3"<<sceneRect()<<targetRect<<sourceRect;
680 if ( targetRect
.width() > sourceRect
.width() && targetRect
.height() > sourceRect
.height() ) {
681 targetRect
.setSize( sourceRect
.size() );
682 render( painter
, targetRect
, sourceRect
, Qt::IgnoreAspectRatio
);
684 render( painter
, targetRect
, sourceRect
);
686 qDeleteAll(labelItems
);
688 setSceneRect( oldSceneRect
);
692 #include "moc_kdganttgraphicsscene.cpp"