kdganttview.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL included.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "kdganttview.h"
00024 #include "kdganttview_p.h"
00025 
00026 #include "kdganttitemdelegate.h"
00027 #include "kdganttgraphicsitem.h"
00028 #include "kdganttsummaryhandlingproxymodel.h"
00029 
00030 #include <QAbstractItemModel>
00031 #include <QHeaderView>
00032 #include <QVBoxLayout>
00033 #include <QGraphicsItem>
00034 #include <QGraphicsRectItem>
00035 #include <QScrollBar>
00036 #include <QPaintEvent>
00037 
00038 #include <QDebug>
00039 
00040 #include <cassert>
00041 
00042 #if defined KDAB_EVAL
00043 #include "../evaldialog/evaldialog.h"
00044 #endif
00045 
00046 using namespace KDGantt;
00047 
00048 namespace {
00049     class HeaderView : public QHeaderView {
00050     public:
00051         explicit HeaderView( QWidget* parent=0 ) : QHeaderView( Qt::Horizontal, parent ) {
00052         }
00053 
00054         QSize sizeHint() const { QSize s = QHeaderView::sizeHint(); s.rheight() *= 2; return s; }
00055     };
00056 }
00057 
00058 KDGanttTreeView::KDGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent )
00059     : QTreeView( parent ),
00060       m_controller( this, proxy )
00061 {
00062     setHeader( new HeaderView );
00063 }
00064 
00065 KDGanttTreeView::~KDGanttTreeView()
00066 {
00067 }
00068 
00069 void KDGanttTreeView::expandAll(QModelIndex index)
00070 {
00071     for(int i = 0; i < model()->rowCount(index); i++) {
00072         QModelIndex indexAt = model()->index(i, 0, index);
00073         if(model()->hasChildren(indexAt))
00074             expandAll(indexAt);
00075         if(isExpanded(indexAt))
00076             continue;
00077         expand(indexAt);
00078     }
00079 }
00080 
00081 void KDGanttTreeView::collapseAll(QModelIndex index)
00082 {
00083     for(int i = 0; i < model()->rowCount(index); i++) {
00084         QModelIndex indexAt = model()->index(i, 0, index);
00085         if(model()->hasChildren(indexAt))
00086             collapseAll(indexAt);
00087         if(!isExpanded(indexAt))
00088             continue;
00089         collapse(indexAt);
00090     }
00091 }
00092 
00093 View::Private::Private(View* v)
00094     : q(v),
00095       splitter(v),
00096       rowController(0),
00097       gfxview(&splitter),
00098       model(0)
00099 {
00100     //init();
00101 }
00102 
00103 View::Private::~Private()
00104 {
00105 }
00106 
00107 void View::Private::init()
00108 {
00109     KDGanttTreeView* tw = new KDGanttTreeView( &ganttProxyModel, &splitter );
00110     tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00111     tw->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
00112 
00113     q->setLeftView( tw );
00114     q->setRowController( tw->rowController() );
00115 
00116     gfxview.setAlignment(Qt::AlignTop|Qt::AlignLeft);
00117     //gfxview.setRenderHints( QPainter::Antialiasing );
00118 
00119     tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
00120     gfxview.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
00121 
00122     QVBoxLayout* layout = new QVBoxLayout(q);
00123     layout->setMargin(0);
00124     layout->addWidget(&splitter);
00125     q->setLayout(layout);
00126 
00127     constraintProxy.setProxyModel( &ganttProxyModel );
00128     constraintProxy.setDestinationModel( &mappedConstraintModel );
00129     gfxview.setSelectionModel( leftWidget->selectionModel() );
00130     gfxview.setConstraintModel( &mappedConstraintModel );
00131 }
00132 
00133 void View::Private::updateScene()
00134 {
00135     gfxview.clearItems();
00136     if( !model) return;
00137 
00138     if( QTreeView* tw = qobject_cast<QTreeView*>(leftWidget)) {
00139       QModelIndex idx = ganttProxyModel.mapFromSource( model->index( 0, 0, leftWidget->rootIndex() ) );
00140       do {
00141         gfxview.updateRow( idx );
00142       } while ( ( idx = tw->indexBelow( idx ) ) != QModelIndex() &&
00143                 gfxview.rowController()->isRowVisible(idx) );
00144       gfxview.updateSceneRect();
00145     } else {
00146       const QModelIndex rootidx = ganttProxyModel.mapFromSource( leftWidget->rootIndex() );
00147       for( int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r ) {
00148         gfxview.updateRow( ganttProxyModel.index( r, 0, rootidx ) );
00149       }
00150     }
00151 }
00152 
00153 void View::Private::slotCollapsed(const QModelIndex& _idx)
00154 {
00155     QTreeView* tw = qobject_cast<QTreeView*>(leftWidget);
00156     if(!tw) return;
00157 
00158     bool blocked = gfxview.blockSignals( true );
00159 
00160     QModelIndex idx( _idx );
00161     const QAbstractItemModel* model = leftWidget->model();
00162     const QModelIndex pidx = ganttProxyModel.mapFromSource(idx);
00163     bool isMulti = false;
00164     for( QModelIndex treewalkidx = pidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
00165         if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
00166              && !gfxview.rowController()->isRowExpanded( treewalkidx ) ) {
00167             isMulti = true;
00168             break;
00169         }
00170     }
00171 
00172     if ( !isMulti ) {
00173         for ( int i = 0; i < model->rowCount( idx ); ++i ) {
00174             gfxview.deleteSubtree( ganttProxyModel.index( i, 0, pidx ) );
00175         }
00176     } else {
00177         gfxview.updateRow(pidx);
00178     }
00179     //qDebug() << "Looking to update from " << idx;
00180     while ( ( idx=tw->indexBelow( idx ) ) != QModelIndex() &&
00181             gfxview.rowController()->isRowVisible( ganttProxyModel.mapFromSource(idx) ) ) {
00182         const QModelIndex proxyidx( ganttProxyModel.mapFromSource( idx ) );
00183         gfxview.updateRow(proxyidx);
00184     }
00185     gfxview.blockSignals( blocked );
00186     gfxview.updateSceneRect();
00187 }
00188 
00189 void View::Private::slotExpanded(const QModelIndex& _idx)
00190 {
00191     QModelIndex idx( ganttProxyModel.mapFromSource( _idx ) );
00192     do {
00193         //qDebug() << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString();
00194         gfxview.updateRow(idx);
00195     } while( ( idx=gfxview.rowController()->indexBelow( idx ) ) != QModelIndex()
00196              && gfxview.rowController()->isRowVisible( idx ) );
00197     gfxview.updateSceneRect();
00198 }
00199 
00200 void View::Private::slotVerticalScrollValueChanged( int val )
00201 {
00202 #if 0
00203     qDebug() << "View::Private::slotVerticalScrollValueChanged("<<val<<")="
00204              << val/gfxview.verticalScrollBar()->singleStep();
00205 #endif
00206     leftWidget->verticalScrollBar()->setValue( val/gfxview.verticalScrollBar()->singleStep() );
00207 }
00208 
00209 void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max )
00210 {
00211     //qDebug() << "View::Private::slotLeftWidgetVerticalRangeChanged("<<min<<max<<")";
00212     gfxview.verticalScrollBar()->setRange( min, max );
00213     gfxview.updateSceneRect();
00214 }
00215 
00216 void View::Private::slotGfxViewVerticalRangeChanged( int min, int max )
00217 {
00218     //qDebug() << "View::Private::slotGfxViewVerticalRangeChanged("<<min<<max<<")";
00219     int leftMin = leftWidget->verticalScrollBar()->minimum();
00220     int leftMax = leftWidget->verticalScrollBar()->maximum();
00221     bool blocked = gfxview.verticalScrollBar()->blockSignals( true );
00222     gfxview.verticalScrollBar()->setRange( qMax( min, leftMin ), qMax( max, leftMax ) );
00223     gfxview.verticalScrollBar()->blockSignals( blocked );
00224 }
00225 
00239 View::View(QWidget* parent)
00240     : QWidget(parent),
00241       _d(new Private(this))
00242 {
00243 #if defined KDAB_EVAL
00244    EvalDialog::checkEvalLicense( "KD Gantt" );
00245 #endif
00246    _d->init();
00247 }
00248 
00249 View::~View()
00250 {
00251     delete _d;
00252 }
00253 
00254 #define d d_func()
00255 
00261 void View::setLeftView( QAbstractItemView* aiv )
00262 {
00263     assert( aiv );
00264     if ( aiv==d->leftWidget ) return;
00265     if ( !d->leftWidget.isNull() ) {
00266         d->leftWidget->disconnect( this );
00267         d->leftWidget->hide();
00268         d->leftWidget->verticalScrollBar()->disconnect( d->gfxview.verticalScrollBar() );
00269         d->gfxview.verticalScrollBar()->disconnect( d->leftWidget->verticalScrollBar() );
00270     }
00271 
00272     d->leftWidget = aiv;
00273     d->splitter.insertWidget( 0, d->leftWidget );
00274 
00275     if( qobject_cast<QTreeView*>(d->leftWidget) ) {
00276       connect( d->leftWidget,  SIGNAL( collapsed( const QModelIndex& ) ),
00277                this, SLOT( slotCollapsed( const QModelIndex& ) ) );
00278       connect( d->leftWidget,  SIGNAL( expanded( const QModelIndex& ) ),
00279                this, SLOT( slotExpanded( const QModelIndex& ) ) );
00280     }
00281 
00282     connect( d->gfxview.verticalScrollBar(), SIGNAL( valueChanged( int ) ),
00283              d->leftWidget->verticalScrollBar(), SLOT( setValue( int ) ) );
00284     connect( d->leftWidget->verticalScrollBar(), SIGNAL( valueChanged( int ) ),
00285              d->gfxview.verticalScrollBar(), SLOT( setValue( int ) ) );
00286     connect( d->leftWidget->verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ),
00287              this, SLOT( slotLeftWidgetVerticalRangeChanged( int, int ) ) );
00288     connect( d->gfxview.verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ),
00289              this, SLOT( slotGfxViewVerticalRangeChanged( int, int ) ) );
00290 }
00291 
00297 void View::setRowController( AbstractRowController* ctrl )
00298 {
00299     if ( ctrl == d->rowController ) return;
00300     d->rowController = ctrl;
00301     d->gfxview.setRowController( d->rowController );
00302 }
00303 
00307 AbstractRowController* View::rowController()
00308 {
00309     return d->rowController;
00310 }
00311 
00314 const AbstractRowController* View::rowController() const
00315 {
00316     return d->rowController;
00317 }
00318 
00323 const QAbstractItemView* View::leftView() const
00324 {
00325     return d->leftWidget;
00326 }
00327 
00331 QAbstractItemView* View::leftView()
00332 {
00333     return d->leftWidget;
00334 }
00335 
00339 const GraphicsView* View::graphicsView() const
00340 {
00341     return &d->gfxview;
00342 }
00343 
00347 GraphicsView* View::graphicsView()
00348 {
00349     return &d->gfxview;
00350 }
00351 
00355 const QSplitter* View::splitter() const
00356 {
00357     return &d->splitter;
00358 }
00359 
00363 QSplitter* View::splitter()
00364 {
00365     return &d->splitter;
00366 }
00367 
00368 
00371 QAbstractItemModel* View::model() const
00372 {
00373     return leftView()->model();
00374 }
00375 
00381 void View::setModel( QAbstractItemModel* model )
00382 {
00383     leftView()->setModel( model );
00384     d->ganttProxyModel.setSourceModel( model );
00385     d->gfxview.setModel( &d->ganttProxyModel );
00386 }
00387 
00390 QItemSelectionModel* View::selectionModel() const
00391 {
00392     return leftView()->selectionModel();
00393 }
00394 
00398 void View::setSelectionModel( QItemSelectionModel* smodel )
00399 {
00400     leftView()->setSelectionModel( smodel );
00401     d->gfxview.setSelectionModel( new QItemSelectionModel( &( d->ganttProxyModel ),this ) );
00402 }
00403 
00409 void View::setGrid( AbstractGrid* grid )
00410 {
00411     d->gfxview.setGrid( grid );
00412 }
00413 
00414 void View::expandAll( QModelIndex index )
00415 {
00416     KDGanttTreeView* tw = qobject_cast<KDGanttTreeView*>(leftView());
00417     tw->expandAll(index);
00418 }
00419 
00420 void View::collapseAll( QModelIndex index )
00421 {
00422     KDGanttTreeView* tw = qobject_cast<KDGanttTreeView*>(leftView());
00423     tw->collapseAll(index);
00424 }
00425 
00428 AbstractGrid* View::grid() const
00429 {
00430     return d->gfxview.grid();
00431 }
00432 
00435 QModelIndex View::rootIndex() const
00436 {
00437     return leftView()->rootIndex();
00438 }
00439 
00443 void View::setRootIndex( const QModelIndex& idx )
00444 {
00445     leftView()->setRootIndex( idx );
00446     d->gfxview.setRootIndex( idx );
00447 }
00448 
00451 ItemDelegate* View::itemDelegate() const
00452 {
00453     return d->gfxview.itemDelegate();
00454 }
00455 
00459 void View::setItemDelegate( ItemDelegate* delegate )
00460 {
00461     leftView()->setItemDelegate( delegate );
00462     d->gfxview.setItemDelegate( delegate );
00463 }
00464 
00468 void View::setConstraintModel( ConstraintModel* cm )
00469 {
00470     d->constraintProxy.setSourceModel( cm );
00471     d->gfxview.setConstraintModel( &d->mappedConstraintModel );
00472 }
00473 
00476 ConstraintModel* View::constraintModel() const
00477 {
00478     return d->constraintProxy.sourceModel();
00479 }
00480 
00481 const QAbstractProxyModel* View::ganttProxyModel() const
00482 {
00483     return &( d->ganttProxyModel );
00484 }
00485 
00486 QAbstractProxyModel* View::ganttProxyModel()
00487 {
00488     return &( d->ganttProxyModel );
00489 }
00490 
00491 void View::ensureVisible(const QModelIndex& index)
00492 {
00493     QGraphicsView* view = graphicsView();
00494     KDGantt::GraphicsScene* scene = static_cast<KDGantt::GraphicsScene*>(view->scene());
00495     if(!scene)
00496         return;
00497 
00498     KDGantt::SummaryHandlingProxyModel* model = static_cast<KDGantt::SummaryHandlingProxyModel*>(scene->summaryHandlingModel());
00499 
00500     const QModelIndex pidx = d->ganttProxyModel.mapFromSource(index);
00501     const QModelIndex idx = model->mapFromSource( pidx );
00502     QGraphicsItem* item = scene->findItem(idx);
00503     view->ensureVisible(item);
00504 }
00505 
00506 void View::resizeEvent(QResizeEvent*ev)
00507 {
00508     QWidget::resizeEvent(ev);
00509 }
00510 
00517 QModelIndex View::indexAt( const QPoint& pos ) const
00518 {
00519     return d->gfxview.indexAt( pos );
00520 }
00521 
00528 void View::print( QPrinter* printer, bool drawRowLabels )
00529 {
00530     graphicsView()->print( printer, drawRowLabels );
00531 }
00532 
00543 void View::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels )
00544 {
00545     graphicsView()->print( printer, start, end, drawRowLabels );
00546 }
00547 
00552 void View::print( QPainter* painter, const QRectF& target, bool drawRowLabels)
00553 {
00554     d->gfxview.print( painter,
00555                       target,
00556                       drawRowLabels);
00557 }
00558 
00567 void View::print( QPainter* painter, qreal start, qreal end, const QRectF& target, bool drawRowLabels)
00568 {
00569     d->gfxview.print( painter,
00570                       start, end,
00571                       target,
00572                       drawRowLabels);
00573 }
00574 
00575 
00576 #include "moc_kdganttview.cpp"
00577 
00578 #ifndef KDAB_NO_UNIT_TESTS
00579 #include "unittest/test.h"
00580 
00581 #include "kdganttlistviewrowcontroller.h"
00582 #include <QApplication>
00583 #include <QTimer>
00584 #include <QPixmap>
00585 #include <QListView>
00586 
00587 namespace {
00588     std::ostream& operator<<( std::ostream& os, const QImage& img )
00589     {
00590         os << "QImage[ size=("<<img.width()<<", "<<img.height()<<")]";
00591         return os;
00592     }
00593 }
00594 
00595 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, View, "test" ) {
00596     View view( 0 );
00597 #if 0 // GUI tests do not work well on the server
00598     QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
00599     view.show();
00600 
00601     qApp->exec();
00602     QPixmap screenshot1 = QPixmap::grabWidget( &view );
00603 
00604     QTreeView* tv = new QTreeView;
00605     view.setLeftView( tv );
00606     view.setRowController( new TreeViewRowController(tv,view.ganttProxyModel()) );
00607 
00608     QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
00609 
00610     qApp->exec();
00611     QPixmap screenshot2 = QPixmap::grabWidget( &view );
00612 
00613     assertEqual( screenshot1.toImage(),  screenshot2.toImage() );
00614 
00615     QListView* lv = new QListView;
00616     view.setLeftView(lv);
00617     view.setRowController( new ListViewRowController(lv,view.ganttProxyModel()));
00618     view.show();
00619     QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
00620     qApp->exec();
00621 #endif
00622 }
00623 #endif /* KDAB_NO_UNIT_TESTS */