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 distributed and/or modified under the terms of the
7 ** GNU General Public License version 2 as published by the Free Software
8 ** Foundation and appearing in the file LICENSE.GPL included in the
9 ** packaging of this file.
11 ** Licensees holding valid commercial KD Gantt licenses may use this file in
12 ** accordance with the KD Gantt Commercial License Agreement provided with
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 ** See http://www.kdab.net/kdgantt for
19 ** information about KD Gantt Commercial License Agreements.
21 ** Contact info@kdab.net if any conditions of this
22 ** licensing are not clear to you.
24 **********************************************************************/
25 #include "kdganttview.h"
26 #include "kdganttview_p.h"
28 #include "kdganttitemdelegate.h"
29 #include "kdganttgraphicsitem.h"
31 #include <QAbstractItemModel>
32 #include <QHeaderView>
33 #include <QVBoxLayout>
34 #include <QGraphicsItem>
35 #include <QGraphicsRectItem>
37 #include <QPaintEvent>
39 #include "kdgantt_debug.h"
44 #include "../evaldialog/evaldialog.h"
47 using namespace KDGantt
;
51 class HeaderView
: public QHeaderView
54 explicit HeaderView(QWidget
*parent
= Q_NULLPTR
) : QHeaderView(Qt::Horizontal
, parent
)
58 QSize
sizeHint() const Q_DECL_OVERRIDE
60 QSize s
= QHeaderView::sizeHint();
67 KDGanttTreeView::KDGanttTreeView(QAbstractProxyModel
*proxy
, QWidget
*parent
)
69 m_controller(this, proxy
)
71 setHeader(new HeaderView
);
74 KDGanttTreeView::~KDGanttTreeView()
78 View::Private::Private(View
*v
)
88 View::Private::~Private()
92 void View::Private::init()
94 KDGanttTreeView
*tw
= new KDGanttTreeView(&ganttProxyModel
, &splitter
);
95 tw
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
96 tw
->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel
);
99 q
->setRowController(tw
->rowController());
101 gfxview
.setAlignment(Qt::AlignTop
| Qt::AlignLeft
);
102 //gfxview.setRenderHints( QPainter::Antialiasing );
104 tw
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn
);
105 gfxview
.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn
);
107 QVBoxLayout
*layout
= new QVBoxLayout(q
);
108 layout
->setMargin(0);
109 layout
->addWidget(&splitter
);
110 q
->setLayout(layout
);
112 constraintProxy
.setProxyModel(&ganttProxyModel
);
113 constraintProxy
.setDestinationModel(&mappedConstraintModel
);
114 gfxview
.setSelectionModel(leftWidget
->selectionModel());
115 gfxview
.setConstraintModel(&mappedConstraintModel
);
118 void View::Private::updateScene()
120 gfxview
.clearItems();
125 if (QTreeView
*tw
= qobject_cast
<QTreeView
*>(leftWidget
)) {
126 QModelIndex idx
= ganttProxyModel
.mapFromSource(model
->index(0, 0, leftWidget
->rootIndex()));
128 gfxview
.updateRow(idx
);
129 } while ((idx
= tw
->indexBelow(idx
)) != QModelIndex() &&
130 gfxview
.rowController()->isRowVisible(idx
));
131 gfxview
.updateSceneRect();
133 const QModelIndex rootidx
= ganttProxyModel
.mapFromSource(leftWidget
->rootIndex());
134 for (int r
= 0; r
< ganttProxyModel
.rowCount(rootidx
); ++r
) {
135 gfxview
.updateRow(ganttProxyModel
.index(r
, 0, rootidx
));
140 void View::Private::slotCollapsed(const QModelIndex
&_idx
)
142 QTreeView
*tw
= qobject_cast
<QTreeView
*>(leftWidget
);
147 bool blocked
= gfxview
.blockSignals(true);
149 QModelIndex
idx(_idx
);
150 const QAbstractItemModel
*model
= leftWidget
->model();
151 const QModelIndex pidx
= ganttProxyModel
.mapFromSource(idx
);
152 bool isMulti
= false;
153 for (QModelIndex treewalkidx
= pidx
; treewalkidx
.isValid(); treewalkidx
= treewalkidx
.parent()) {
154 if (treewalkidx
.data(ItemTypeRole
).toInt() == TypeMulti
155 && !gfxview
.rowController()->isRowExpanded(treewalkidx
)) {
162 for (int i
= 0; i
< model
->rowCount(idx
); ++i
) {
163 gfxview
.deleteSubtree(ganttProxyModel
.index(i
, 0, pidx
));
166 gfxview
.updateRow(pidx
);
168 //qCDebug(KDGANTT_LOG) << "Looking to update from " << idx;
169 while ((idx
= tw
->indexBelow(idx
)) != QModelIndex() &&
170 gfxview
.rowController()->isRowVisible(ganttProxyModel
.mapFromSource(idx
))) {
171 const QModelIndex
proxyidx(ganttProxyModel
.mapFromSource(idx
));
172 gfxview
.updateRow(proxyidx
);
174 gfxview
.blockSignals(blocked
);
175 gfxview
.updateSceneRect();
178 void View::Private::slotExpanded(const QModelIndex
&_idx
)
180 QModelIndex
idx(ganttProxyModel
.mapFromSource(_idx
));
182 //qCDebug(KDGANTT_LOG) << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString();
183 gfxview
.updateRow(idx
);
184 } while ((idx
= gfxview
.rowController()->indexBelow(idx
)) != QModelIndex()
185 && gfxview
.rowController()->isRowVisible(idx
));
186 gfxview
.updateSceneRect();
189 void View::Private::slotVerticalScrollValueChanged(int val
)
192 qCDebug(KDGANTT_LOG
) << "View::Private::slotVerticalScrollValueChanged(" << val
<< ")="
193 << val
/ gfxview
.verticalScrollBar()->singleStep();
195 leftWidget
->verticalScrollBar()->setValue(val
/ gfxview
.verticalScrollBar()->singleStep());
198 void View::Private::slotLeftWidgetVerticalRangeChanged(int min
, int max
)
200 //qCDebug(KDGANTT_LOG) << "View::Private::slotLeftWidgetVerticalRangeChanged("<<min<<max<<")";
201 gfxview
.verticalScrollBar()->setRange(min
, max
);
202 gfxview
.updateSceneRect();
205 void View::Private::slotGfxViewVerticalRangeChanged(int min
, int max
)
207 //qCDebug(KDGANTT_LOG) << "View::Private::slotGfxViewVerticalRangeChanged("<<min<<max<<")";
208 int leftMin
= leftWidget
->verticalScrollBar()->minimum();
209 int leftMax
= leftWidget
->verticalScrollBar()->maximum();
210 bool blocked
= gfxview
.verticalScrollBar()->blockSignals(true);
211 gfxview
.verticalScrollBar()->setRange(qMax(min
, leftMin
), qMax(max
, leftMax
));
212 gfxview
.verticalScrollBar()->blockSignals(blocked
);
215 /*!\class KDGantt::View kdganttview.h KDGanttView
217 * \brief This widget that consists of a QTreeView and a GraphicsView
219 * This is the easy to use, complete gantt chart widget. It
220 * consists of a QTreeView on the left and a KDGantt::GraphicsView
221 * on the right separated by a QSplitter. The two views share the same
225 /*! Constructor. Creates a View with parent \a parent,
226 * a DateTimeGrid as default grid implementaion and no model etc.
228 View::View(QWidget
*parent
)
230 _d(new Private(this))
232 #if defined KDAB_EVAL
233 EvalDialog::checkEvalLicense("KD Gantt");
245 /*! Replaces the left widget with a custom QAbstractItemView.
247 * \param aiv The view to be used to the left, instead of the default tree view
248 * \sa setRowController()
250 void View::setLeftView(QAbstractItemView
*aiv
)
253 if (aiv
== d
->leftWidget
) {
256 if (!d
->leftWidget
.isNull()) {
257 d
->leftWidget
->disconnect(this);
258 d
->leftWidget
->hide();
259 d
->leftWidget
->verticalScrollBar()->disconnect(d
->gfxview
.verticalScrollBar());
260 d
->gfxview
.verticalScrollBar()->disconnect(d
->leftWidget
->verticalScrollBar());
264 d
->splitter
.insertWidget(0, d
->leftWidget
);
266 if (qobject_cast
<QTreeView
*>(d
->leftWidget
)) {
267 connect(d
->leftWidget
, SIGNAL(collapsed(QModelIndex
)),
268 this, SLOT(slotCollapsed(QModelIndex
)));
269 connect(d
->leftWidget
, SIGNAL(expanded(QModelIndex
)),
270 this, SLOT(slotExpanded(QModelIndex
)));
273 connect(d
->gfxview
.verticalScrollBar(), SIGNAL(valueChanged(int)),
274 d
->leftWidget
->verticalScrollBar(), SLOT(setValue(int)));
275 connect(d
->leftWidget
->verticalScrollBar(), SIGNAL(valueChanged(int)),
276 d
->gfxview
.verticalScrollBar(), SLOT(setValue(int)));
277 connect(d
->leftWidget
->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
278 this, SLOT(slotLeftWidgetVerticalRangeChanged(int,int)));
279 connect(d
->gfxview
.verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
280 this, SLOT(slotGfxViewVerticalRangeChanged(int,int)));
283 /*! Sets \a ctrl to be the rowcontroller used by this View.
284 * The default rowcontroller is owned by KDGantt::View and is
285 * suitable for the default treeview in the left part of the view.
286 * You probably only want to change this if you replace the treeview.
288 void View::setRowController(AbstractRowController
*ctrl
)
290 if (ctrl
== d
->rowController
) {
293 d
->rowController
= ctrl
;
294 d
->gfxview
.setRowController(d
->rowController
);
297 /*! \returns a pointer to the current rowcontroller.
298 * \see AbstractRowController
300 AbstractRowController
*View::rowController()
302 return d
->rowController
;
305 /*! \overload AbstractRowController* KDGantt::View::rowController()
307 const AbstractRowController
*View::rowController() const
309 return d
->rowController
;
313 * \returns a pointer to the QAbstractItemView in the left
314 * part of the widget.
316 const QAbstractItemView
*View::leftView() const
318 return d
->leftWidget
;
322 * \overload const QAbstractItemView* KDGantt::View::leftView() const
324 QAbstractItemView
*View::leftView()
326 return d
->leftWidget
;
330 * \returns a pointer to the GraphicsView
332 const GraphicsView
*View::graphicsView() const
338 * \overload const GraphicsView* KDGantt::View::graphicsView() const
340 GraphicsView
*View::graphicsView()
346 * \returns a pointer to the QSplitter that manages the left view and graphicsView
348 const QSplitter
*View::splitter() const
354 * \overload const QSplitter* KDGantt::View::splitter() const
356 QSplitter
*View::splitter()
361 /*! \returns the current model displayed by this view
363 QAbstractItemModel
*View::model() const
365 return leftView()->model();
368 /*! Sets the QAbstractItemModel to be displayed in this view
371 * \see GraphicsView::setModel
373 void View::setModel(QAbstractItemModel
*model
)
375 leftView()->setModel(model
);
376 d
->ganttProxyModel
.setSourceModel(model
);
377 d
->gfxview
.setModel(&d
->ganttProxyModel
);
380 /*! \returns the QItemSelectionModel used by this view
382 QItemSelectionModel
*View::selectionModel() const
384 return leftView()->selectionModel();
387 /*! Sets the QItemSelectionModel used by this view to manage
388 * selections. Similar to QAbstractItemView::setSelectionModel
390 void View::setSelectionModel(QItemSelectionModel
*smodel
)
392 leftView()->setSelectionModel(smodel
);
393 d
->gfxview
.setSelectionModel(new QItemSelectionModel(&(d
->ganttProxyModel
), this));
396 /*! Sets the AbstractGrid for this view. The grid is an
397 * object that controls how QModelIndexes are mapped
398 * to and from the view and how the background and header
399 * is rendered. \see AbstractGrid and DateTimeGrid.
401 void View::setGrid(AbstractGrid
*grid
)
403 d
->gfxview
.setGrid(grid
);
406 /*! \returns the AbstractGrid used by this view.
408 AbstractGrid
*View::grid() const
410 return d
->gfxview
.grid();
413 /*! \returns the rootindex for this view.
415 QModelIndex
View::rootIndex() const
417 return leftView()->rootIndex();
420 /*! Sets the root index of the model displayed by this view.
421 * Similar to QAbstractItemView::setRootIndex, default is QModelIndex().
423 void View::setRootIndex(const QModelIndex
&idx
)
425 leftView()->setRootIndex(idx
);
426 d
->gfxview
.setRootIndex(idx
);
429 /*! \returns the ItemDelegate used by this view to render items
431 ItemDelegate
*View::itemDelegate() const
433 return d
->gfxview
.itemDelegate();
436 /*! Sets the KDGantt::ItemDelegate used for rendering items on this
437 * view. \see ItemDelegate and QAbstractItemDelegate.
439 void View::setItemDelegate(ItemDelegate
*delegate
)
441 leftView()->setItemDelegate(delegate
);
442 d
->gfxview
.setItemDelegate(delegate
);
445 /*! Sets the constraintmodel displayed by this view.
446 * \see KDGantt::ConstraintModel.
448 void View::setConstraintModel(ConstraintModel
*cm
)
450 d
->constraintProxy
.setSourceModel(cm
);
451 d
->gfxview
.setConstraintModel(&d
->mappedConstraintModel
);
454 /*! \returns the KDGantt::ConstraintModel displayed by this view.
456 ConstraintModel
*View::constraintModel() const
458 return d
->constraintProxy
.sourceModel();
461 const QAbstractProxyModel
*View::ganttProxyModel() const
463 return &(d
->ganttProxyModel
);
466 QAbstractProxyModel
*View::ganttProxyModel()
468 return &(d
->ganttProxyModel
);
471 void View::resizeEvent(QResizeEvent
*ev
)
473 QWidget::resizeEvent(ev
);
476 /*!\returns The QModelIndex for the item located at
477 * position \a pos in the view or an invalid index
478 * if no item was present at that position.
480 * \see GraphicsView::indexAt
482 QModelIndex
View::indexAt(const QPoint
&pos
) const
484 return d
->gfxview
.indexAt(pos
);
487 /*! Print the Gantt chart using \a printer. If \a drawRowLabels
488 * is true (the default), each row will have it's label printed
491 * This version of print() will print multiple pages.
493 void View::print(QPrinter
*printer
, bool drawRowLabels
)
495 graphicsView()->print(printer
, drawRowLabels
);
498 /*! Print part of the Gantt chart from \a start to \a end using \a printer.
499 * If \a drawRowLabels is true (the default), each row will have it's
500 * label printed on the left side.
502 * This version of print() will print multiple pages.
504 * To print a certain range of a chart with a DateTimeGrid, use
505 * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
506 * to figure out the values for \a start and \a end.
508 void View::print(QPrinter
*printer
, qreal start
, qreal end
, bool drawRowLabels
)
510 graphicsView()->print(printer
, start
, end
, drawRowLabels
);
513 /*! Render the GanttView inside the rectangle \a target using the painter \a painter.
514 * If \a drawRowLabels is true (the default), each row will have it's
515 * label printed on the left side.
517 void View::print(QPainter
*painter
, const QRectF
&target
, bool drawRowLabels
)
519 d
->gfxview
.print(painter
,
524 /*! Render the GanttView inside the rectangle \a target using the painter \a painter.
525 * If \a drawRowLabels is true (the default), each row will have it's
526 * label printed on the left side.
528 * To print a certain range of a chart with a DateTimeGrid, use
529 * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
530 * to figure out the values for \a start and \a end.
532 void View::print(QPainter
*painter
, qreal start
, qreal end
, const QRectF
&target
, bool drawRowLabels
)
534 d
->gfxview
.print(painter
,
540 #include "moc_kdganttview.cpp"
542 #ifndef KDAB_NO_UNIT_TESTS
543 #include "unittest/test.h"
545 #include "kdganttlistviewrowcontroller.h"
546 #include <QApplication>
553 std::ostream
&operator<<(std::ostream
&os
, const QImage
&img
)
555 os
<< "QImage[ size=(" << img
.width() << ", " << img
.height() << ")]";
560 KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt
, View
, "test")
563 #if 0 // GUI tests do not work well on the server
564 QTimer::singleShot(1000, qApp
, SLOT(quit()));
568 QPixmap screenshot1
= QPixmap::grabWidget(&view
);
570 QTreeView
*tv
= new QTreeView
;
571 view
.setLeftView(tv
);
572 view
.setRowController(new TreeViewRowController(tv
, view
.ganttProxyModel()));
574 QTimer::singleShot(1000, qApp
, SLOT(quit()));
577 QPixmap screenshot2
= QPixmap::grabWidget(&view
);
579 assertEqual(screenshot1
.toImage(), screenshot2
.toImage());
581 QListView
*lv
= new QListView
;
582 view
.setLeftView(lv
);
583 view
.setRowController(new ListViewRowController(lv
, view
.ganttProxyModel()));
585 QTimer::singleShot(1000, qApp
, SLOT(quit()));
589 #endif /* KDAB_NO_UNIT_TESTS */