File indexing completed on 2024-05-12 15:54:26

0001 /*
0002  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB.  All rights reserved.
0003  *
0004  * This file is part of the KGantt library.
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation; either version 2 of
0009  * the License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "kganttgraphicsscene.h"
0021 #include "kganttgraphicsscene_p.h"
0022 #include "kganttgraphicsitem.h"
0023 #include "kganttconstraint.h"
0024 #include "kganttconstraintgraphicsitem.h"
0025 #include "kganttitemdelegate.h"
0026 #include "kganttabstractrowcontroller.h"
0027 #include "kganttabstractgrid.h"
0028 #include "kganttdatetimegrid.h"
0029 #include "kganttsummaryhandlingproxymodel.h"
0030 #include "kganttgraphicsview.h"
0031 #include "kganttprintingcontext.h"
0032 
0033 #include <QApplication>
0034 #include <QGraphicsSceneHelpEvent>
0035 #include <QPainter>
0036 #include <QPrinter>
0037 #include <QTextDocument>
0038 #include <QToolTip>
0039 #include <QSet>
0040 
0041 #include <QDebug>
0042 
0043 #include <functional>
0044 #include <algorithm>
0045 #include <cassert>
0046 
0047 // defines HAVE_PRINTER if support for printing should be included
0048 #ifdef _WIN32_WCE
0049     // There is no printer support under wince even if QT_NO_PRINTER is not set
0050 #else
0051 #ifndef QT_NO_PRINTER
0052     #define HAVE_PRINTER
0053 #endif
0054 #endif
0055 
0056 
0057 
0058 using namespace KGantt;
0059 
0060 GraphicsScene::Private::Private( GraphicsScene* _q )
0061     : q( _q ),
0062       dragSource( nullptr ),
0063       itemDelegate( new ItemDelegate( _q ) ),
0064       rowController( nullptr ),
0065       readOnly( false ),
0066       isPrinting( false ),
0067       drawColumnLabels( true ),
0068       labelsWidth( 0.0 ),
0069       summaryHandlingModel( new SummaryHandlingProxyModel( _q ) ),
0070       selectionModel( nullptr )
0071 {
0072     default_grid.setStartDateTime( QDateTime::currentDateTime().addDays( -1 ) );
0073 }
0074 
0075 GraphicsScene::Private::~Private()
0076 {
0077     delete grid;
0078 }
0079 
0080 void GraphicsScene::Private::clearConstraintItems()
0081 {
0082     for(ConstraintGraphicsItem *citem : constraintItems) {
0083         // remove constraint from items first
0084         for(GraphicsItem *item : items) {
0085             item->removeStartConstraint(citem);
0086             item->removeEndConstraint(citem);
0087         }
0088         q->removeItem(citem);
0089         delete citem;
0090     }
0091     constraintItems.clear();
0092 }
0093 
0094 void GraphicsScene::Private::resetConstraintItems()
0095 {
0096     clearConstraintItems();
0097     if ( constraintModel.isNull() ) return;
0098     const QList<Constraint> clst = constraintModel->constraints();
0099     for ( const Constraint& c : clst ) {
0100         createConstraintItem( c );
0101     }
0102     q->updateItems();
0103 }
0104 
0105 void GraphicsScene::Private::createConstraintItem( const Constraint& c )
0106 {
0107     GraphicsItem* sitem = q->findItem( summaryHandlingModel->mapFromSource( c.startIndex() ) );
0108     GraphicsItem* eitem = q->findItem( summaryHandlingModel->mapFromSource( c.endIndex() ) );
0109 
0110     if ( sitem && eitem ) {
0111         ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c );
0112         sitem->addStartConstraint( citem );
0113         eitem->addEndConstraint( citem );
0114         constraintItems.append( citem );
0115         q->addItem( citem );
0116     }
0117 
0118     //q->insertConstraintItem( c, citem );
0119 }
0120 
0121 // Delete the constraint item, and clean up pointers in the start- and end item
0122 void GraphicsScene::Private::deleteConstraintItem( ConstraintGraphicsItem *citem )
0123 {
0124     //qDebug()<<"GraphicsScene::Private::deleteConstraintItem citem="<<citem;
0125     if ( citem == nullptr ) {
0126         return;
0127     }
0128     Constraint c = citem->constraint();
0129     GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr );
0130     if ( item ) {
0131         item->removeStartConstraint( citem );
0132     }
0133     item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr );
0134     if ( item ) {
0135         item->removeEndConstraint( citem );
0136     }
0137     constraintItems.removeAt(constraintItems.indexOf(citem));
0138     delete citem;
0139 }
0140 
0141 void GraphicsScene::Private::deleteConstraintItem( const Constraint& c )
0142 {
0143     deleteConstraintItem( findConstraintItem( c ) );
0144 }
0145 
0146 ConstraintGraphicsItem* GraphicsScene::Private::findConstraintItem( const Constraint& c ) const
0147 {
0148     GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr );
0149     if ( item ) {
0150         const QList<ConstraintGraphicsItem*> clst = item->startConstraints();
0151         QList<ConstraintGraphicsItem*>::const_iterator it = clst.begin();
0152         for ( ; it != clst.end() ; ++it ) {
0153             if ( c.compareIndexes((*it)->constraint()) )
0154                 break;
0155         }
0156         if ( it != clst.end() ) {
0157             return *it;
0158         }
0159     }
0160     item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr );
0161     if ( item ) {
0162         const QList<ConstraintGraphicsItem*> clst = item->endConstraints();
0163         QList<ConstraintGraphicsItem*>::const_iterator it = clst.begin();
0164         for ( ; it != clst.end() ; ++it ) {
0165             if ( c.compareIndexes( (*it)->constraint() ) )
0166                 break;
0167         }
0168         if ( it != clst.end() ) {
0169             return *it;
0170         }
0171     }
0172     return nullptr;
0173 }
0174 
0175 // NOTE: we might get here after indexes are invalidated, so cannot do any controlled cleanup
0176 void GraphicsScene::Private::clearItems()
0177 {
0178     for(GraphicsItem *item : items) {
0179         q->removeItem(item);
0180         delete item;
0181     }
0182     items.clear();
0183     // do last to avoid cleaning up items
0184     clearConstraintItems();
0185 }
0186 
0187 AbstractGrid *GraphicsScene::Private::getGrid()
0188 {
0189     if (grid.isNull()) {
0190         return static_cast<AbstractGrid*>(&default_grid);
0191     }
0192     return grid.data();
0193 }
0194 
0195 const AbstractGrid *GraphicsScene::Private::getGrid() const
0196 {
0197     if (grid.isNull()) {
0198         return static_cast<const AbstractGrid*>(&default_grid);
0199     }
0200     return grid.data();
0201 }
0202 
0203 GraphicsScene::GraphicsScene( QObject* parent )
0204     : QGraphicsScene( parent ), _d( new Private( this ) )
0205 {
0206     init();
0207 }
0208 
0209 GraphicsScene::~GraphicsScene()
0210 {
0211     qDeleteAll( items() );
0212     delete _d;
0213 }
0214 
0215 #define d d_func()
0216 
0217 void GraphicsScene::init()
0218 {
0219     setItemIndexMethod( QGraphicsScene::NoIndex );
0220     setConstraintModel( new ConstraintModel( this ) );
0221     connect( d->getGrid(), SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) );
0222 }
0223 
0224 /* NOTE: The delegate should really be a property
0225  * of the view, but that doesn't really fit at
0226  * this time
0227  */
0228 void GraphicsScene::setItemDelegate( ItemDelegate* delegate )
0229 {
0230     if ( !d->itemDelegate.isNull() && d->itemDelegate->parent()==this ) delete d->itemDelegate;
0231     d->itemDelegate = delegate;
0232     update();
0233 }
0234 
0235 ItemDelegate* GraphicsScene::itemDelegate() const
0236 {
0237     return d->itemDelegate;
0238 }
0239 
0240 QAbstractItemModel* GraphicsScene::model() const
0241 {
0242     assert(!d->summaryHandlingModel.isNull());
0243     return d->summaryHandlingModel->sourceModel();
0244 }
0245 
0246 void GraphicsScene::setModel( QAbstractItemModel* model )
0247 {
0248     assert(!d->summaryHandlingModel.isNull());
0249     d->summaryHandlingModel->setSourceModel(model);
0250     d->getGrid()->setModel( d->summaryHandlingModel );
0251     setSelectionModel( new QItemSelectionModel( model, this ) );
0252 }
0253 
0254 QAbstractProxyModel* GraphicsScene::summaryHandlingModel() const
0255 {
0256     return d->summaryHandlingModel;
0257 }
0258 
0259 void GraphicsScene::setSummaryHandlingModel( QAbstractProxyModel* proxyModel )
0260 {
0261     proxyModel->setSourceModel( model() );
0262     d->summaryHandlingModel = proxyModel;
0263 }
0264 
0265 void GraphicsScene::setRootIndex( const QModelIndex& idx )
0266 {
0267     d->getGrid()->setRootIndex( idx );
0268 }
0269 
0270 QModelIndex GraphicsScene::rootIndex() const
0271 {
0272     return d->getGrid()->rootIndex();
0273 }
0274 
0275 ConstraintModel* GraphicsScene::constraintModel() const
0276 {
0277     return d->constraintModel;
0278 }
0279 
0280 void GraphicsScene::setConstraintModel( ConstraintModel* cm )
0281 {
0282     if ( !d->constraintModel.isNull() ) {
0283         d->constraintModel->disconnect( this );
0284         d->clearConstraintItems();
0285     }
0286     d->constraintModel = cm;
0287 
0288     connect( cm, SIGNAL(constraintAdded(KGantt::Constraint)),
0289              this, SLOT(slotConstraintAdded(KGantt::Constraint)) );
0290     connect( cm, SIGNAL(constraintRemoved(KGantt::Constraint)),
0291              this, SLOT(slotConstraintRemoved(KGantt::Constraint)) );
0292     d->resetConstraintItems();
0293 }
0294 
0295 void GraphicsScene::setSelectionModel( QItemSelectionModel* smodel )
0296 {
0297     if (d->selectionModel) {
0298         d->selectionModel->disconnect( this );
0299     }
0300     d->selectionModel = smodel;
0301     if (smodel) {
0302         connect(d->selectionModel, SIGNAL(modelChanged(QAbstractItemModel*)),
0303                 this, SLOT(selectionModelChanged(QAbstractItemModel*)));
0304         connect( smodel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),
0305                  this, SLOT(slotSelectionChanged(const QItemSelection&,const QItemSelection&)) );
0306     }
0307 }
0308 
0309 QItemSelectionModel* GraphicsScene::selectionModel() const
0310 {
0311     return d->selectionModel;
0312 }
0313 
0314 void GraphicsScene::setRowController( AbstractRowController* rc )
0315 {
0316     d->rowController = rc;
0317 }
0318 
0319 AbstractRowController* GraphicsScene::rowController() const
0320 {
0321     return d->rowController;
0322 }
0323 
0324 AbstractGrid *GraphicsScene::takeGrid()
0325 {
0326     AbstractGrid *grid = d->grid;
0327     grid->disconnect( this );
0328     d->grid = nullptr;
0329     if (grid) {
0330         // revert to the default_grid
0331         connect( &d->default_grid, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) );
0332     }
0333     return grid;
0334 }
0335 
0336 void GraphicsScene::setGrid( AbstractGrid* grid )
0337 {
0338     QAbstractItemModel* model = nullptr;
0339     if ( d->getGrid() ) {
0340         d->getGrid()->disconnect( this );
0341         model = d->getGrid()->model();
0342     }
0343     delete d->grid;
0344     d->grid = grid;
0345     connect( d->getGrid(), SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) );
0346     d->getGrid()->setModel( model );
0347     slotGridChanged();
0348 }
0349 
0350 // Returns the explicitly set grid
0351 AbstractGrid* GraphicsScene::grid() const
0352 {
0353     return d->grid;
0354 }
0355 
0356 // May also return the default_grid if a grid has not been set
0357 const AbstractGrid *GraphicsScene::getGrid() const
0358 {
0359     return d->getGrid();
0360 }
0361 
0362 void GraphicsScene::setReadOnly( bool ro )
0363 {
0364     d->readOnly = ro;
0365 }
0366 
0367 bool GraphicsScene::isReadOnly() const
0368 {
0369     return d->readOnly;
0370 }
0371 
0372 /* Returns the index with column=0 fromt the
0373  * same row as idx and with the same parent.
0374  * This is used to traverse the tree-structure
0375  * of the model
0376  */
0377 QModelIndex GraphicsScene::mainIndex( const QModelIndex& idx )
0378 {
0379 #if 0
0380     if ( idx.isValid() ) {
0381         return idx.model()->index( idx.row(), 0,idx.parent() );
0382     } else {
0383         return QModelIndex();
0384     }
0385 #else
0386     return idx;
0387 #endif
0388 }
0389 
0390 
0391 QModelIndex GraphicsScene::dataIndex( const QModelIndex& idx )
0392 {
0393 #if 0
0394     if ( idx.isValid() ) {
0395         const QAbstractItemModel* model = idx.model();
0396         return model->index( idx.row(), model->columnCount( idx.parent() )-1,idx.parent() );
0397     } else {
0398         return QModelIndex();
0399     }
0400 #else
0401     return idx;
0402 #endif
0403 }
0404 
0405 
0406 GraphicsItem* GraphicsScene::createItem( ItemType type ) const
0407 {
0408 #if 0
0409     TODO For 3.0
0410     assert(views().count() == 1);
0411     GraphicsView *v = qobject_cast<GraphicsView*>(views().first());
0412     assert(v);
0413     return v->createItem(type);
0414 #else
0415     Q_UNUSED(type)
0416     return new GraphicsItem;
0417 #endif
0418 }
0419 
0420 void GraphicsScene::Private::recursiveUpdateMultiItem( const Span& span, const QModelIndex& idx )
0421 {
0422     //qDebug() << "recursiveUpdateMultiItem("<<span<<idx<<")";
0423     GraphicsItem* item = q->findItem( idx );
0424     const int itemtype = summaryHandlingModel->data( idx, ItemTypeRole ).toInt();
0425     if (!item) {
0426         item = q->createItem( static_cast<ItemType>( itemtype ) );
0427         item->setIndex( idx );
0428         q->insertItem( idx, item);
0429     }
0430     item->updateItem( span, idx );
0431     QModelIndex child;
0432     int cr = 0;
0433     while ( ( child = summaryHandlingModel->index( cr, 0, idx ) ).isValid() ) {
0434         recursiveUpdateMultiItem( span, child );
0435         ++cr;
0436     }
0437 }
0438 
0439 void GraphicsScene::updateRow( const QModelIndex& rowidx )
0440 {
0441     //qDebug() << "GraphicsScene::updateRow("<<rowidx<<")" << rowidx.data( Qt::DisplayRole );
0442     if ( !rowidx.isValid() ) return;
0443 #if !defined(NDEBUG)
0444     const QAbstractItemModel* model = rowidx.model(); // why const?
0445 #endif
0446     assert( model );
0447     assert( rowController() );
0448     assert( model == summaryHandlingModel() );
0449 
0450     const QModelIndex sidx = summaryHandlingModel()->mapToSource( rowidx );
0451     Span rg = rowController()->rowGeometry( sidx );
0452     for ( QModelIndex treewalkidx = sidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
0453         if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
0454              && !rowController()->isRowExpanded( treewalkidx )) {
0455             rg = rowController()->rowGeometry( treewalkidx );
0456         }
0457     }
0458 
0459     bool blocked = blockSignals( true );
0460     for ( int col = 0; col < summaryHandlingModel()->columnCount( rowidx.parent() ); ++col ) {
0461         const QModelIndex idx = summaryHandlingModel()->index( rowidx.row(), col, rowidx.parent() );
0462         const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
0463         const int itemtype = summaryHandlingModel()->data( idx, ItemTypeRole ).toInt();
0464         const bool isExpanded = rowController()->isRowExpanded( sidx );
0465         if ( itemtype == TypeNone ) {
0466             removeItem( idx );
0467             continue;
0468         }
0469         if ( itemtype == TypeMulti && !isExpanded ) {
0470             d->recursiveUpdateMultiItem( rg, idx );
0471         } else {
0472             if ( summaryHandlingModel()->data( rowidx.parent(), ItemTypeRole ).toInt() == TypeMulti && !isExpanded ) {
0473                 //continue;
0474             }
0475 
0476             GraphicsItem* item = findItem( idx );
0477             if (!item) {
0478                 item = createItem( static_cast<ItemType>( itemtype ) );
0479                 item->setIndex( idx );
0480                 insertItem(idx, item);
0481             }
0482             const Span span = rowController()->rowGeometry( sidx );
0483             item->updateItem( span, idx );
0484         }
0485     }
0486     blockSignals( blocked );
0487 }
0488 
0489 void GraphicsScene::insertItem( const QPersistentModelIndex& idx, GraphicsItem* item )
0490 {
0491     if ( !d->constraintModel.isNull() ) {
0492         // Create items for constraints
0493         const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
0494         const QList<Constraint> clst = d->constraintModel->constraintsForIndex( sidx );
0495         for ( const Constraint& c :  clst ) {
0496             QModelIndex other_idx;
0497             if ( c.startIndex() == sidx ) {
0498                 other_idx = c.endIndex();
0499                 GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr);
0500                 if ( !other_item ) continue;
0501                 ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c );
0502                 item->addStartConstraint( citem );
0503                 other_item->addEndConstraint( citem );
0504                 d->constraintItems.append( citem );
0505                 addItem( citem );
0506             } else if ( c.endIndex() == sidx ) {
0507                 other_idx = c.startIndex();
0508                 GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr);
0509                 if ( !other_item ) continue;
0510                 ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c );
0511                 other_item->addStartConstraint( citem );
0512                 item->addEndConstraint( citem );
0513                 d->constraintItems.append( citem );
0514                 addItem( citem );
0515             } else {
0516                 assert( 0 ); // Impossible
0517             }
0518         }
0519     }
0520     d->items.insert( idx, item );
0521     addItem( item );
0522 }
0523 
0524 void GraphicsScene::removeItem( const QModelIndex& idx )
0525 {
0526     //qDebug() << "GraphicsScene::removeItem("<<idx<<")";
0527     QHash<QPersistentModelIndex,GraphicsItem*>::iterator it = d->items.find( idx );
0528     if ( it != d->items.end() ) {
0529         GraphicsItem* item = *it;
0530         assert( item );
0531         // We have to remove the item from the list first because
0532         // there is a good chance there will be reentrant calls
0533         d->items.erase( it );
0534         {
0535             // Remove any constraintitems attached
0536 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
0537             // TODO remove when we depend on 5.14+
0538             const QSet<ConstraintGraphicsItem*> clst = QSet<ConstraintGraphicsItem*>::fromList( item->startConstraints() ) +
0539                                                        QSet<ConstraintGraphicsItem*>::fromList( item->endConstraints() );
0540 #else
0541             const QList<ConstraintGraphicsItem*> lst1 = item->startConstraints();
0542             const QList<ConstraintGraphicsItem*> lst2 = item->endConstraints();
0543             const QSet<ConstraintGraphicsItem*> clst = QSet<ConstraintGraphicsItem*>( lst1.begin(), lst1.end() ) +
0544                                                        QSet<ConstraintGraphicsItem*>( lst2.begin(), lst2.end() );
0545 #endif
0546             for ( ConstraintGraphicsItem* citem : clst ) {
0547                 d->deleteConstraintItem( citem );
0548             }
0549         }
0550         // Get rid of the item
0551         delete item;
0552     }
0553 }
0554 
0555 GraphicsItem* GraphicsScene::findItem( const QModelIndex& idx ) const
0556 {
0557     if ( !idx.isValid() ) return nullptr;
0558     assert( idx.model() == summaryHandlingModel() );
0559     QHash<QPersistentModelIndex,GraphicsItem*>::const_iterator it = d->items.find( idx );
0560     return ( it != d->items.end() )?*it:nullptr;
0561 }
0562 
0563 GraphicsItem* GraphicsScene::findItem( const QPersistentModelIndex& idx ) const
0564 {
0565     if ( !idx.isValid() ) return nullptr;
0566     assert( idx.model() == summaryHandlingModel() );
0567     QHash<QPersistentModelIndex,GraphicsItem*>::const_iterator it = d->items.find( idx );
0568     return ( it != d->items.end() )?*it:nullptr;
0569 }
0570 
0571 void GraphicsScene::clearItems()
0572 {
0573     d->clearItems();
0574 }
0575 
0576 void GraphicsScene::updateItems()
0577 {
0578     for ( QHash<QPersistentModelIndex,GraphicsItem*>::iterator it = d->items.begin();
0579           it != d->items.end(); ++it ) {
0580         GraphicsItem* const item = it.value();
0581         const QPersistentModelIndex& idx = it.key();
0582         item->updateItem( Span( item->pos().y(), item->rect().height() ), idx );
0583     }
0584     invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
0585 }
0586 
0587 void GraphicsScene::deleteSubtree( const QModelIndex& _idx )
0588 {
0589     QModelIndex idx = dataIndex( _idx );
0590     if ( !idx.model() ) return;
0591     const QModelIndex parent( idx.parent() );
0592     const int colcount = idx.model()->columnCount( parent );
0593     {for ( int i = 0; i < colcount; ++i ) {
0594         removeItem( summaryHandlingModel()->index(idx.row(), i, parent ) );
0595     }}
0596     const int rowcount = summaryHandlingModel()->rowCount( _idx );
0597     {for ( int i = 0; i < rowcount; ++i ) {
0598         deleteSubtree( summaryHandlingModel()->index( i, summaryHandlingModel()->columnCount(_idx)-1, _idx ) );
0599     }}
0600 }
0601 
0602 
0603 ConstraintGraphicsItem* GraphicsScene::findConstraintItem( const Constraint& c ) const
0604 {
0605     return d->findConstraintItem( c );
0606 }
0607 
0608 void GraphicsScene::slotConstraintAdded( const KGantt::Constraint& c )
0609 {
0610     d->createConstraintItem( c );
0611 }
0612 
0613 void GraphicsScene::slotConstraintRemoved( const KGantt::Constraint& c )
0614 {
0615     d->deleteConstraintItem( c );
0616 }
0617 
0618 void GraphicsScene::slotGridChanged()
0619 {
0620     updateItems();
0621     update();
0622     Q_EMIT gridChanged();
0623 }
0624 
0625 void GraphicsScene::selectionModelChanged(QAbstractItemModel *model)
0626 {
0627     Q_UNUSED(model)
0628 }
0629 
0630 void GraphicsScene::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
0631 {
0632     for (const QModelIndex &idx : deselected.indexes()) {
0633         GraphicsItem *item = findItem(idx.model() == d->summaryHandlingModel ? idx : d->summaryHandlingModel->mapFromSource(idx));
0634         if (item) {
0635             item->setSelected(false);
0636         }
0637     }
0638     for (const QModelIndex &idx : selected.indexes()) {
0639         GraphicsItem *item = findItem(idx.model() == d->summaryHandlingModel ? idx : d->summaryHandlingModel->mapFromSource(idx));
0640         if (item) {
0641             item->setSelected(true);
0642         }
0643     }
0644     update();
0645 }
0646 
0647 void GraphicsScene::helpEvent( QGraphicsSceneHelpEvent *helpEvent )
0648 {
0649 #ifndef QT_NO_TOOLTIP
0650     QGraphicsItem *item = itemAt( helpEvent->scenePos(), QTransform() );
0651     if ( GraphicsItem* gitem = qgraphicsitem_cast<GraphicsItem*>( item ) ) {
0652         QToolTip::showText(helpEvent->screenPos(), gitem->ganttToolTip());
0653     } else if ( ConstraintGraphicsItem* citem = qgraphicsitem_cast<ConstraintGraphicsItem*>( item ) ) {
0654         QToolTip::showText(helpEvent->screenPos(), citem->ganttToolTip());
0655     } else {
0656         QGraphicsScene::helpEvent( helpEvent );
0657     }
0658 #endif /* QT_NO_TOOLTIP */
0659 }
0660 
0661 void GraphicsScene::drawBackground( QPainter* painter, const QRectF& _rect )
0662 {
0663     QRectF scn( sceneRect() );
0664     QRectF rect( _rect );
0665     if ( d->isPrinting && d->drawColumnLabels ) {
0666         QRectF headerRect( scn.topLeft()+QPointF( d->labelsWidth, 0 ),
0667                            QSizeF( scn.width()-d->labelsWidth, d->rowController->headerHeight() ));
0668 
0669         d->getGrid()->paintHeader( painter, headerRect, rect, 0, nullptr );
0670 
0671 #if 0
0672         /* We have to blank out the part of the header that is invisible during
0673         * normal rendering when we are printing.
0674         */
0675         QRectF labelsTabRect( scn.topLeft(), QSizeF( d->labelsWidth, headerRect.height() ) );
0676 
0677         QStyleOptionHeader opt;
0678         opt.rect = labelsTabRect.toRect();
0679         opt.text = QLatin1String("");
0680         opt.textAlignment = Qt::AlignCenter;
0681         style()->drawControl(QStyle::CE_Header, &opt, painter, 0);
0682 #endif
0683 
0684         scn.setTop( headerRect.bottom() );
0685         scn.setLeft( headerRect.left() );
0686         rect = rect.intersected( scn );
0687     }
0688     d->getGrid()->paintGrid( painter, scn, rect, d->rowController );
0689 
0690     d->getGrid()->drawBackground(painter, rect);
0691 }
0692 
0693 void GraphicsScene::drawForeground( QPainter* painter, const QRectF& rect )
0694 {
0695     d->getGrid()->drawForeground(painter, rect);
0696 }
0697 
0698 void GraphicsScene::itemEntered( const QModelIndex& idx )
0699 {
0700     Q_EMIT entered( idx );
0701 }
0702 
0703 void GraphicsScene::itemPressed( const QModelIndex& idx, QGraphicsSceneMouseEvent *event )
0704 {
0705     if (event->button() == Qt::LeftButton) {
0706         QItemSelectionModel::SelectionFlags flags;
0707         if (event->modifiers() & Qt::ControlModifier) {
0708             flags |= QItemSelectionModel::Toggle;
0709         } else {
0710             flags |= QItemSelectionModel::ClearAndSelect;
0711         }
0712         d->selectionModel->select(d->summaryHandlingModel->mapToSource(idx), flags);
0713     }
0714     Q_EMIT pressed( idx );
0715 }
0716 
0717 void GraphicsScene::itemClicked( const QModelIndex& idx )
0718 {
0719     Q_EMIT clicked( idx );
0720 }
0721 
0722 void GraphicsScene::itemDoubleClicked( const QModelIndex& idx )
0723 {
0724     Q_EMIT qrealClicked( idx );
0725 }
0726 
0727 void GraphicsScene::setDragSource( GraphicsItem* item )
0728 {
0729     d->dragSource = item;
0730 }
0731 
0732 GraphicsItem* GraphicsScene::dragSource() const
0733 {
0734     return d->dragSource;
0735 }
0736 
0737 
0738 void GraphicsScene::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
0739 {
0740 #ifndef HAVE_PRINTER
0741     Q_UNUSED( printer );
0742     Q_UNUSED( drawRowLabels );
0743     Q_UNUSED( drawColumnLabels );
0744 #else
0745     QPainter painter( printer );
0746     doPrint( &painter, printer->pageRect(), sceneRect().left(), sceneRect().right(), printer, drawRowLabels, drawColumnLabels );
0747 #endif
0748 }
0749 
0750 
0751 void GraphicsScene::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
0752 {
0753 #ifndef HAVE_PRINTER
0754     Q_UNUSED( printer );
0755     Q_UNUSED( start );
0756     Q_UNUSED( end );
0757     Q_UNUSED( drawRowLabels );
0758     Q_UNUSED( drawColumnLabels );
0759 #else
0760     QPainter painter( printer );
0761     doPrint( &painter, printer->pageRect(), start, end, printer, drawRowLabels, drawColumnLabels );
0762 #endif
0763 }
0764 
0765 
0766 void GraphicsScene::print( QPainter* painter, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
0767 {
0768     QRectF targetRect( _targetRect );
0769     if ( targetRect.isNull() ) {
0770         targetRect = sceneRect();
0771     }
0772 
0773     doPrint( painter, targetRect, sceneRect().left(), sceneRect().right(), nullptr, drawRowLabels, drawColumnLabels );
0774 }
0775 
0776 
0777 void GraphicsScene::print( QPainter* painter, qreal start, qreal end,
0778                            const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
0779 {
0780     QRectF targetRect( _targetRect );
0781     if ( targetRect.isNull() ) {
0782         targetRect = sceneRect();
0783     }
0784 
0785     doPrint( painter, targetRect, start, end, nullptr, drawRowLabels, drawColumnLabels );
0786 }
0787 
0788 void GraphicsScene::printDiagram( QPrinter *printer, const PrintingContext &context )
0789 {
0790 #ifndef HAVE_PRINTER
0791     Q_UNUSED( printer );
0792     Q_UNUSED( context );
0793 #else
0794     PrintingContext ctx( context );
0795     if (ctx.sceneRect().isNull()) {
0796         ctx.setSceneRect(sceneRect());
0797     }
0798     QRectF targetRect = printer->pageRect( QPrinter::DevicePixel );
0799     if ( printer->fullPage() ) {
0800         // Handle margins
0801         QPageLayout pl = printer->pageLayout();
0802         targetRect = targetRect.marginsRemoved( pl.marginsPixels( printer->resolution() ) );
0803     }
0804     QPainter painter( printer );
0805     doPrintScene( printer, &painter, targetRect, ctx );
0806 #endif
0807 }
0808 
0809 void GraphicsScene::doPrint( QPainter* painter, const QRectF& targetRect,
0810                              qreal start, qreal end,
0811                              QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
0812 {
0813     assert( painter );
0814     PrintingContext ctx;
0815     ctx.setFitting(PrintingContext::FitPageHeight); // keep old behavior (?)
0816     ctx.setDrawRowLabels( drawRowLabels );
0817     ctx.setDrawColumnLabels( drawColumnLabels );
0818     ctx.setSceneRect( sceneRect() );
0819     ctx.setLeft( start );
0820     ctx.setRight( end );
0821     doPrintScene( printer, painter, targetRect, ctx );
0822 }
0823 
0824 void GraphicsScene::doPrintScene( QPrinter *printer, QPainter *painter, const QRectF &targetRect, const PrintingContext &context )
0825 {
0826     assert( painter );
0827 
0828     bool b = blockSignals( true );
0829 
0830     d->isPrinting = true;
0831     d->drawColumnLabels = context.drawColumnLabels();
0832     d->labelsWidth = 0.0;
0833 
0834     QFont sceneFont( font() );
0835 #ifdef HAVE_PRINTER
0836     if ( printer ) {
0837         sceneFont = QFont( font(), printer );
0838         if ( font().pointSizeF() >= 0.0 )
0839             sceneFont.setPointSizeF( font().pointSizeF() );
0840         else if ( font().pointSize() >= 0 )
0841             sceneFont.setPointSize( font().pointSize() );
0842         else
0843             sceneFont.setPixelSize( font().pixelSize() );
0844     }
0845 #endif
0846 
0847     QGraphicsTextItem dummyTextItem( QLatin1String("X") );
0848     dummyTextItem.adjustSize();
0849     QFontMetrics fm(dummyTextItem.font());
0850     sceneFont.setPixelSize( fm.height() );
0851 
0852     const QRectF oldScnRect( sceneRect() );
0853     QRectF scnRect( oldScnRect );
0854     QRectF sceneHeaderRect;
0855     QRectF labelsHeaderRect;
0856     QRectF labelsRect;
0857 
0858     /* column labels */
0859     qreal headerHeight = 0.0;
0860     if ( context.drawColumnLabels() ) {
0861         headerHeight = d->rowController->headerHeight();
0862         sceneHeaderRect = context.sceneRect();
0863         sceneHeaderRect.setLeft( context.left() );
0864         sceneHeaderRect.setTop( -headerHeight );
0865         sceneHeaderRect.setHeight( headerHeight );
0866         scnRect.setTop(scnRect.top() - headerHeight);
0867     }
0868 
0869     /* row labels */
0870     QVector<QGraphicsTextItem*> textLabels;
0871     if ( context.drawRowLabels() ) {
0872         qreal textWidth = 0.;
0873         qreal charWidth = QFontMetricsF(sceneFont).boundingRect( QString::fromLatin1( "X" ) ).width();
0874         QModelIndex sidx = summaryHandlingModel()->mapToSource( summaryHandlingModel()->index( 0, 0, rootIndex()) );
0875         do {
0876             QModelIndex idx = summaryHandlingModel()->mapFromSource( sidx );
0877             const Span rg=rowController()->rowGeometry( sidx );
0878             const QString txt = idx.data( Qt::DisplayRole ).toString();
0879             QGraphicsTextItem* item = new QGraphicsTextItem( txt );
0880             addItem( item );
0881             textLabels << item;
0882             item->setTextWidth( QFontMetricsF(sceneFont).boundingRect( txt ).width() + charWidth );
0883             textWidth = qMax( item->textWidth(), textWidth );
0884             item->setPos( 0, rg.start() );
0885         } while ( ( sidx = rowController()->indexBelow( sidx ) ).isValid() );
0886         d->labelsWidth = textWidth;
0887         scnRect.setLeft( scnRect.left() - textWidth );
0888         for( QGraphicsTextItem* item : textLabels ) {
0889             item->setPos( scnRect.left(), item->y() );
0890             item->show();
0891         }
0892         if ( context.drawColumnLabels() ) {
0893             labelsHeaderRect = sceneHeaderRect;
0894             labelsHeaderRect.translate( -textWidth, 0.0 );
0895             labelsHeaderRect.setWidth( textWidth );
0896         }
0897         labelsRect = QRectF( scnRect.left(), context.top(), textWidth, context.sceneRect().height() );
0898     }
0899     setSceneRect( scnRect );
0900     scnRect.setRight( context.right() );
0901 
0902     // The scene looks like this:
0903     //  Labels   Do not print    Print        Behind end
0904     // 1       2               3            4
0905     // !-------!---------------!------------!-----------
0906     // sceneWidth is 1 to 2 + 3 to 4
0907     qreal sceneWidth = d->labelsWidth + context.right() - context.left();
0908     qreal sceneHeight = context.sceneRect().height() + sceneHeaderRect.height();
0909     // qInfo()<<Q_FUNC_INFO<<targetRect<<scnRect<<sceneWidth;
0910 
0911     int horPages = 1;
0912     int vertPages = 1;
0913     qreal scaleFactor = targetRect.height() / scnRect.height(); // FitPageHeight (default)
0914     if ( printer ) {
0915         if ( context.fitting() & PrintingContext::NoFitting ) {
0916             scaleFactor = printer->logicalDpiX() / views().at(0)->logicalDpiX(); // always have only one view
0917             vertPages = qRound( ( sceneHeight * scaleFactor / targetRect.height() ) + 0.5 );
0918             horPages = qRound( ( sceneWidth * scaleFactor / targetRect.width() ) + 0.5 );
0919         } else if ( context.fitting() & PrintingContext::FitSinglePage ) {
0920             scaleFactor = std::min( scaleFactor, targetRect.width() / sceneWidth );
0921         } else /*FitPageHeight (default)*/ {
0922             horPages = qRound( ( sceneWidth * scaleFactor / targetRect.width() ) + 0.5 );
0923         }
0924     } else {
0925         // paint device has no pages so just fit inside the target
0926         scaleFactor = std::min( scaleFactor, targetRect.width() / sceneWidth );
0927     }
0928     // qInfo()<<Q_FUNC_INFO<<"labels header:"<<labelsHeaderRect<<"labels:"<<labelsRect<<"scene header:"<<sceneHeaderRect<<"scene:"<<scnRect<<"scaleFactor:"<<scaleFactor;
0929     painter->save();
0930     painter->setFont( sceneFont );
0931 
0932     // qInfo()<<Q_FUNC_INFO<<'s'<<scaleFactor<<"pages="<<((sceneWidth * scaleFactor)/targetRect.width())<<'h'<<horPages<<'v'<<vertPages<<'s'<<scnRect<<'t'<<(targetRect.size()/scaleFactor);
0933     qreal yPos = labelsRect.top();
0934     for ( int vpage = 0; vpage < vertPages && yPos < context.bottom(); ++vpage ) {
0935         // qInfo()<<Q_FUNC_INFO<<"print vertical page"<<vpage;
0936         // Disable painting of noInformation during labels printing
0937         // or else labels might be painted over
0938         QBrush noInfoBrush;
0939         DateTimeGrid *dateTimeGrid = qobject_cast<DateTimeGrid*>(grid());
0940         if (dateTimeGrid) {
0941             noInfoBrush = dateTimeGrid->noInformationBrush();
0942             dateTimeGrid->setNoInformationBrush(QBrush());
0943         }
0944         int hpage = 0;
0945         qreal targetLabelsOffset = 0.0;
0946         qreal labelsOffsetX = 0.0;
0947         while ( labelsOffsetX < labelsHeaderRect.width() ) {
0948             // qInfo()<<Q_FUNC_INFO<<"print labels"<<"vert page:"<<vpage<<','<<hpage<<"yPos"<<yPos<<"label x:"<<labelsOffsetX;
0949             // print labels, they might span multiple pages
0950             QRectF target = targetRect;
0951             target.setWidth(std::min(target.width(), (labelsHeaderRect.width() - labelsOffsetX) * scaleFactor) );
0952             if ( vpage == 0 && headerHeight > 0.0 ) {
0953                 QRectF sourceHeader = labelsHeaderRect;
0954                 sourceHeader.translate( labelsOffsetX, 0.0 );
0955                 QRectF targetHeader = target;
0956                 targetHeader.setSize( sourceHeader.size() * scaleFactor );
0957                 drawLabelsHeader( painter, sourceHeader, targetHeader );
0958                 target.adjust( 0.0, targetHeader.height(), 0.0, 0.0 );
0959             }
0960             QRectF rect = labelsRect;
0961             rect.setLeft( rect.left() + labelsOffsetX );
0962             rect.setTop( yPos );
0963             rect.setHeight( std::min(rect.height(), target.height() / scaleFactor ) );
0964             painter->setClipRect(target);
0965             // disable header, it has been drawn above
0966             bool drawColumnLabels = d->drawColumnLabels;
0967             d->drawColumnLabels = false;
0968             // qInfo()<<Q_FUNC_INFO<<"print labels"<<"vert page:"<<vpage<<','<<hpage<<"scene rect:"<<rect<<"target:"<<target;
0969             render( painter, target, rect );
0970             d->drawColumnLabels = drawColumnLabels;
0971             labelsOffsetX += rect.width();
0972             if ( targetRect.right() <= target.right() ) {
0973                 // we have used the whole page
0974                 ++hpage;
0975 #ifdef HAVE_PRINTER
0976                 if ( printer ) {
0977                     printer->newPage();
0978                 }
0979 #endif
0980             } else {
0981                 // labels might take part of the page
0982                 targetLabelsOffset = target.width();
0983                 // qInfo()<<Q_FUNC_INFO<<"print labels finished"<<"vert page:"<<vpage<<"hor page:"<<hpage<<"target offset:"<<targetLabelsOffset;
0984                 break;
0985             }
0986         }
0987         if (dateTimeGrid) {
0988             dateTimeGrid->setNoInformationBrush(noInfoBrush);
0989         }
0990         qreal xPos = context.left();
0991         // qInfo()<<Q_FUNC_INFO<<"print diagram"<<"page:"<<vpage<<','<<hpage<<"xPos"<<xPos<<"yPos:"<<yPos;
0992         for ( ; hpage < horPages && xPos < context.right(); ++hpage ) {
0993             // Adjust for row labels (first time only)
0994             QRectF target = targetRect.adjusted(targetLabelsOffset, 0., 0., 0.);
0995             targetLabelsOffset = 0.0;
0996             if (!sceneHeaderRect.isNull() && vpage == 0) {
0997                 // draw header
0998                 QRectF rect = sceneHeaderRect;
0999                 rect.setLeft( xPos );
1000                 QRectF targetHeader = target;
1001                 targetHeader.setHeight( rect.height() * scaleFactor );
1002                 rect.setWidth( std::min( rect.width(), target.width() / scaleFactor) );
1003                 // qInfo()<<Q_FUNC_INFO<<"scene header:"<<"page:"<<vpage<<','<<hpage<<"source:"<<rect<<"target:"<<targetHeader;
1004                 render( painter, targetHeader, rect );
1005                 target.adjust( 0.0, targetHeader.height(), 0.0, 0.0 );
1006             }
1007             QRectF rect = context.sceneRect();
1008             rect.setLeft( xPos );
1009             rect.setTop( yPos );
1010             rect.setWidth( std::min( rect.width(), target.width() / scaleFactor) );
1011             rect.setHeight( std::min( rect.height(), target.height() / scaleFactor ) );
1012             target.setWidth( rect.width() * scaleFactor );
1013             painter->setClipRect( target );
1014             // disable header, it has been drawn above
1015             bool drawColumnLabels = d->drawColumnLabels;
1016             d->drawColumnLabels = false;
1017             // qInfo()<<Q_FUNC_INFO<<"scene:"<<"page:"<<vpage<<','<<hpage<<"source:"<<rect<<"target:"<<target;
1018             render( painter, target, rect );
1019             d->drawColumnLabels = drawColumnLabels;
1020 
1021             xPos += rect.width();
1022             // qInfo()<<Q_FUNC_INFO<<context<<"xPos:"<<xPos;
1023             if ( printer && xPos < context.right() ) {
1024 #ifdef HAVE_PRINTER
1025                 printer->newPage();
1026 #endif
1027             } else {
1028                 // qInfo()<<Q_FUNC_INFO<<"print horizontal finished if"<<xPos<<">="<<scnRect.right();
1029                 break;
1030             }
1031         }
1032         yPos += targetRect.height() / scaleFactor;
1033         if ( vpage == 0 ) {
1034             yPos -= headerHeight;
1035         }
1036         // qInfo()<<Q_FUNC_INFO<<"yPos:"<<yPos<<"bottom:"<<context.bottom();
1037         if ( printer && yPos < context.bottom() ) {
1038 #ifdef HAVE_PRINTER
1039             // next vertical page
1040             printer->newPage();
1041 #endif
1042         }
1043         // qInfo()<<Q_FUNC_INFO<<"next vertical page if"<<yPos<<'<'<<scnRect.bottom();
1044     }
1045 
1046     d->isPrinting = false;
1047     d->drawColumnLabels = true;
1048     d->labelsWidth = 0.0;
1049     qDeleteAll( textLabels );
1050     blockSignals( b );
1051     setSceneRect( oldScnRect );
1052     painter->restore();
1053 }
1054 
1055 void GraphicsScene::drawLabelsHeader( QPainter *painter, const QRectF &sourceRect, const QRectF &targetRect )
1056 {
1057     // qInfo()<<Q_FUNC_INFO<<"header:"<<sourceRect<<targetRect;
1058     // TODO This should paint itemview header
1059     painter->setClipRect( targetRect );
1060     render( painter, targetRect, sourceRect );
1061 }
1062 
1063 #include "moc_kganttgraphicsscene.cpp"
1064 
1065 
1066 #ifndef KDAB_NO_UNIT_TESTS
1067 #include "unittest/test.h"
1068 
1069 #include <QGraphicsLineItem>
1070 #include <QPointer>
1071 #include <QStandardItemModel>
1072 
1073 #include "kganttgraphicsview.h"
1074 
1075 class SceneTestRowController : public KGantt::AbstractRowController {
1076 private:
1077     static const int ROW_HEIGHT;
1078     QPointer<QAbstractItemModel> m_model;
1079 
1080 public:
1081     SceneTestRowController()
1082     {
1083     }
1084 
1085     void setModel( QAbstractItemModel* model )
1086     {
1087         m_model = model;
1088     }
1089 
1090     /*reimp*/int headerHeight() const override { return 40; }
1091 
1092     /*reimp*/ bool isRowVisible( const QModelIndex& ) const override { return true;}
1093     /*reimp*/ bool isRowExpanded( const QModelIndex& ) const override { return false; }
1094     /*reimp*/ KGantt::Span rowGeometry( const QModelIndex& idx ) const override
1095     {
1096         return KGantt::Span( idx.row() * ROW_HEIGHT, ROW_HEIGHT );
1097     }
1098     /*reimp*/ int maximumItemHeight() const override {
1099         return ROW_HEIGHT/2;
1100     }
1101     /*reimp*/int totalHeight() const override {
1102         return m_model->rowCount()* ROW_HEIGHT;
1103     }
1104 
1105     /*reimp*/ QModelIndex indexAt( int height ) const override {
1106         return m_model->index( height/ROW_HEIGHT, 0 );
1107     }
1108 
1109     /*reimp*/ QModelIndex indexBelow( const QModelIndex& idx ) const override {
1110         if ( !idx.isValid() )return QModelIndex();
1111         return idx.model()->index( idx.row()+1, idx.column(), idx.parent() );
1112     }
1113     /*reimp*/ QModelIndex indexAbove( const QModelIndex& idx ) const override {
1114         if ( !idx.isValid() )return QModelIndex();
1115         return idx.model()->index( idx.row()-1, idx.column(), idx.parent() );
1116     }
1117 
1118 };
1119 
1120 class TestLineItem : public QGraphicsLineItem
1121 {
1122 public:
1123     TestLineItem( bool *destroyedFlag )
1124          : QGraphicsLineItem( 0, 0, 10, 10 ), // geometry doesn't matter
1125            m_destroyedFlag( destroyedFlag )
1126     {}
1127 
1128     ~TestLineItem()
1129     { *m_destroyedFlag = true; }
1130 
1131 private:
1132     bool *m_destroyedFlag;
1133 };
1134 
1135 const int SceneTestRowController::ROW_HEIGHT = 30;
1136 
1137 KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, GraphicsView, "test" ) {
1138     QStandardItemModel model;
1139 
1140     QStandardItem* item = new QStandardItem();
1141     item->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
1142     item->setData( QString::fromLatin1( "Decide on new product" ) );
1143     item->setData( QDateTime( QDate( 2007, 3, 1 ), QTime() ), KGantt::StartTimeRole );
1144     item->setData( QDateTime( QDate( 2007, 3, 3 ), QTime() ), KGantt::EndTimeRole );
1145 
1146     QStandardItem* item2 = new QStandardItem();
1147     item2->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
1148     item2->setData( QString::fromLatin1( "Educate personnel" ) );
1149     item2->setData( QDateTime( QDate( 2007, 3, 3 ), QTime() ), KGantt::StartTimeRole );
1150     item2->setData( QDateTime( QDate( 2007, 3, 6 ), QTime() ), KGantt::EndTimeRole );
1151 
1152     model.appendRow( item );
1153     model.appendRow( item2 );
1154 
1155     SceneTestRowController rowController;
1156     rowController.setModel( &model );
1157 
1158     KGantt::GraphicsView graphicsView;
1159     graphicsView.setRowController( &rowController );
1160     graphicsView.setModel( &model );
1161 
1162     // Now the interesting stuff - the items above are just for a "realistic environment"
1163 
1164     bool foreignItemDestroyed = false;
1165     TestLineItem *foreignItem = new TestLineItem( &foreignItemDestroyed );
1166     graphicsView.scene()->addItem( foreignItem );
1167 
1168     assertFalse( foreignItemDestroyed );
1169     graphicsView.updateScene();
1170     assertFalse( foreignItemDestroyed );
1171 }
1172 #endif /* KDAB_NO_UNIT_TESTS */