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