Backport r950340 | aacid | 2009-04-06 23:21:18 +0200 (Mon, 06 Apr 2009) | 4 lines
[kdepim.git] / kdgantt / kdganttview.cpp
blob3f3982ce343e7971e613ea97183582b987252011
1 /****************************************************************************
2 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Gantt library.
5 **
6 ** This file may be used under the terms of the GNU General Public
7 ** License versions 2.0 or 3.0 as published by the Free Software
8 ** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
9 ** included in the packaging of this file. Alternatively you may (at
10 ** your option) use any later version of the GNU General Public
11 ** License if such license has been publicly approved by
12 ** Klarälvdalens Datakonsult AB (or its successors, if any).
13 **
14 ** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
15 ** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
16 ** A PARTICULAR PURPOSE. Klarälvdalens Datakonsult AB reserves all rights
17 ** not expressly granted herein.
18 **
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 **********************************************************************/
23 #include "kdganttview.h"
24 #include "kdganttview_p.h"
26 #include "kdganttitemdelegate.h"
27 #include "kdganttgraphicsitem.h"
29 #include <QAbstractItemModel>
30 #include <QHeaderView>
31 #include <QVBoxLayout>
32 #include <QGraphicsItem>
33 #include <QGraphicsRectItem>
34 #include <QScrollBar>
35 #include <QPaintEvent>
37 #include <QDebug>
39 #include <cassert>
41 #if defined KDAB_EVAL
42 #include "../evaldialog/evaldialog.h"
43 #endif
45 using namespace KDGantt;
47 namespace {
48 class HeaderView : public QHeaderView {
49 public:
50 explicit HeaderView( QWidget* parent=0 ) : QHeaderView( Qt::Horizontal, parent ) {
53 QSize sizeHint() const { QSize s = QHeaderView::sizeHint(); s.rheight() *= 2; return s; }
57 KDGanttTreeView::KDGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent )
58 : QTreeView( parent ),
59 m_controller( this, proxy )
61 setHeader( new HeaderView );
64 KDGanttTreeView::~KDGanttTreeView()
68 View::Private::Private(View* v)
69 : q(v),
70 splitter(v),
71 rowController(0),
72 gfxview(&splitter),
73 model(0)
75 //init();
78 View::Private::~Private()
82 void View::Private::init()
84 KDGanttTreeView* tw = new KDGanttTreeView( &ganttProxyModel, &splitter );
85 tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
86 tw->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
88 q->setLeftView( tw );
89 q->setRowController( tw->rowController() );
91 gfxview.setAlignment(Qt::AlignTop|Qt::AlignLeft);
92 //gfxview.setRenderHints( QPainter::Antialiasing );
94 tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
95 gfxview.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
97 QVBoxLayout* layout = new QVBoxLayout(q);
98 layout->setMargin(0);
99 layout->addWidget(&splitter);
100 q->setLayout(layout);
102 constraintProxy.setProxyModel( &ganttProxyModel );
103 constraintProxy.setDestinationModel( &mappedConstraintModel );
104 gfxview.setSelectionModel( leftWidget->selectionModel() );
105 gfxview.setConstraintModel( &mappedConstraintModel );
108 void View::Private::updateScene()
110 gfxview.clearItems();
111 if( !model) return;
113 if( QTreeView* tw = qobject_cast<QTreeView*>(leftWidget)) {
114 QModelIndex idx = ganttProxyModel.mapFromSource( model->index( 0, 0, leftWidget->rootIndex() ) );
115 do {
116 gfxview.updateRow( idx );
117 } while ( ( idx = tw->indexBelow( idx ) ) != QModelIndex() &&
118 gfxview.rowController()->isRowVisible(idx) );
119 gfxview.updateSceneRect();
120 } else {
121 const QModelIndex rootidx = ganttProxyModel.mapFromSource( leftWidget->rootIndex() );
122 for( int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r ) {
123 gfxview.updateRow( ganttProxyModel.index( r, 0, rootidx ) );
128 void View::Private::slotCollapsed(const QModelIndex& _idx)
130 QTreeView* tw = qobject_cast<QTreeView*>(leftWidget);
131 if(!tw) return;
132 QModelIndex idx( _idx );
133 const QAbstractItemModel* model = leftWidget->model();
134 const QModelIndex pidx = ganttProxyModel.mapFromSource(idx);
135 if ( pidx.data( ItemTypeRole ).toInt() != TypeMulti ) {
136 for ( int i = 0; i < model->rowCount( idx ); ++i ) {
137 gfxview.deleteSubtree( ganttProxyModel.index( i, 0, pidx ) );
140 //qDebug() << "Looking to update from " << idx;
141 while ( ( idx=tw->indexBelow( idx ) ) != QModelIndex() &&
142 gfxview.rowController()->isRowVisible( ganttProxyModel.mapFromSource(idx) ) ) {
143 const QModelIndex proxyidx( ganttProxyModel.mapFromSource( idx ) );
144 gfxview.updateRow(proxyidx);
146 gfxview.updateSceneRect();
149 void View::Private::slotExpanded(const QModelIndex& _idx)
151 QModelIndex idx( ganttProxyModel.mapFromSource( _idx ) );
152 do {
153 gfxview.updateRow(idx);
154 } while( ( idx=gfxview.rowController()->indexBelow( idx ) ) != QModelIndex()
155 && gfxview.rowController()->isRowVisible( idx ) );
156 gfxview.updateSceneRect();
159 void View::Private::slotVerticalScrollValueChanged( int val )
161 leftWidget->verticalScrollBar()->setValue( val/gfxview.verticalScrollBar()->singleStep() );
164 void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max )
166 gfxview.verticalScrollBar()->setRange( min, max );
169 void View::Private::slotGfxViewVerticalRangeChanged( int min, int max )
171 int leftMin = leftWidget->verticalScrollBar()->minimum();
172 int leftMax = leftWidget->verticalScrollBar()->maximum();
173 bool blocked = gfxview.verticalScrollBar()->blockSignals( true );
174 gfxview.verticalScrollBar()->setRange( qMax( min, leftMin ), qMax( max, leftMax ) );
175 gfxview.verticalScrollBar()->blockSignals( blocked );
178 /*!\class KDGantt::View kdganttview.h KDGanttView
179 * \ingroup KDGantt
180 * \brief This widget that consists of a QTreeView and a GraphicsView
182 * This is the easy to use, complete gantt chart widget. It
183 * consists of a QTreeView on the left and a KDGantt::GraphicsView
184 * on the right separated by a QSplitter. The two views share the same
185 * model.
188 /*! Constructor. Creates a View with parent \a parent,
189 * a DateTimeGrid as default grid implementaion and no model etc.
191 View::View(QWidget* parent)
192 : QWidget(parent),
193 _d(new Private(this))
195 #if defined KDAB_EVAL
196 EvalDialog::checkEvalLicense( "KD Gantt" );
197 #endif
198 _d->init();
201 View::~View()
203 delete _d;
206 #define d d_func()
208 /*! \param aiv The view to be used to the left, instead of the default tree view
209 * \sa setRowController()
211 void View::setLeftView( QAbstractItemView* aiv )
213 assert( aiv );
214 if ( aiv==d->leftWidget ) return;
215 if ( !d->leftWidget.isNull() ) {
216 d->leftWidget->disconnect( this );
217 d->leftWidget->hide();
218 d->leftWidget->verticalScrollBar()->disconnect( d->gfxview.verticalScrollBar() );
219 d->gfxview.verticalScrollBar()->disconnect( d->leftWidget->verticalScrollBar() );
222 d->leftWidget = aiv;
223 d->splitter.insertWidget( 0, d->leftWidget );
225 if( qobject_cast<QTreeView*>(d->leftWidget) ) {
226 connect( d->leftWidget, SIGNAL( collapsed( const QModelIndex& ) ),
227 this, SLOT( slotCollapsed( const QModelIndex& ) ) );
228 connect( d->leftWidget, SIGNAL( expanded( const QModelIndex& ) ),
229 this, SLOT( slotExpanded( const QModelIndex& ) ) );
232 connect( d->gfxview.verticalScrollBar(), SIGNAL( valueChanged( int ) ),
233 d->leftWidget->verticalScrollBar(), SLOT( setValue( int ) ) );
234 connect( d->leftWidget->verticalScrollBar(), SIGNAL( valueChanged( int ) ),
235 d->gfxview.verticalScrollBar(), SLOT( setValue( int ) ) );
236 connect( d->leftWidget->verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ),
237 this, SLOT( slotLeftWidgetVerticalRangeChanged( int, int ) ) );
238 connect( d->gfxview.verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ),
239 this, SLOT( slotGfxViewVerticalRangeChanged( int, int ) ) );
241 setTabOrder( &(d->splitter), d->leftWidget );
242 setTabOrder( d->leftWidget, graphicsView() );
245 /*! Sets \a ctrl to be the rowcontroller used by this View.
246 * The default rowcontroller is owned by KDGantt::View and is
247 * suitable for the default treeview in the left part of the view.
248 * You probably only want to change this if you replace the treeview.
250 void View::setRowController( AbstractRowController* ctrl )
252 if ( ctrl == d->rowController ) return;
253 d->rowController = ctrl;
254 d->gfxview.setRowController( d->rowController );
257 /*! \returns a pointer to the current rowcontroller.
258 * \see AbstractRowController
260 AbstractRowController* View::rowController()
262 return d->rowController;
265 /*! \overload AbstractRowController* KDGantt::View::rowController()
267 const AbstractRowController* View::rowController() const
269 return d->rowController;
273 * \returns a pointer to the QAbstractItemView in the left
274 * part of the widget.
275 * */
276 const QAbstractItemView* View::leftView() const
278 return d->leftWidget;
282 * \overload const QAbstractItemView* KDGantt::View::leftView() const
284 QAbstractItemView* View::leftView()
286 return d->leftWidget;
290 * \returns a pointer to the GraphicsView
292 const GraphicsView* View::graphicsView() const
294 return &d->gfxview;
298 * \overload const GraphicsView* KDGantt::View::graphicsView() const
300 GraphicsView* View::graphicsView()
302 return &d->gfxview;
305 /*! \returns the current model displayed by this view
307 QAbstractItemModel* View::model() const
309 return leftView()->model();
312 /*! Sets the QAbstractItemModel to be displayed in this view
313 * to \a model.
315 * \see GraphicsView::setModel
317 void View::setModel( QAbstractItemModel* model )
319 leftView()->setModel( model );
320 d->ganttProxyModel.setSourceModel( model );
321 d->gfxview.setModel( &d->ganttProxyModel );
324 /*! \returns the QItemSelectionModel used by this view
326 QItemSelectionModel* View::selectionModel() const
328 return leftView()->selectionModel();
331 /*! Sets the QItemSelectionModel used by this view to manage
332 * selections. Similar to QAbstractItemView::setSelectionModel
334 void View::setSelectionModel( QItemSelectionModel* smodel )
336 leftView()->setSelectionModel( smodel );
337 d->gfxview.setSelectionModel( new QItemSelectionModel( &( d->ganttProxyModel ),this ) );
340 /*! Sets the AbstractGrid for this view. The grid is an
341 * object that controls how QModelIndexes are mapped
342 * to and from the view and how the background and header
343 * is rendered. \see AbstractGrid and DateTimeGrid.
345 void View::setGrid( AbstractGrid* grid )
347 d->gfxview.setGrid( grid );
350 /*! \returns the AbstractGrid used by this view.
352 AbstractGrid* View::grid() const
354 return d->gfxview.grid();
357 /*! \returns the rootindex for this view.
359 QModelIndex View::rootIndex() const
361 return leftView()->rootIndex();
364 /*! Sets the root index of the model displayed by this view.
365 * Similar to QAbstractItemView::setRootIndex, default is QModelIndex().
367 void View::setRootIndex( const QModelIndex& idx )
369 leftView()->setRootIndex( idx );
370 d->gfxview.setRootIndex( idx );
373 /*! \returns the ItemDelegate used by this view to render items
375 ItemDelegate* View::itemDelegate() const
377 return d->gfxview.itemDelegate();
380 /*! Sets the KDGantt::ItemDelegate used for rendering items on this
381 * view. \see ItemDelegate and QAbstractItemDelegate.
383 void View::setItemDelegate( ItemDelegate* delegate )
385 leftView()->setItemDelegate( delegate );
386 d->gfxview.setItemDelegate( delegate );
389 /*! Sets the constraintmodel displayed by this view.
390 * \see KDGantt::ConstraintModel.
392 void View::setConstraintModel( ConstraintModel* cm )
394 d->constraintProxy.setSourceModel( cm );
395 d->gfxview.setConstraintModel( &d->mappedConstraintModel );
398 /*! \returns the KDGantt::ConstraintModel displayed by this view.
400 ConstraintModel* View::constraintModel() const
402 return d->constraintProxy.sourceModel();
405 const QAbstractProxyModel* View::ganttProxyModel() const
407 return &( d->ganttProxyModel );
410 QAbstractProxyModel* View::ganttProxyModel()
412 return &( d->ganttProxyModel );
415 void View::resizeEvent(QResizeEvent*ev)
417 QWidget::resizeEvent(ev);
420 /*!\returns The QModelIndex for the item located at
421 * position \a pos in the view or an invalid index
422 * if no item was present at that position.
424 * \see GraphicsView::indexAt
426 QModelIndex View::indexAt( const QPoint& pos ) const
428 return d->gfxview.indexAt( pos );
431 /*! Render the GanttView inside the rectangle \a target using the painter \a painter.
432 * \see KDGantt::GraphicsView::print(QPainter* painter, const QRectF& target,bool drawRowLabels)
434 void View::print( QPainter* painter, const QRectF& target, const QRectF& source, bool drawRowLabels, bool drawHeader)
437 QRectF targetRect = target;
438 if( targetRect.isNull() ) {
439 targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
441 d->gfxview.print( painter, targetRect, source, drawRowLabels, drawHeader);
445 #include "moc_kdganttview.cpp"
447 #ifndef KDAB_NO_UNIT_TESTS
448 #include "unittest/test.h"
450 #include "kdganttlistviewrowcontroller.h"
451 #include <QApplication>
452 #include <QTimer>
453 #include <QPixmap>
454 #include <QListView>
456 namespace {
457 std::ostream& operator<<( std::ostream& os, const QImage& img )
459 os << "QImage[ size=("<<img.width()<<", "<<img.height()<<")]";
460 return os;
464 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, View, "test" ) {
465 View view( 0 );
466 #if 0 // GUI tests do not work well on the server
467 QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
468 view.show();
470 qApp->exec();
471 QPixmap screenshot1 = QPixmap::grabWidget( &view );
473 QTreeView* tv = new QTreeView;
474 view.setLeftView( tv );
475 view.setRowController( new TreeViewRowController(tv,view.ganttProxyModel()) );
477 QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
479 qApp->exec();
480 QPixmap screenshot2 = QPixmap::grabWidget( &view );
482 assertEqual( screenshot1.toImage(), screenshot2.toImage() );
484 QListView* lv = new QListView;
485 view.setLeftView(lv);
486 view.setRowController( new ListViewRowController(lv,view.ganttProxyModel()));
487 view.show();
488 QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
489 qApp->exec();
490 #endif
492 #endif /* KDAB_NO_UNIT_TESTS */