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