1 /****************************************************************************
2 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
4 ** This file is part of the KD Gantt library.
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).
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.
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>
35 #include <QPaintEvent>
42 #include "../evaldialog/evaldialog.h"
45 using namespace KDGantt
;
48 class HeaderView
: public QHeaderView
{
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
)
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
);
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
);
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();
113 if( QTreeView
* tw
= qobject_cast
<QTreeView
*>(leftWidget
)) {
114 QModelIndex idx
= ganttProxyModel
.mapFromSource( model
->index( 0, 0, leftWidget
->rootIndex() ) );
116 gfxview
.updateRow( idx
);
117 } while ( ( idx
= tw
->indexBelow( idx
) ) != QModelIndex() &&
118 gfxview
.rowController()->isRowVisible(idx
) );
119 gfxview
.updateSceneRect();
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
);
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
) );
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
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
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
)
193 _d(new Private(this))
195 #if defined KDAB_EVAL
196 EvalDialog::checkEvalLicense( "KD Gantt" );
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
)
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() );
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.
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
298 * \overload const GraphicsView* KDGantt::View::graphicsView() const
300 GraphicsView
* View::graphicsView()
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
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>
457 std::ostream
& operator<<( std::ostream
& os
, const QImage
& img
)
459 os
<< "QImage[ size=("<<img
.width()<<", "<<img
.height()<<")]";
464 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt
, View
, "test" ) {
466 #if 0 // GUI tests do not work well on the server
467 QTimer::singleShot( 1000, qApp
, SLOT( quit() ) );
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() ) );
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()));
488 QTimer::singleShot( 1000, qApp
, SLOT( quit() ) );
492 #endif /* KDAB_NO_UNIT_TESTS */