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