File indexing completed on 2024-05-12 04:20:45
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 "kganttview.h" 0010 #include "kganttview_p.h" 0011 0012 #include "kganttitemdelegate.h" 0013 #include "kganttgraphicsitem.h" 0014 #include "kganttsummaryhandlingproxymodel.h" 0015 0016 #include <QAbstractItemModel> 0017 #include <QHeaderView> 0018 #include <QVBoxLayout> 0019 #include <QGraphicsItem> 0020 #include <QGraphicsRectItem> 0021 #include <QScrollBar> 0022 #include <QPaintEvent> 0023 0024 #include <QDebug> 0025 0026 #include <cassert> 0027 0028 #if defined KDAB_EVAL 0029 #include "../evaldialog/evaldialog.h" 0030 #endif 0031 0032 using namespace KGantt; 0033 0034 namespace { 0035 class HeaderView : public QHeaderView { 0036 public: 0037 explicit HeaderView( QWidget* parent=nullptr ) : QHeaderView( Qt::Horizontal, parent ) { 0038 } 0039 0040 QSize sizeHint() const override { QSize s = QHeaderView::sizeHint(); s.rheight() *= 2; return s; } 0041 }; 0042 } 0043 0044 KGanttTreeView::KGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent ) 0045 : QTreeView( parent ), 0046 m_controller( this, proxy ) 0047 { 0048 setHeader( new HeaderView ); 0049 } 0050 0051 KGanttTreeView::~KGanttTreeView() 0052 { 0053 } 0054 0055 void KGanttTreeView::expandAll(QModelIndex index) 0056 { 0057 for (int i = 0; i < model()->rowCount(index); i++) { 0058 QModelIndex indexAt = model()->index(i, 0, index); 0059 if (model()->hasChildren(indexAt)) 0060 expandAll(indexAt); 0061 if (isExpanded(indexAt)) 0062 continue; 0063 expand(indexAt); 0064 } 0065 } 0066 0067 void KGanttTreeView::collapseAll(QModelIndex index) 0068 { 0069 for (int i = 0; i < model()->rowCount(index); i++) { 0070 QModelIndex indexAt = model()->index(i, 0, index); 0071 if (model()->hasChildren(indexAt)) 0072 collapseAll(indexAt); 0073 if (!isExpanded(indexAt)) 0074 continue; 0075 collapse(indexAt); 0076 } 0077 } 0078 0079 View::Private::Private(View* v) 0080 : q(v), 0081 splitter(v), 0082 rowController(nullptr), 0083 gfxview( new GraphicsView( &splitter ) ), 0084 model(nullptr) 0085 { 0086 //init(); 0087 } 0088 0089 View::Private::~Private() 0090 { 0091 delete gfxview; 0092 } 0093 0094 void View::Private::init() 0095 { 0096 KGanttTreeView* tw = new KGanttTreeView( &ganttProxyModel, &splitter ); 0097 tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0098 tw->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); 0099 0100 q->setLeftView( tw ); 0101 q->setRowController( tw->rowController() ); 0102 0103 //gfxview.setRenderHints( QPainter::Antialiasing ); 0104 0105 tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0106 0107 QVBoxLayout* layout = new QVBoxLayout(q); 0108 layout->setContentsMargins(0, 0, 0, 0); 0109 layout->addWidget(&splitter); 0110 q->setLayout(layout); 0111 0112 constraintProxy.setProxyModel( &ganttProxyModel ); 0113 constraintProxy.setDestinationModel( &mappedConstraintModel ); 0114 setupGraphicsView(); 0115 } 0116 0117 void View::Private::setupGraphicsView() 0118 { 0119 gfxview->setParent( &splitter ); 0120 gfxview->setAlignment(Qt::AlignTop|Qt::AlignLeft); 0121 gfxview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0122 gfxview->setSelectionModel( leftWidget->selectionModel() ); 0123 gfxview->setConstraintModel( &mappedConstraintModel ); 0124 q->setLeftView( leftWidget ); 0125 q->setRowController( rowController ); 0126 updateScene(); 0127 } 0128 0129 void View::Private::updateScene() 0130 { 0131 gfxview->clearItems(); 0132 if ( !model) return; 0133 0134 if ( QTreeView* tw = qobject_cast<QTreeView*>(leftWidget)) { 0135 QModelIndex idx = ganttProxyModel.mapFromSource( model->index( 0, 0, leftWidget->rootIndex() ) ); 0136 do { 0137 gfxview->updateRow( idx ); 0138 } while ( ( idx = tw->indexBelow( idx ) ) != QModelIndex() && 0139 gfxview->rowController()->isRowVisible(idx) ); 0140 gfxview->updateSceneRect(); 0141 } else { 0142 const QModelIndex rootidx = ganttProxyModel.mapFromSource( leftWidget->rootIndex() ); 0143 for ( int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r ) { 0144 gfxview->updateRow( ganttProxyModel.index( r, 0, rootidx ) ); 0145 } 0146 } 0147 } 0148 0149 void View::Private::slotCollapsed(const QModelIndex& _idx) 0150 { 0151 QTreeView* tw = qobject_cast<QTreeView*>(leftWidget); 0152 if (!tw) return; 0153 0154 bool blocked = gfxview->blockSignals( true ); 0155 0156 QModelIndex idx( _idx ); 0157 const QAbstractItemModel* model = leftWidget->model(); 0158 const QModelIndex pidx = ganttProxyModel.mapFromSource(idx); 0159 bool isMulti = false; 0160 for ( QModelIndex treewalkidx = pidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) { 0161 if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti 0162 && !gfxview->rowController()->isRowExpanded( treewalkidx ) ) { 0163 isMulti = true; 0164 break; 0165 } 0166 } 0167 0168 if ( !isMulti ) { 0169 for ( int i = 0; i < model->rowCount( idx ); ++i ) { 0170 gfxview->deleteSubtree( ganttProxyModel.index( i, 0, pidx ) ); 0171 } 0172 } else { 0173 gfxview->updateRow(pidx); 0174 } 0175 //qDebug() << "Looking to update from " << idx; 0176 while ( ( idx=tw->indexBelow( idx ) ) != QModelIndex() && 0177 gfxview->rowController()->isRowVisible( ganttProxyModel.mapFromSource(idx) ) ) { 0178 const QModelIndex proxyidx( ganttProxyModel.mapFromSource( idx ) ); 0179 gfxview->updateRow(proxyidx); 0180 } 0181 gfxview->blockSignals( blocked ); 0182 gfxview->updateSceneRect(); 0183 } 0184 0185 void View::Private::slotExpanded(const QModelIndex& _idx) 0186 { 0187 QModelIndex idx( ganttProxyModel.mapFromSource( _idx ) ); 0188 do { 0189 //qDebug() << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString(); 0190 gfxview->updateRow(idx); 0191 } while ( ( idx=gfxview->rowController()->indexBelow( idx ) ) != QModelIndex() 0192 && gfxview->rowController()->isRowVisible( idx ) ); 0193 gfxview->updateSceneRect(); 0194 } 0195 0196 void View::Private::slotVerticalScrollValueChanged( int val ) 0197 { 0198 #if 0 0199 qDebug() << "View::Private::slotVerticalScrollValueChanged("<<val<<")=" 0200 << val/gfxview->verticalScrollBar()->singleStep(); 0201 #endif 0202 leftWidget->verticalScrollBar()->setValue( val/gfxview->verticalScrollBar()->singleStep() ); 0203 } 0204 0205 void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max ) 0206 { 0207 //qDebug() << "View::Private::slotLeftWidgetVerticalRangeChanged("<<min<<max<<")"; 0208 // In some cases the gfxview has already been deleted when this signal arrive 0209 if (!gfxview.isNull()) { 0210 gfxview->verticalScrollBar()->setRange( min, max ); 0211 gfxview->updateSceneRect(); 0212 } 0213 } 0214 0215 void View::Private::slotGfxViewVerticalRangeChanged( int min, int max ) 0216 { 0217 //qDebug() << "View::Private::slotGfxViewVerticalRangeChanged("<<min<<max<<")"; 0218 if ( !leftWidget.isNull() && !gfxview.isNull() ) { 0219 int leftMin = leftWidget->verticalScrollBar()->minimum(); 0220 int leftMax = leftWidget->verticalScrollBar()->maximum(); 0221 bool blocked = gfxview->verticalScrollBar()->blockSignals( true ); 0222 gfxview->verticalScrollBar()->setRange( qMax( min, leftMin ), qMax( max, leftMax ) ); 0223 gfxview->verticalScrollBar()->blockSignals( blocked ); 0224 } 0225 } 0226 0227 0228 0229 0230 View::View(QWidget* parent) 0231 : QWidget(parent), 0232 _d(new Private(this)) 0233 { 0234 #if defined KDAB_EVAL 0235 EvalDialog::checkEvalLicense( "KD Gantt" ); 0236 #endif 0237 _d->init(); 0238 } 0239 0240 View::~View() 0241 { 0242 delete _d; 0243 } 0244 0245 #define d d_func() 0246 0247 0248 void View::setLeftView( QAbstractItemView* aiv ) 0249 { 0250 assert( aiv ); 0251 if ( aiv==d->leftWidget ) return; 0252 if ( !d->leftWidget.isNull() ) { 0253 d->leftWidget->disconnect( this ); 0254 d->leftWidget->hide(); 0255 d->leftWidget->verticalScrollBar()->disconnect( d->gfxview->verticalScrollBar() ); 0256 d->gfxview->verticalScrollBar()->disconnect( d->leftWidget->verticalScrollBar() ); 0257 } 0258 0259 d->leftWidget = aiv; 0260 d->splitter.insertWidget( 0, d->leftWidget ); 0261 0262 if ( qobject_cast<QTreeView*>(d->leftWidget) ) { 0263 connect( d->leftWidget, SIGNAL(collapsed(QModelIndex)), 0264 this, SLOT(slotCollapsed(QModelIndex)) ); 0265 connect( d->leftWidget, SIGNAL(expanded(QModelIndex)), 0266 this, SLOT(slotExpanded(QModelIndex)) ); 0267 } 0268 0269 connect( d->gfxview->verticalScrollBar(), SIGNAL(valueChanged(int)), 0270 d->leftWidget->verticalScrollBar(), SLOT(setValue(int)) ); 0271 connect( d->leftWidget->verticalScrollBar(), SIGNAL(valueChanged(int)), 0272 d->gfxview->verticalScrollBar(), SLOT(setValue(int)) ); 0273 connect( d->leftWidget->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), 0274 this, SLOT(slotLeftWidgetVerticalRangeChanged(int,int)) ); 0275 connect( d->gfxview->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), 0276 this, SLOT(slotGfxViewVerticalRangeChanged(int,int)) ); 0277 } 0278 0279 0280 void View::setRowController( AbstractRowController* ctrl ) 0281 { 0282 if ( ctrl == d->rowController && d->gfxview->rowController() == ctrl ) return; 0283 d->rowController = ctrl; 0284 d->gfxview->setRowController( d->rowController ); 0285 } 0286 0287 0288 AbstractRowController* View::rowController() 0289 { 0290 return d->rowController; 0291 } 0292 0293 0294 const AbstractRowController* View::rowController() const 0295 { 0296 return d->rowController; 0297 } 0298 0299 0300 const QAbstractItemView* View::leftView() const 0301 { 0302 return d->leftWidget; 0303 } 0304 0305 0306 QAbstractItemView* View::leftView() 0307 { 0308 return d->leftWidget; 0309 } 0310 0311 0312 void View::setGraphicsView( GraphicsView* gv ) 0313 { 0314 if ( gv != d->gfxview ) { 0315 GraphicsView* old = d->gfxview; 0316 AbstractGrid *grid = old->takeGrid(); 0317 d->gfxview = gv; 0318 d->gfxview->setModel(old->model()); // use the old ForwardingProxyModel 0319 d->setupGraphicsView(); 0320 d->gfxview->setGrid( grid ); 0321 delete old; 0322 } 0323 } 0324 0325 0326 const GraphicsView* View::graphicsView() const 0327 { 0328 return d->gfxview; 0329 } 0330 0331 0332 GraphicsView* View::graphicsView() 0333 { 0334 return d->gfxview; 0335 } 0336 0337 0338 const QSplitter* View::splitter() const 0339 { 0340 return &d->splitter; 0341 } 0342 0343 0344 QSplitter* View::splitter() 0345 { 0346 return &d->splitter; 0347 } 0348 0349 0350 0351 QAbstractItemModel* View::model() const 0352 { 0353 return leftView()->model(); 0354 } 0355 0356 0357 void View::setModel( QAbstractItemModel* model ) 0358 { 0359 leftView()->setModel( model ); 0360 d->ganttProxyModel.setSourceModel( model ); 0361 d->gfxview->setModel( &d->ganttProxyModel ); 0362 } 0363 0364 0365 QItemSelectionModel* View::selectionModel() const 0366 { 0367 return leftView()->selectionModel(); 0368 } 0369 0370 0371 void View::setSelectionModel( QItemSelectionModel* smodel ) 0372 { 0373 leftView()->setSelectionModel( smodel ); 0374 d->gfxview->setSelectionModel( new QItemSelectionModel( &( d->ganttProxyModel ),this ) ); 0375 } 0376 0377 0378 void View::setGrid( AbstractGrid* grid ) 0379 { 0380 d->gfxview->setGrid( grid ); 0381 } 0382 0383 void View::expandAll( QModelIndex index ) 0384 { 0385 // FIXME: 0386 // It is legal to call setLeftView() with any QAbstractItemView, 0387 // so expandAll should be reimplemented to work with that. 0388 KGanttTreeView* tw = qobject_cast<KGanttTreeView*>(leftView()); 0389 if (tw) { 0390 tw->expandAll(index); 0391 } 0392 } 0393 0394 void View::collapseAll( QModelIndex index ) 0395 { 0396 // FIXME: 0397 // It is legal to call setLeftView() with any QAbstractItemView, 0398 // so expandAll should be reimplemented to work with that. 0399 KGanttTreeView* tw = qobject_cast<KGanttTreeView*>(leftView()); 0400 if (tw) { 0401 tw->collapseAll(index); 0402 } 0403 } 0404 0405 0406 AbstractGrid* View::grid() const 0407 { 0408 return d->gfxview->grid(); 0409 } 0410 0411 0412 QModelIndex View::rootIndex() const 0413 { 0414 return leftView()->rootIndex(); 0415 } 0416 0417 0418 void View::setRootIndex( const QModelIndex& idx ) 0419 { 0420 leftView()->setRootIndex( idx ); 0421 d->gfxview->setRootIndex( idx ); 0422 } 0423 0424 0425 ItemDelegate* View::itemDelegate() const 0426 { 0427 return d->gfxview->itemDelegate(); 0428 } 0429 0430 0431 void View::setItemDelegate( ItemDelegate* delegate ) 0432 { 0433 leftView()->setItemDelegate( delegate ); 0434 d->gfxview->setItemDelegate( delegate ); 0435 } 0436 0437 0438 void View::setConstraintModel( ConstraintModel* cm ) 0439 { 0440 d->constraintProxy.setSourceModel( cm ); 0441 d->gfxview->setConstraintModel( &d->mappedConstraintModel ); 0442 } 0443 0444 0445 ConstraintModel* View::constraintModel() const 0446 { 0447 return d->constraintProxy.sourceModel(); 0448 } 0449 0450 const QAbstractProxyModel* View::ganttProxyModel() const 0451 { 0452 return &( d->ganttProxyModel ); 0453 } 0454 0455 QAbstractProxyModel* View::ganttProxyModel() 0456 { 0457 return &( d->ganttProxyModel ); 0458 } 0459 0460 void View::ensureVisible(const QModelIndex& index) 0461 { 0462 QGraphicsView* view = graphicsView(); 0463 KGantt::GraphicsScene* scene = static_cast<KGantt::GraphicsScene*>(view->scene()); 0464 if (!scene) 0465 return; 0466 0467 KGantt::SummaryHandlingProxyModel* model = static_cast<KGantt::SummaryHandlingProxyModel*>(scene->summaryHandlingModel()); 0468 0469 const QModelIndex pidx = d->ganttProxyModel.mapFromSource(index); 0470 const QModelIndex idx = model->mapFromSource( pidx ); 0471 QGraphicsItem* item = scene->findItem(idx); 0472 view->ensureVisible(item); 0473 } 0474 0475 void View::resizeEvent(QResizeEvent*ev) 0476 { 0477 QWidget::resizeEvent(ev); 0478 } 0479 0480 0481 QModelIndex View::indexAt( const QPoint& pos ) const 0482 { 0483 return d->gfxview->indexAt( pos ); 0484 } 0485 0486 0487 void View::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) 0488 { 0489 graphicsView()->print( printer, drawRowLabels, drawColumnLabels ); 0490 } 0491 0492 0493 void View::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels ) 0494 { 0495 graphicsView()->print( printer, start, end, drawRowLabels, drawColumnLabels ); 0496 } 0497 0498 0499 void View::print( QPainter* painter, const QRectF& target, bool drawRowLabels, bool drawColumnLabels) 0500 { 0501 d->gfxview->print( painter, 0502 target, 0503 drawRowLabels, 0504 drawColumnLabels); 0505 } 0506 0507 0508 void View::print( QPainter* painter, qreal start, qreal end, const QRectF& target, bool drawRowLabels, bool drawColumnLabels) 0509 { 0510 d->gfxview->print( painter, 0511 start, end, 0512 target, 0513 drawRowLabels, 0514 drawColumnLabels); 0515 } 0516 0517 void View::printDiagram( QPrinter *printer, const PrintingContext &context ) 0518 { 0519 graphicsView()->printDiagram( printer, context ); 0520 } 0521 0522 #include "moc_kganttview.cpp" 0523 0524 #ifndef KDAB_NO_UNIT_TESTS 0525 #include "unittest/test.h" 0526 0527 #include "kganttlistviewrowcontroller.h" 0528 #include <QApplication> 0529 #include <QTimer> 0530 #include <QPixmap> 0531 #include <QListView> 0532 0533 KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, View, "test" ) { 0534 View view( nullptr ); 0535 #if 0 // GUI tests do not work well on the server 0536 QTimer::singleShot( 1000, qApp, SLOT(quit()) ); 0537 view.show(); 0538 0539 qApp->exec(); 0540 QPixmap screenshot1 = QPixmap::grabWidget( &view ); 0541 0542 QTreeView* tv = new QTreeView; 0543 view.setLeftView( tv ); 0544 view.setRowController( new TreeViewRowController(tv,view.ganttProxyModel()) ); 0545 0546 QTimer::singleShot( 1000, qApp, SLOT(quit()) ); 0547 0548 qApp->exec(); 0549 QPixmap screenshot2 = QPixmap::grabWidget( &view ); 0550 0551 assertEqual( screenshot1.toImage(), screenshot2.toImage() ); 0552 0553 QListView* lv = new QListView; 0554 view.setLeftView(lv); 0555 view.setRowController( new ListViewRowController(lv,view.ganttProxyModel())); 0556 view.show(); 0557 QTimer::singleShot( 1000, qApp, SLOT(quit()) ); 0558 qApp->exec(); 0559 #endif 0560 } 0561 #endif /* KDAB_NO_UNIT_TESTS */