File indexing completed on 2024-05-12 04:20:43

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 "kganttgraphicsview.h"
0010 #include "kganttgraphicsview_p.h"
0011 #include "kganttabstractrowcontroller.h"
0012 #include "kganttgraphicsitem.h"
0013 #include "kganttconstraintmodel.h"
0014 #include "kganttdatetimetimelinedialog.h"
0015 
0016 #include <QLocale>
0017 #include <QMenu>
0018 #include <QPainter>
0019 #include <QPaintEvent>
0020 #include <QResizeEvent>
0021 #include <QScrollBar>
0022 #include <QAbstractProxyModel>
0023 #include <QPrinter>
0024 #include <QItemSelectionModel>
0025 #include <QGraphicsSceneMouseEvent>
0026 #include <QGuiApplication>
0027 #include <QActionGroup>
0028 
0029 #include <cassert>
0030 
0031 #if defined KDAB_EVAL
0032 #include "../evaldialog/evaldialog.h"
0033 #endif
0034 
0035 /*\class KGantt::HeaderWidget
0036  * \internal
0037  */
0038 
0039 using namespace KGantt;
0040 
0041 HeaderWidget::HeaderWidget( GraphicsView* parent )
0042     : QWidget( parent ), m_offset( 0. ), m_headerType( DateTimeGrid::NoHeader )
0043 {
0044     assert( parent ); // Parent must be set
0045     setMouseTracking(true);
0046 }
0047 
0048 HeaderWidget::~HeaderWidget()
0049 {
0050 }
0051 
0052 void HeaderWidget::scrollTo( int v )
0053 {
0054     m_offset = v;
0055     // QWidget::scroll() won't work properly for me on Mac
0056     //scroll( static_cast<int>( old-v ), 0 );
0057     update();
0058 }
0059 
0060 void HeaderWidget::paintEvent( QPaintEvent* ev )
0061 {
0062     QPainter p( this );
0063     view()->grid()->paintHeader( &p, rect(), ev->rect(), m_offset, this );
0064 }
0065 
0066 bool HeaderWidget::event( QEvent* event )
0067 {
0068     if ( event->type() == QEvent::ToolTip ) {
0069         DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
0070         if ( grid ) {
0071             QHelpEvent *e = static_cast<QHelpEvent*>( event );
0072             QDateTime dt = grid->mapFromChart( view()->mapToScene( e->x(), 0 ).x() ).toDateTime();
0073             setToolTip(QLocale().toString(dt));
0074         }
0075     }
0076     return QWidget::event( event );
0077 }
0078 
0079 void HeaderWidget::mousePressEvent(QMouseEvent *event)
0080 {
0081     DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
0082     int mousePosX = event->x();
0083     m_headerType = grid->sectionHandleAtPos( view()->mapToScene( event->x(), 0 ).x(), event->pos().y(), geometry() );
0084     if (m_headerType != DateTimeGrid::NoHeader) {
0085         bool hasCursor = testAttribute(Qt::WA_SetCursor);
0086         if (!hasCursor) {
0087             setCursor(QCursor(Qt::SplitHCursor));
0088         }
0089         m_mousePosX = mousePosX;
0090         event->accept();
0091         return;
0092     }
0093     QWidget::mousePressEvent( event );
0094 }
0095 
0096 void HeaderWidget::mouseReleaseEvent(QMouseEvent *event)
0097 {
0098     if ( m_headerType > 0 ) {
0099         DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
0100         int mousePosX = view()->mapToScene( event->x(), 0 ).x();
0101         if ( grid->sectionHandleAtPos( mousePosX, event->pos().y(), geometry() ) == DateTimeGrid::NoHeader ) {
0102             bool hasCursor = testAttribute(Qt::WA_SetCursor);
0103             if (hasCursor) {
0104                 unsetCursor();
0105             }
0106         }
0107         m_headerType = DateTimeGrid::NoHeader;
0108         m_mousePosX = event->x();
0109         QGuiApplication::restoreOverrideCursor();
0110     }
0111     QWidget::mouseReleaseEvent(event);
0112 }
0113 
0114 void HeaderWidget::mouseMoveEvent(QMouseEvent *event)
0115 {
0116     DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
0117     int mousePosX = event->x();
0118     qreal gridX = view()->mapToScene( event->x(), 0.0 ).x();
0119     switch ( m_headerType ) {
0120         case DateTimeGrid::UpperHeader:
0121         {
0122             if ( mousePosX > m_mousePosX )  {
0123                 grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 1.05 ) );
0124             } else {
0125                 grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() / 1.05 ) );
0126             }
0127             m_mousePosX = mousePosX;
0128             event->accept();
0129             return;
0130         }
0131         case DateTimeGrid::LowerHeader:
0132         {
0133             if ( mousePosX > m_mousePosX )  {
0134                 grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 1.01 ) );
0135             } else {
0136                 grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() / 1.01 ) );
0137             }
0138             m_mousePosX = mousePosX;
0139             event->accept();
0140             return;
0141         }
0142         default: {
0143             bool hasCursor = testAttribute(Qt::WA_SetCursor);
0144             DateTimeGrid::HeaderType type = grid->sectionHandleAtPos( gridX, event->pos().y(), geometry());
0145             if (type != DateTimeGrid::NoHeader) {
0146                 if (!hasCursor) {
0147                     setCursor(QCursor(Qt::SplitHCursor));
0148                 }
0149                 event->accept();
0150                 return;
0151             }
0152             if (hasCursor) {
0153                 unsetCursor();
0154             }
0155             break;
0156         }
0157     }
0158     QWidget::mouseMoveEvent(event);
0159 }
0160 
0161 void HeaderWidget::wheelEvent( QWheelEvent *event )
0162 {
0163     DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
0164     if ( event->angleDelta().y() > 0 ) {
0165         grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 1.1 ) );
0166     } else {
0167         grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() / 1.1 ) );
0168     }
0169     event->accept();
0170 }
0171 
0172 void HeaderWidget::contextMenuEvent( QContextMenuEvent* event )
0173 {
0174     QMenu contextMenu;
0175 
0176     DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
0177     QAction* actionScaleAuto = nullptr;
0178     QAction* actionScaleMonth = nullptr;
0179     QAction* actionScaleWeek = nullptr;
0180     QAction* actionScaleDay = nullptr;
0181     QAction* actionScaleHour = nullptr;
0182     QAction* actionZoomIn = nullptr;
0183     QAction* actionZoomOut = nullptr;
0184     QAction* actionTimeline = nullptr;
0185     if ( grid != nullptr )
0186     {
0187         QMenu* menuScale = new QMenu( tr( "Scale", "@title:menu" ), &contextMenu );
0188         QActionGroup* scaleGroup = new QActionGroup( &contextMenu );
0189         scaleGroup->setExclusive( true );
0190 
0191         actionScaleAuto = new QAction( tr( "Auto", "@item:inmenu Automatic scale" ), menuScale );
0192         actionScaleAuto->setCheckable( true );
0193         actionScaleAuto->setChecked( grid->scale() == DateTimeGrid::ScaleAuto );
0194         actionScaleMonth = new QAction( tr( "Month", "@item:inmenu" ), menuScale );
0195         actionScaleMonth->setCheckable( true );
0196         actionScaleMonth->setChecked( grid->scale() == DateTimeGrid::ScaleMonth );
0197         actionScaleWeek = new QAction( tr( "Week", "@item:inmenu" ), menuScale );
0198         actionScaleWeek->setCheckable( true );
0199         actionScaleWeek->setChecked( grid->scale() == DateTimeGrid::ScaleWeek );
0200         actionScaleDay = new QAction( tr( "Day", "@item:inmenu" ), menuScale );
0201         actionScaleDay->setCheckable( true );
0202         actionScaleDay->setChecked( grid->scale() == DateTimeGrid::ScaleDay );
0203         actionScaleHour = new QAction( tr( "Hour", "@item:inmenu" ), menuScale );
0204         actionScaleHour->setCheckable( true );
0205         actionScaleHour->setChecked( grid->scale() == DateTimeGrid::ScaleHour );
0206 
0207         scaleGroup->addAction( actionScaleAuto );
0208         menuScale->addAction( actionScaleAuto );
0209 
0210         scaleGroup->addAction( actionScaleMonth );
0211         menuScale->addAction( actionScaleMonth );
0212 
0213         scaleGroup->addAction( actionScaleWeek );
0214         menuScale->addAction( actionScaleWeek );
0215 
0216         scaleGroup->addAction( actionScaleDay );
0217         menuScale->addAction( actionScaleDay );
0218 
0219         scaleGroup->addAction( actionScaleHour );
0220         menuScale->addAction( actionScaleHour );
0221 
0222         contextMenu.addMenu( menuScale );
0223 
0224         contextMenu.addSeparator();
0225 
0226         actionZoomIn = new QAction( tr( "Zoom In", "@action:inmenu" ), &contextMenu );
0227         contextMenu.addAction( actionZoomIn );
0228         actionZoomOut = new QAction( tr( "Zoom Out", "@action:inmenu" ), &contextMenu );
0229         contextMenu.addAction( actionZoomOut );
0230 
0231         contextMenu.addSeparator();
0232         actionTimeline = new QAction( tr( "Timeline...", "@action:inmenu" ), &contextMenu );
0233         contextMenu.addAction( actionTimeline );
0234     }
0235 
0236     if ( contextMenu.isEmpty() )
0237     {
0238         event->ignore();
0239         return;
0240     }
0241 
0242     const QAction* const action = contextMenu.exec( event->globalPos() );
0243     if ( action == nullptr ) {}
0244     else if ( action == actionScaleAuto )
0245     {
0246         assert( grid != nullptr );
0247         grid->setScale( DateTimeGrid::ScaleAuto );
0248     }
0249     else if ( action == actionScaleMonth )
0250     {
0251         assert( grid != nullptr );
0252         grid->setScale( DateTimeGrid::ScaleMonth );
0253     }
0254     else if ( action == actionScaleWeek )
0255     {
0256         assert( grid != nullptr );
0257         grid->setScale( DateTimeGrid::ScaleWeek );
0258     }
0259     else if ( action == actionScaleDay )
0260     {
0261         assert( grid != nullptr );
0262         grid->setScale( DateTimeGrid::ScaleDay );
0263     }
0264     else if ( action == actionScaleHour )
0265     {
0266         assert( grid != nullptr );
0267         grid->setScale( DateTimeGrid::ScaleHour );
0268     }
0269     else if ( action == actionZoomIn )
0270     {
0271         assert( grid != nullptr );
0272         grid->setDayWidth( grid->dayWidth() * 1.25 );
0273     }
0274     else if ( action == actionZoomOut )
0275     {
0276         assert( grid != nullptr );
0277         // daywidth *MUST NOT* go below 1.0, it is used as an integer later on
0278         grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 0.8 ) );
0279     }
0280     else if ( action == actionTimeline )
0281     {
0282         assert( grid != nullptr );
0283         DateTimeTimeLineDialog dlg(grid->timeLine());
0284         dlg.exec();
0285     }
0286     event->accept();
0287 }
0288 
0289 GraphicsView::Private::Private( GraphicsView* _q )
0290   : q( _q ), rowcontroller(nullptr), headerwidget( _q )
0291 {
0292 }
0293 
0294 GraphicsView::Private::~Private()
0295 {
0296 }
0297 
0298 void GraphicsView::Private::updateHeaderGeometry()
0299 {
0300     q->setViewportMargins(0,rowcontroller->headerHeight(),0,0);
0301     headerwidget.setGeometry( q->viewport()->x(),
0302                               q->viewport()->y() - rowcontroller->headerHeight(),
0303                               q->viewport()->width(),
0304                               rowcontroller->headerHeight() );
0305 }
0306 
0307 void GraphicsView::Private::slotGridChanged()
0308 {
0309     updateHeaderGeometry();
0310     headerwidget.update();
0311     q->updateSceneRect();
0312     q->update();
0313 }
0314 
0315 void GraphicsView::Private::slotHorizontalScrollValueChanged( int val )
0316 {
0317     const QRectF viewRect = q->transform().mapRect( q->sceneRect() );
0318     headerwidget.scrollTo( val-q->horizontalScrollBar()->minimum()+static_cast<int>( viewRect.left() ) );
0319 }
0320 
0321 void GraphicsView::Private::slotColumnsInserted( const QModelIndex& parent,  int start, int end )
0322 {
0323     Q_UNUSED( start );
0324     Q_UNUSED( end );
0325     QModelIndex idx = scene.model()->index( 0, 0, scene.summaryHandlingModel()->mapToSource( parent ) );
0326     do {
0327         scene.updateRow( scene.summaryHandlingModel()->mapFromSource( idx ) );
0328     } while ( ( idx = rowcontroller->indexBelow( idx ) ) != QModelIndex() && rowcontroller->isRowVisible( idx ) );
0329         //} while ( ( idx = d->treeview.indexBelow( idx ) ) != QModelIndex() && d->treeview.visualRect(idx).isValid() );
0330      q->updateSceneRect();
0331 }
0332 
0333 void GraphicsView::Private::slotColumnsRemoved( const QModelIndex& parent,  int start, int end )
0334 {
0335     // TODO
0336     Q_UNUSED( start );
0337     Q_UNUSED( end );
0338     Q_UNUSED( parent );
0339     q->updateScene();
0340 }
0341 
0342 void GraphicsView::Private::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight )
0343 {
0344     //qDebug() << "GraphicsView::slotDataChanged("<<topLeft<<bottomRight<<")";
0345     const QModelIndex parent = topLeft.parent();
0346     for ( int row = topLeft.row(); row <= bottomRight.row(); ++row ) {
0347         scene.updateRow( scene.summaryHandlingModel()->index( row, 0, parent ) );
0348     }
0349 }
0350 
0351 void GraphicsView::Private::slotLayoutChanged()
0352 {
0353     //qDebug() << "slotLayoutChanged()";
0354     q->updateScene();
0355 }
0356 
0357 void GraphicsView::Private::slotModelReset()
0358 {
0359     //qDebug() << "slotModelReset()";
0360     q->updateScene();
0361 }
0362 
0363 void GraphicsView::Private::slotRowsInserted( const QModelIndex& parent,  int start, int end )
0364 {
0365     Q_UNUSED( parent );
0366     Q_UNUSED( start );
0367     Q_UNUSED( end );
0368     q->updateScene(); // TODO: This might be optimised
0369 }
0370 
0371 void GraphicsView::Private::removeConstraintsRecursive( QAbstractProxyModel *summaryModel, const QModelIndex& index )
0372 {
0373     if ( summaryModel->hasChildren( index ) ) {
0374         //qDebug() << "removing constraints from children of"<<index;
0375         for ( int row = 0; row < summaryModel->rowCount( index ); ++row ) {
0376             const QModelIndex child = summaryModel->index( row, index.column(), index );
0377             removeConstraintsRecursive( summaryModel, child );
0378         }
0379     }
0380     //qDebug() << "removing constraints from"<<index;
0381     // NOTE: Constraints are mapped to indexes in the summaryModel->sourceModel()
0382     const QList<Constraint> clst = scene.constraintModel()->constraintsForIndex( summaryModel->mapToSource( index ) );
0383     for ( const Constraint &c : clst ) {
0384         scene.constraintModel()->removeConstraint( c );
0385     }
0386 }
0387 
0388 void GraphicsView::Private::slotRowsAboutToBeRemoved( const QModelIndex& parent,  int start, int end )
0389 {
0390     //qDebug() << "GraphicsView::Private::slotRowsAboutToBeRemoved("<<parent<<start<<end<<")";
0391     QAbstractProxyModel *summaryModel = scene.summaryHandlingModel();
0392     for ( int row = start; row <= end; ++row ) {
0393         for ( int col = 0; col < summaryModel->columnCount( parent ); ++col ) {
0394             const QModelIndex idx = summaryModel->index( row, col, parent );
0395             removeConstraintsRecursive( summaryModel, idx );
0396             scene.removeItem( idx );
0397         }
0398     }
0399 }
0400 
0401 void GraphicsView::Private::slotRowsRemoved( const QModelIndex& parent,  int start, int end )
0402 {
0403     //qDebug() << "GraphicsView::Private::slotRowsRemoved("<<parent<<start<<end<<")";
0404     // TODO
0405     Q_UNUSED( parent );
0406     Q_UNUSED( start );
0407     Q_UNUSED( end );
0408 
0409     q->updateScene();
0410 }
0411 
0412 void GraphicsView::Private::slotItemClicked( const QModelIndex& idx )
0413 {
0414     QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx );
0415     Q_EMIT q->clicked( sidx );
0416     if (q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
0417         Q_EMIT q->activated( sidx );
0418 }
0419 
0420 void GraphicsView::Private::slotItemDoubleClicked( const QModelIndex& idx )
0421 {
0422     QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx );
0423     Q_EMIT q->qrealClicked( sidx );
0424     if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
0425         Q_EMIT q->activated( sidx );
0426 }
0427 
0428 void GraphicsView::Private::slotHeaderContextMenuRequested( const QPoint& pt )
0429 {
0430     Q_EMIT q->headerContextMenuRequested( headerwidget.mapToGlobal( pt ) );
0431 }
0432 
0433 GraphicsView::GraphicsView( QWidget* parent )
0434     : QGraphicsView( parent ), _d( new Private( this ) )
0435 {
0436 #if defined KDAB_EVAL
0437   EvalDialog::checkEvalLicense( "KD Gantt" );
0438 #endif
0439     connect( horizontalScrollBar(), SIGNAL(valueChanged(int)),
0440              this, SLOT(slotHorizontalScrollValueChanged(int)) );
0441     connect( &_d->scene, SIGNAL(gridChanged()),
0442              this, SLOT(slotGridChanged()) );
0443     connect( &_d->scene, SIGNAL(entered(QModelIndex)),
0444              this, SIGNAL(entered(QModelIndex)) );
0445     connect( &_d->scene, SIGNAL(pressed(QModelIndex)),
0446              this, SIGNAL(pressed(QModelIndex)) );
0447     connect( &_d->scene, SIGNAL(clicked(QModelIndex)),
0448              this, SLOT(slotItemClicked(QModelIndex)) );
0449     connect( &_d->scene, SIGNAL(qrealClicked(QModelIndex)),
0450              this, SLOT(slotItemDoubleClicked(QModelIndex)) );
0451     connect( &_d->scene, SIGNAL(sceneRectChanged(QRectF)),
0452              this, SLOT(updateSceneRect()) );
0453     connect( &_d->headerwidget, SIGNAL(customContextMenuRequested(QPoint)),
0454              this, SLOT(slotHeaderContextMenuRequested(QPoint)) );
0455     setScene( &_d->scene );
0456 
0457     // HACK!
0458     setSummaryHandlingModel( _d->scene.summaryHandlingModel() );
0459 
0460     // So that AbstractGrid::drawBackground() and AbstractGrid::drawForeground()
0461     // works properly
0462     setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
0463 
0464     //setCacheMode( CacheBackground );
0465 }
0466 
0467 
0468 GraphicsView::~GraphicsView()
0469 {
0470     delete _d;
0471 }
0472 
0473 #define d d_func()
0474 
0475 
0476 void GraphicsView::setModel( QAbstractItemModel* model )
0477 {
0478     if ( d->scene.model() ) {
0479         disconnect( d->scene.model() );
0480     }
0481 
0482     d->scene.setModel( model );
0483     if (model) {
0484         connect( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0485                 this, SLOT(updateSceneRect()) );
0486     }
0487     updateScene();
0488 }
0489 
0490 
0491 QAbstractItemModel* GraphicsView::model() const
0492 {
0493     return d->scene.model();
0494 }
0495 
0496 void GraphicsView::setSummaryHandlingModel( QAbstractProxyModel* proxyModel )
0497 {
0498     disconnect( d->scene.summaryHandlingModel() );
0499     d->scene.setSummaryHandlingModel( proxyModel );
0500 
0501     /* Connections. We have to rely on the treeview
0502      * to receive the signals before we do(!)
0503      */
0504     connect( proxyModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
0505              this,  SLOT(slotColumnsInserted(QModelIndex,int,int)) );
0506     connect( proxyModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
0507              this,  SLOT(slotColumnsRemoved(QModelIndex,int,int)) );
0508     connect( proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0509              this,  SLOT(slotDataChanged(QModelIndex,QModelIndex)) );
0510     connect( proxyModel, SIGNAL(layoutChanged()),
0511              this,  SLOT(slotLayoutChanged()) );
0512     connect( proxyModel, SIGNAL(modelReset()),
0513              this,  SLOT(slotModelReset()) );
0514     connect( proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
0515              this,  SLOT(slotRowsInserted(QModelIndex,int,int)) );
0516     connect( proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
0517              this,  SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) );
0518     connect( proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
0519              this,  SLOT(slotRowsRemoved(QModelIndex,int,int)) );
0520 
0521     updateScene();
0522 }
0523 
0524 
0525 void GraphicsView::setConstraintModel( ConstraintModel* cmodel )
0526 {
0527     d->scene.setConstraintModel( cmodel );
0528 }
0529 
0530 
0531 ConstraintModel* GraphicsView::constraintModel() const
0532 {
0533     return d->scene.constraintModel();
0534 }
0535 
0536 
0537 QAbstractProxyModel* GraphicsView::summaryHandlingModel() const
0538 {
0539     return d->scene.summaryHandlingModel();
0540 }
0541 
0542 
0543 void GraphicsView::setRootIndex( const QModelIndex& idx )
0544 {
0545     d->scene.setRootIndex( idx );
0546 }
0547 
0548 
0549 QModelIndex GraphicsView::rootIndex() const
0550 {
0551     return d->scene.rootIndex();
0552 }
0553 
0554 
0555 void GraphicsView::setSelectionModel( QItemSelectionModel* model )
0556 {
0557     d->scene.setSelectionModel( model );
0558 }
0559 
0560 
0561 QItemSelectionModel* GraphicsView::selectionModel() const
0562 {
0563     return d->scene.selectionModel();
0564 }
0565 
0566 
0567 void GraphicsView::setItemDelegate( ItemDelegate* delegate )
0568 {
0569     d->scene.setItemDelegate( delegate );
0570 }
0571 
0572 
0573 ItemDelegate* GraphicsView::itemDelegate() const
0574 {
0575     return d->scene.itemDelegate();
0576 }
0577 
0578 
0579 void GraphicsView::setRowController( AbstractRowController* rowcontroller )
0580 {
0581     d->rowcontroller = rowcontroller;
0582     d->scene.setRowController( rowcontroller );
0583     updateScene();
0584 }
0585 
0586 
0587 AbstractRowController* GraphicsView::rowController() const
0588 {
0589     return d->rowcontroller;
0590 }
0591 
0592 
0593 void GraphicsView::setGrid( AbstractGrid* grid )
0594 {
0595     d->scene.setGrid( grid );
0596     d->slotGridChanged();
0597 }
0598 
0599 
0600 AbstractGrid* GraphicsView::grid() const
0601 {
0602     return d->scene.grid();
0603 }
0604 
0605 
0606 AbstractGrid* GraphicsView::takeGrid()
0607 {
0608     return d->scene.takeGrid();
0609 }
0610 
0611 
0612 void GraphicsView::setReadOnly( bool ro )
0613 {
0614     d->scene.setReadOnly( ro );
0615 }
0616 
0617 
0618 bool GraphicsView::isReadOnly() const
0619 {
0620     return d->scene.isReadOnly();
0621 }
0622 
0623 
0624 void GraphicsView::setHeaderContextMenuPolicy( Qt::ContextMenuPolicy p )
0625 {
0626     d->headerwidget.setContextMenuPolicy( p );
0627 }
0628 
0629 
0630 Qt::ContextMenuPolicy GraphicsView::headerContextMenuPolicy() const
0631 {
0632     return d->headerwidget.contextMenuPolicy();
0633 }
0634 
0635 
0636 void GraphicsView::addConstraint( const QModelIndex& from,
0637                                   const QModelIndex& to,
0638                                   Qt::KeyboardModifiers modifiers )
0639 {
0640     if ( isReadOnly() ) return;
0641     ConstraintModel* cmodel = constraintModel();
0642     assert( cmodel );
0643     Constraint c( from, to, ( modifiers&Qt::ShiftModifier )?Constraint::TypeHard:Constraint::TypeSoft );
0644     if ( cmodel->hasConstraint( c ) ) cmodel->removeConstraint( c );
0645     else cmodel->addConstraint( c );
0646 }
0647 
0648 void GraphicsView::resizeEvent( QResizeEvent* ev )
0649 {
0650     d->updateHeaderGeometry();
0651     QRectF r = scene()->itemsBoundingRect();
0652     // To scroll more to the left than the actual item start, bug #4516
0653     r.setLeft( qMin<qreal>( 0.0, r.left() ) );
0654     // TODO: take scrollbars into account (if not always on)
0655     // The scene should be at least the size of the viewport
0656     QSizeF size = viewport()->size();
0657     //TODO: why -2 below? size should be ex. frames etc?
0658     if ( size.width() > r.width() ) {
0659         r.setWidth( size.width() - 2 );
0660     }
0661     if ( size.height() > r.height() ) {
0662         r.setHeight( size.height() - 2 );
0663     }
0664     const int totalh = rowController()->totalHeight();
0665     if ( r.height() < totalh ) {
0666         r.setHeight( totalh );
0667     }
0668 
0669     scene()->setSceneRect( r );
0670 
0671     QGraphicsView::resizeEvent( ev );
0672 }
0673 
0674 
0675 QModelIndex GraphicsView::indexAt( const QPoint& pos ) const
0676 {
0677     QGraphicsItem* item = itemAt( pos );
0678     if ( GraphicsItem* gitem = qgraphicsitem_cast<GraphicsItem*>( item ) ) {
0679         return d->scene.summaryHandlingModel()->mapToSource( gitem->index() );
0680     } else {
0681         return QModelIndex();
0682     }
0683 }
0684 
0685 
0686 void GraphicsView::clearItems()
0687 {
0688     d->scene.clearItems();
0689 }
0690 
0691 
0692 void GraphicsView::updateRow( const QModelIndex& idx )
0693 {
0694     d->scene.updateRow( d->scene.summaryHandlingModel()->mapFromSource( idx ) );
0695 }
0696 
0697 
0698 void GraphicsView::updateSceneRect()
0699 {
0700     /* What to do with this? We need to shrink the view to
0701      * make collapsing items work
0702      */
0703     qreal range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum();
0704     const qreal hscroll = horizontalScrollBar()->value()/( range>0?range:1 );
0705     QRectF r = d->scene.itemsBoundingRect();
0706     // To scroll more to the left than the actual item start, bug #4516
0707     r.setTop( 0. );
0708     r.setLeft( qMin<qreal>( 0.0, r.left() ) );
0709     r.setSize( r.size().expandedTo( viewport()->size() ) );
0710     const int totalh = rowController()->totalHeight();
0711     if ( r.height() < totalh ) r.setHeight( totalh );
0712     d->scene.setSceneRect( r );
0713 
0714     /* set scrollbar to keep the same time in view */
0715     range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum();
0716     if ( range>0 ) {
0717         horizontalScrollBar()->setValue( qRound( hscroll*range ) );
0718     } else {
0719         // keep header in sync with scene
0720         d->headerwidget.scrollTo(r.left());
0721     }
0722     /* We have to update here to adjust for any rows with no
0723      * information because they are painted with a different
0724      * background brush
0725      */
0726     d->scene.invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
0727 }
0728 
0729 
0730 void GraphicsView::updateScene()
0731 {
0732     clearItems();
0733     if ( !model()) return;
0734     if ( !rowController()) return;
0735     QModelIndex idx = model()->index( 0, 0, rootIndex() );
0736     do {
0737         updateRow( idx );
0738     } while ( ( idx = rowController()->indexBelow( idx ) ) != QModelIndex() && rowController()->isRowVisible(idx) );
0739     //constraintModel()->cleanup();
0740     //qDebug() << constraintModel();
0741     updateSceneRect();
0742     if ( scene() ) scene()->invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
0743 }
0744 
0745 #if 0
0746 TODO: For 3.0
0747 
0748 GraphicsItem* GraphicsView::createItem( ItemType type ) const
0749 {
0750     Q_UNUSED(type)
0751     return new GraphicsItem;
0752 }
0753 #endif
0754 
0755 
0756 void GraphicsView::deleteSubtree( const QModelIndex& idx )
0757 {
0758     d->scene.deleteSubtree( d->scene.summaryHandlingModel()->mapFromSource( idx ) );
0759 }
0760 
0761 
0762 void GraphicsView::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
0763 {
0764     d->scene.print( printer, drawRowLabels, drawColumnLabels );
0765 }
0766 
0767 
0768 void GraphicsView::print( QPrinter* printer,  qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
0769 {
0770     d->scene.print( printer, start, end, drawRowLabels, drawColumnLabels );
0771 }
0772 
0773 
0774 void GraphicsView::print( QPainter* painter, const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels )
0775 {
0776   d->scene.print(painter, targetRect, drawRowLabels, drawColumnLabels);
0777 }
0778 
0779 
0780 void GraphicsView::print( QPainter* painter, qreal start, qreal end,
0781                           const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels )
0782 {
0783   d->scene.print(painter, start, end, targetRect, drawRowLabels, drawColumnLabels);
0784 }
0785 
0786 void GraphicsView::printDiagram( QPrinter *printer, const PrintingContext &context )
0787 {
0788     d->scene.printDiagram( printer, context );
0789 }
0790 
0791 #include "moc_kganttgraphicsview.cpp"