Parent Content pointer are no longer needed for KMime headers.
[kdepim.git] / kdgantt2 / kdganttview.cpp
blobddbfd1746582a3ebf19bb9c61c0de67e7e603b9f
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 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
13 ** the Software.
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>
36 #include <QScrollBar>
37 #include <QPaintEvent>
39 #include "kdgantt_debug.h"
41 #include <cassert>
43 #if defined KDAB_EVAL
44 #include "../evaldialog/evaldialog.h"
45 #endif
47 using namespace KDGantt;
49 namespace
51 class HeaderView : public QHeaderView
53 public:
54 explicit HeaderView(QWidget *parent = Q_NULLPTR) : QHeaderView(Qt::Horizontal, parent)
58 QSize sizeHint() const Q_DECL_OVERRIDE
60 QSize s = QHeaderView::sizeHint();
61 s.rheight() *= 2;
62 return s;
67 KDGanttTreeView::KDGanttTreeView(QAbstractProxyModel *proxy, QWidget *parent)
68 : QTreeView(parent),
69 m_controller(this, proxy)
71 setHeader(new HeaderView);
74 KDGanttTreeView::~KDGanttTreeView()
78 View::Private::Private(View *v)
79 : q(v),
80 splitter(v),
81 rowController(0),
82 gfxview(&splitter),
83 model(0)
85 //init();
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);
98 q->setLeftView(tw);
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();
121 if (!model) {
122 return;
125 if (QTreeView *tw = qobject_cast<QTreeView *>(leftWidget)) {
126 QModelIndex idx = ganttProxyModel.mapFromSource(model->index(0, 0, leftWidget->rootIndex()));
127 do {
128 gfxview.updateRow(idx);
129 } while ((idx = tw->indexBelow(idx)) != QModelIndex() &&
130 gfxview.rowController()->isRowVisible(idx));
131 gfxview.updateSceneRect();
132 } else {
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);
143 if (!tw) {
144 return;
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)) {
156 isMulti = true;
157 break;
161 if (!isMulti) {
162 for (int i = 0; i < model->rowCount(idx); ++i) {
163 gfxview.deleteSubtree(ganttProxyModel.index(i, 0, pidx));
165 } else {
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));
181 do {
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)
191 #if 0
192 qCDebug(KDGANTT_LOG) << "View::Private::slotVerticalScrollValueChanged(" << val << ")="
193 << val / gfxview.verticalScrollBar()->singleStep();
194 #endif
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
216 * \ingroup KDGantt
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
222 * model.
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)
229 : QWidget(parent),
230 _d(new Private(this))
232 #if defined KDAB_EVAL
233 EvalDialog::checkEvalLicense("KD Gantt");
234 #endif
235 _d->init();
238 View::~View()
240 delete _d;
243 #define d d_func()
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)
252 assert(aiv);
253 if (aiv == d->leftWidget) {
254 return;
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());
263 d->leftWidget = aiv;
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) {
291 return;
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.
315 * */
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
334 return &d->gfxview;
338 * \overload const GraphicsView* KDGantt::View::graphicsView() const
340 GraphicsView *View::graphicsView()
342 return &d->gfxview;
346 * \returns a pointer to the QSplitter that manages the left view and graphicsView
348 const QSplitter *View::splitter() const
350 return &d->splitter;
354 * \overload const QSplitter* KDGantt::View::splitter() const
356 QSplitter *View::splitter()
358 return &d->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
369 * to \a model.
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
489 * on the left side.
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,
520 target,
521 drawRowLabels);
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,
535 start, end,
536 target,
537 drawRowLabels);
540 #include "moc_kdganttview.cpp"
542 #ifndef KDAB_NO_UNIT_TESTS
543 #include "unittest/test.h"
545 #include "kdganttlistviewrowcontroller.h"
546 #include <QApplication>
547 #include <QTimer>
548 #include <QPixmap>
549 #include <QListView>
551 namespace
553 std::ostream &operator<<(std::ostream &os, const QImage &img)
555 os << "QImage[ size=(" << img.width() << ", " << img.height() << ")]";
556 return os;
560 KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt, View, "test")
562 View view(0);
563 #if 0 // GUI tests do not work well on the server
564 QTimer::singleShot(1000, qApp, SLOT(quit()));
565 view.show();
567 qApp->exec();
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()));
576 qApp->exec();
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()));
584 view.show();
585 QTimer::singleShot(1000, qApp, SLOT(quit()));
586 qApp->exec();
587 #endif
589 #endif /* KDAB_NO_UNIT_TESTS */