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