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 */