KD Chart API Documentation 3.1
Loading...
Searching...
No Matches
kdganttgraphicsview.cpp
Go to the documentation of this file.
1/****************************************************************************
2**
3** This file is part of the KD Chart library.
4**
5** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6**
7** SPDX-License-Identifier: MIT
8**
9****************************************************************************/
10
11#include "kdganttgraphicsview.h"
14#include "kdganttgraphicsitem.h"
15#include "kdganttgraphicsview_p.h"
16
17#include <QAbstractProxyModel>
18#include <QActionGroup>
19#include <QMenu>
20#include <QPaintEvent>
21#include <QPainter>
22#include <QPrinter>
23#include <QResizeEvent>
24#include <QScrollBar>
25
26#include <cassert>
27
28#if defined KDAB_EVAL
29#include "../evaldialog/evaldialog.h"
30#endif
31
36using namespace KDGantt;
37
38HeaderWidget::HeaderWidget(GraphicsView *parent)
39 : QWidget(parent)
40 , m_offset(0.)
41{
42 assert(parent); // Parent must be set
43}
44
45HeaderWidget::~HeaderWidget()
46{
47}
48
49void HeaderWidget::scrollTo(int v)
50{
51 m_offset = v;
52 // QWidget::scroll() won't work properly for me on Mac
53 // scroll( static_cast<int>( old-v ), 0 );
54 update();
55}
56
57void HeaderWidget::paintEvent(QPaintEvent *ev)
58{
59 QPainter p(this);
60 view()->grid()->paintHeader(&p, rect(), ev->rect(), m_offset, this);
61}
62
63bool HeaderWidget::event(QEvent *event)
64{
65 if (event->type() == QEvent::ToolTip) {
66 auto *const grid = qobject_cast<DateTimeGrid *>(view()->grid());
67 if (grid) {
68 auto *e = static_cast<QHelpEvent *>(event);
69 QDateTime dt = grid->mapFromChart(view()->mapToScene(e->x(), 0).x()).toDateTime();
70 setToolTip(dt.toString());
71 }
72 }
73 return QWidget::event(event);
74}
75
76void HeaderWidget::contextMenuEvent(QContextMenuEvent *event)
77{
78 QMenu contextMenu;
79
80 auto *const grid = qobject_cast<DateTimeGrid *>(view()->grid());
81 QAction *actionScaleAuto = nullptr;
82 QAction *actionScaleMonth = nullptr;
83 QAction *actionScaleWeek = nullptr;
84 QAction *actionScaleDay = nullptr;
85 QAction *actionScaleHour = nullptr;
86 QAction *actionZoomIn = nullptr;
87 QAction *actionZoomOut = nullptr;
88 if (grid != nullptr) {
89 auto *menuScale = new QMenu(tr("Scale"), &contextMenu);
90 auto *scaleGroup = new QActionGroup(&contextMenu);
91 scaleGroup->setExclusive(true);
92
93 actionScaleAuto = new QAction(tr("Auto"), menuScale);
94 actionScaleAuto->setCheckable(true);
95 actionScaleAuto->setChecked(grid->scale() == DateTimeGrid::ScaleAuto);
96 actionScaleMonth = new QAction(tr("Month"), menuScale);
97 actionScaleMonth->setCheckable(true);
98 actionScaleMonth->setChecked(grid->scale() == DateTimeGrid::ScaleMonth);
99 actionScaleWeek = new QAction(tr("Week"), menuScale);
100 actionScaleWeek->setCheckable(true);
101 actionScaleWeek->setChecked(grid->scale() == DateTimeGrid::ScaleWeek);
102 actionScaleDay = new QAction(tr("Day"), menuScale);
103 actionScaleDay->setCheckable(true);
104 actionScaleDay->setChecked(grid->scale() == DateTimeGrid::ScaleDay);
105 actionScaleHour = new QAction(tr("Hour"), menuScale);
106 actionScaleHour->setCheckable(true);
107 actionScaleHour->setChecked(grid->scale() == DateTimeGrid::ScaleHour);
108
109 scaleGroup->addAction(actionScaleAuto);
110 menuScale->addAction(actionScaleAuto);
111
112 scaleGroup->addAction(actionScaleMonth);
113 menuScale->addAction(actionScaleMonth);
114
115 scaleGroup->addAction(actionScaleWeek);
116 menuScale->addAction(actionScaleWeek);
117
118 scaleGroup->addAction(actionScaleDay);
119 menuScale->addAction(actionScaleDay);
120
121 scaleGroup->addAction(actionScaleHour);
122 menuScale->addAction(actionScaleHour);
123
124 contextMenu.addMenu(menuScale);
125
126 contextMenu.addSeparator();
127
128 actionZoomIn = new QAction(tr("Zoom In"), &contextMenu);
129 contextMenu.addAction(actionZoomIn);
130 actionZoomOut = new QAction(tr("Zoom Out"), &contextMenu);
131 contextMenu.addAction(actionZoomOut);
132 }
133
134 if (contextMenu.isEmpty()) {
135 event->ignore();
136 return;
137 }
138
139 const QAction *const action = contextMenu.exec(event->globalPos());
140 if (action == nullptr) {
141 } else if (action == actionScaleAuto) {
142 assert(grid != nullptr);
143 grid->setScale(DateTimeGrid::ScaleAuto);
144 } else if (action == actionScaleMonth) {
145 assert(grid != nullptr);
146 grid->setScale(DateTimeGrid::ScaleMonth);
147 } else if (action == actionScaleWeek) {
148 assert(grid != nullptr);
149 grid->setScale(DateTimeGrid::ScaleWeek);
150 } else if (action == actionScaleDay) {
151 assert(grid != nullptr);
152 grid->setScale(DateTimeGrid::ScaleDay);
153 } else if (action == actionScaleHour) {
154 assert(grid != nullptr);
155 grid->setScale(DateTimeGrid::ScaleHour);
156 } else if (action == actionZoomIn) {
157 assert(grid != nullptr);
158 grid->setDayWidth(qMax(0.1, grid->dayWidth() + grid->dayWidth() * 0.2));
159 } else if (action == actionZoomOut) {
160 assert(grid != nullptr);
161 grid->setDayWidth(qMax(0.1, grid->dayWidth() - grid->dayWidth() * 0.2));
162 }
163
164 event->accept();
165}
166
167GraphicsView::Private::Private(GraphicsView *_q)
168 : q(_q)
169 , rowcontroller(nullptr)
170 , headerwidget(_q)
171{
172}
173
174void GraphicsView::Private::updateHeaderGeometry()
175{
176 q->setViewportMargins(0, rowcontroller->headerHeight(), 0, 0);
177 headerwidget.setGeometry(q->viewport()->x(),
178 q->viewport()->y() - rowcontroller->headerHeight(),
179 q->viewport()->width(),
180 rowcontroller->headerHeight());
181}
182
183void GraphicsView::Private::slotGridChanged()
184{
185 updateHeaderGeometry();
186 headerwidget.update();
187 q->updateSceneRect();
188 q->update();
189}
190
191void GraphicsView::Private::slotHorizontalScrollValueChanged(int val)
192{
193 const QRectF viewRect = q->transform().mapRect(q->sceneRect());
194 headerwidget.scrollTo(val - q->horizontalScrollBar()->minimum() + static_cast<int>(viewRect.left()));
195}
196
197void GraphicsView::Private::slotColumnsInserted(const QModelIndex &parent, int start, int end)
198{
199 Q_UNUSED(start);
200 Q_UNUSED(end);
201 QModelIndex idx = scene.model()->index(0, 0, scene.summaryHandlingModel()->mapToSource(parent));
202 do {
203 scene.updateRow(scene.summaryHandlingModel()->mapFromSource(idx));
204 } while ((idx = rowcontroller->indexBelow(idx)) != QModelIndex() && rowcontroller->isRowVisible(idx));
205 //} while ( ( idx = d->treeview.indexBelow( idx ) ) != QModelIndex() && d->treeview.visualRect(idx).isValid() );
206 q->updateSceneRect();
207}
208
209void GraphicsView::Private::slotColumnsRemoved(const QModelIndex &parent, int start, int end)
210{
211 // TODO
212 Q_UNUSED(start);
213 Q_UNUSED(end);
214 Q_UNUSED(parent);
215 q->updateScene();
216}
217
218void GraphicsView::Private::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
219{
220 // qDebug() << "GraphicsView::slotDataChanged("<<topLeft<<bottomRight<<")";
221 const QModelIndex parent = topLeft.parent();
222 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
223 scene.updateRow(scene.summaryHandlingModel()->index(row, 0, parent));
224 }
225}
226
227void GraphicsView::Private::slotLayoutChanged()
228{
229 // qDebug() << "slotLayoutChanged()";
230 q->updateScene();
231}
232
233void GraphicsView::Private::slotModelReset()
234{
235 // qDebug() << "slotModelReset()";
236 q->updateScene();
237}
238
239void GraphicsView::Private::slotRowsInserted(const QModelIndex &parent, int start, int end)
240{
241 Q_UNUSED(parent);
242 Q_UNUSED(start);
243 Q_UNUSED(end);
244 q->updateScene(); // TODO: This might be optimised
245}
246
247void GraphicsView::Private::slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
248{
249 // qDebug() << "GraphicsView::Private::slotRowsAboutToBeRemoved("<<parent<<start<<end<<")";
250 for (int row = start; row <= end; ++row) {
251 for (int col = 0; col < scene.summaryHandlingModel()->columnCount(parent); ++col) {
252 // qDebug() << "removing "<<scene.summaryHandlingModel()->index( row, col, parent );
253 scene.removeItem(scene.summaryHandlingModel()->index(row, col, parent));
254 }
255 }
256}
257
258void GraphicsView::Private::slotRowsRemoved(const QModelIndex &parent, int start, int end)
259{
260 // qDebug() << "GraphicsView::Private::slotRowsRemoved("<<parent<<start<<end<<")";
261 // TODO
262 Q_UNUSED(parent);
263 Q_UNUSED(start);
264 Q_UNUSED(end);
265
266 q->updateScene();
267}
268
269void GraphicsView::Private::slotItemClicked(const QModelIndex &idx)
270{
271 QModelIndex sidx = idx; // scene.summaryHandlingModel()->mapToSource( idx );
272 Q_EMIT q->clicked(sidx);
273 if (q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
274 Q_EMIT q->activated(sidx);
275}
276
277void GraphicsView::Private::slotItemDoubleClicked(const QModelIndex &idx)
278{
279 QModelIndex sidx = idx; // scene.summaryHandlingModel()->mapToSource( idx );
280 Q_EMIT q->qrealClicked(sidx);
281 if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
282 Q_EMIT q->activated(sidx);
283}
284
285void GraphicsView::Private::slotHeaderContextMenuRequested(const QPoint &pt)
286{
287 Q_EMIT q->headerContextMenuRequested(headerwidget.mapToGlobal(pt));
288}
289
312 : QGraphicsView(parent)
313 , _d(new Private(this))
314{
315#if defined KDAB_EVAL
316 EvalDialog::checkEvalLicense("KD Gantt");
317#endif
319 this, [this](int value) {
320 _d->slotHorizontalScrollValueChanged(value);
321 });
323 this, [this] {
324 _d->slotGridChanged();
325 });
326 connect(&_d->scene, &GraphicsScene::entered,
327 this, &GraphicsView::entered);
328 connect(&_d->scene, &GraphicsScene::pressed,
329 this, &GraphicsView::pressed);
330 connect(&_d->scene, &GraphicsScene::clicked,
331 this, [this](const QModelIndex &index) {
332 _d->slotItemClicked(index);
333 });
335 this, [this](const QModelIndex &index) {
336 _d->slotItemDoubleClicked(index);
337 });
340 connect(&_d->headerwidget, &HeaderWidget::customContextMenuRequested,
341 this, [this](const QPoint &point) {
342 _d->slotHeaderContextMenuRequested(point);
343 });
344 setScene(&_d->scene);
345
346 // HACK!
347 setSummaryHandlingModel(_d->scene.summaryHandlingModel());
348
349 // So that AbstractGrid::drawBackground() and AbstractGrid::drawForeground()
350 // works properly
352
353 // setCacheMode( CacheBackground );
354}
355
358{
359 delete _d;
360}
361
362#define d d_func()
363
378{
379 if (d->scene.model()) {
380 disconnect(d->scene.model());
381 }
382
383 d->scene.setModel(model);
385 updateScene();
386}
387
391{
392 return d->scene.model();
393}
394
396{
397 disconnect(d->scene.summaryHandlingModel());
398 d->scene.setSummaryHandlingModel(proxyModel);
399
400 /* Connections. We have to rely on the treeview
401 * to receive the signals before we do(!)
402 */
404 this, [this](const QModelIndex &parent, int first, int last) {
405 d->slotColumnsInserted(parent, first, last);
406 });
408 this, [this](const QModelIndex &parent, int first, int last) {
409 d->slotColumnsRemoved(parent, first, last);
410 });
412 this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
413 d->slotDataChanged(topLeft, bottomRight);
414 });
416 this, [this] {
417 d->slotLayoutChanged();
418 });
420 this, [this] {
421 d->slotModelReset();
422 });
424 this, [this](const QModelIndex &parent, int first, int last) {
425 d->slotRowsInserted(parent, first, last);
426 });
428 this, [this](const QModelIndex &parent, int first, int last) {
429 d->slotRowsAboutToBeRemoved(parent, first, last);
430 });
432 this, [this](const QModelIndex &parent, int first, int last) {
433 d->slotRowsRemoved(parent, first, last);
434 });
435
436 updateScene();
437}
438
443{
444 d->scene.setConstraintModel(cmodel);
445}
446
450{
451 return d->scene.constraintModel();
452}
453
457{
458 return d->scene.summaryHandlingModel();
459}
460
465{
466 d->scene.setRootIndex(idx);
467}
468
472{
473 return d->scene.rootIndex();
474}
475
480{
481 d->scene.setSelectionModel(model);
482}
483
487{
488 return d->scene.selectionModel();
489}
490
495{
496 d->scene.setItemDelegate(delegate);
497}
498
502{
503 return d->scene.itemDelegate();
504}
505
512{
513 d->rowcontroller = rowcontroller;
514 d->scene.setRowController(rowcontroller);
515 updateScene();
516}
517
522{
523 return d->rowcontroller;
524}
525
532{
533 d->scene.setGrid(grid);
534 d->slotGridChanged();
535}
536
540{
541 return d->scene.grid();
542}
543
548{
549 d->scene.setReadOnly(ro);
550}
551
555{
556 return d->scene.isReadOnly();
557}
558
569{
570 d->headerwidget.setContextMenuPolicy(p);
571}
572
576{
577 return d->headerwidget.contextMenuPolicy();
578}
579
589 const QModelIndex &to,
590 Qt::KeyboardModifiers modifiers)
591{
592 if (isReadOnly())
593 return;
595 assert(cmodel);
597 if (cmodel->hasConstraint(c))
598 cmodel->removeConstraint(c);
599 else
600 cmodel->addConstraint(c);
601}
602
604{
605 d->updateHeaderGeometry();
607 // To scroll more to the left than the actual item start, bug #4516
608 r.setLeft(qMin<qreal>(0.0, r.left()));
609 // TODO: take scrollbars into account (if not always on)
610 // The scene should be at least the size of the viewport
611 QSizeF size = viewport()->size();
612 // TODO: why -2 below? size should be ex. frames etc?
613 if (size.width() > r.width()) {
614 r.setWidth(size.width() - 2);
615 }
616 if (size.height() > r.height()) {
617 r.setHeight(size.height() - 2);
618 }
619 const int totalh = rowController()->totalHeight();
620 if (r.height() < totalh) {
621 r.setHeight(totalh);
622 }
623
624 scene()->setSceneRect(r);
625
627}
628
636{
637 QGraphicsItem *item = itemAt(pos);
638 if (auto *gitem = qgraphicsitem_cast<GraphicsItem *>(item)) {
639 return d->scene.summaryHandlingModel()->mapToSource(gitem->index());
640 } else {
641 return QModelIndex();
642 }
643}
644
647{
648 d->scene.clearItems();
649}
650
653{
654 d->scene.updateRow(d->scene.summaryHandlingModel()->mapFromSource(idx));
655}
656
661{
662 /* What to do with this? We need to shrink the view to
663 * make collapsing items work
664 */
666 const qreal hscroll = horizontalScrollBar()->value() / (range > 0 ? range : 1);
667 QRectF r = d->scene.itemsBoundingRect();
668 // To scroll more to the left than the actual item start, bug #4516
669 r.setTop(0.);
670 r.setLeft(qMin<qreal>(0.0, r.left()));
671 r.setSize(r.size().expandedTo(viewport()->size()));
672 const int totalh = rowController()->totalHeight();
673 if (r.height() < totalh)
674 r.setHeight(totalh);
675 d->scene.setSceneRect(r);
676
677 /* set scrollbar to keep the same time in view */
679 if (range > 0)
680 horizontalScrollBar()->setValue(qRound(hscroll * range));
681
682 /* We have to update here to adjust for any rows with no
683 * information because they are painted with a different
684 * background brush
685 */
686 d->scene.invalidate(QRectF(), QGraphicsScene::BackgroundLayer);
687}
688
693{
694 clearItems();
695 if (!model())
696 return;
697 if (!rowController())
698 return;
699 QModelIndex idx = model()->index(0, 0, rootIndex());
700 do {
701 updateRow(idx);
702 } while ((idx = rowController()->indexBelow(idx)) != QModelIndex() && rowController()->isRowVisible(idx));
703 // constraintModel()->cleanup();
704 // qDebug() << constraintModel();
706 if (scene())
708}
709
711GraphicsItem *GraphicsView::createItem(ItemType type) const
712{
713 return d->scene.createItem(type);
714}
715
718{
719 d->scene.deleteSubtree(d->scene.summaryHandlingModel()->mapFromSource(idx));
720}
721
730void GraphicsView::print(QPrinter *printer, bool drawRowLabels, bool drawColumnLabels)
731{
732 d->scene.print(printer, drawRowLabels, drawColumnLabels);
733}
734
747void GraphicsView::print(QPrinter *printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels)
748{
749 d->scene.print(printer, start, end, drawRowLabels, drawColumnLabels);
750}
751
758void GraphicsView::print(QPainter *painter, const QRectF &targetRect, bool drawRowLabels, bool drawColumnLabels)
759{
760 d->scene.print(painter, targetRect, drawRowLabels, drawColumnLabels);
761}
762
773void GraphicsView::print(QPainter *painter, qreal start, qreal end,
774 const QRectF &targetRect, bool drawRowLabels, bool drawColumnLabels)
775{
776 d->scene.print(painter, start, end, targetRect, drawRowLabels, drawColumnLabels);
777}
778
779#include "moc_kdganttgraphicsview.cpp"
Abstract baseclass for grids.
Abstract baseclass for row controllers.
virtual int totalHeight() const =0
The ConstraintModel keeps track of the interdependencies between gantt items in a View.
A class used to represent a dependency.
GraphicsScene * scene() const
void entered(const QModelIndex &index)
void clicked(const QModelIndex &index)
void qrealClicked(const QModelIndex &index)
void pressed(const QModelIndex &index)
void deleteSubtree(const QModelIndex &)
GraphicsItem * createItem(ItemType type) const
Creates a new item of type type.
The GraphicsView class provides a model/view implementation of a gantt chart.
void setItemDelegate(ItemDelegate *delegate)
Sets the KDGantt::ItemDelegate used for rendering items on this view.
void setModel(QAbstractItemModel *)
Sets the model to be displayed in this view to model.
QAbstractItemModel * model() const
void entered(const QModelIndex &index)
void pressed(const QModelIndex &index)
void setRootIndex(const QModelIndex &)
Sets the root index of the model displayed by this view.
void setRowController(AbstractRowController *)
Sets the AbstractRowController used by this view.
~GraphicsView() override
Destroys this view.
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
Print the Gantt chart using printer.
void setSelectionModel(QItemSelectionModel *)
Sets the QItemSelectionModel used by this view to manage selections.
void setGrid(AbstractGrid *)
Sets the AbstractGrid for this view.
void resizeEvent(QResizeEvent *) override
void setHeaderContextMenuPolicy(Qt::ContextMenuPolicy)
Sets the context menu policy for the header.
QModelIndex rootIndex() const
ConstraintModel * constraintModel() const
void setConstraintModel(ConstraintModel *)
Sets the constraintmodel displayed by this view.
GraphicsView(QWidget *parent=nullptr)
Constructor.
virtual void addConstraint(const QModelIndex &from, const QModelIndex &to, Qt::KeyboardModifiers modifiers)
Adds a constraint from from to to.
QItemSelectionModel * selectionModel() const
AbstractGrid * grid() const
AbstractRowController * rowController() const
void setSummaryHandlingModel(QAbstractProxyModel *model)
Qt::ContextMenuPolicy headerContextMenuPolicy() const
void setReadOnly(bool)
Sets the view to read-only mode if to is true.
void updateRow(const QModelIndex &)
ItemDelegate * itemDelegate() const
QModelIndex indexAt(const QPoint &pos) const
QAbstractProxyModel * summaryHandlingModel() const
void deleteSubtree(const QModelIndex &)
Class used to render gantt items in a KDGantt::GraphicsView.
void columnsInserted(const QModelIndex &parent, int first, int last)
void columnsRemoved(const QModelIndex &parent, int first, int last)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
QScrollBar * horizontalScrollBar() const const
QWidget * viewport() const const
void valueChanged(int value)
void setCheckable(bool)
void setChecked(bool)
const QPoint & globalPos() const const
QString toString(Qt::DateFormat format) const const
QEvent::Type type() const const
void invalidate(qreal x, qreal y, qreal w, qreal h, QGraphicsScene::SceneLayers layers)
QRectF itemsBoundingRect() const const
void setSceneRect(const QRectF &rect)
void sceneRectChanged(const QRectF &rect)
QGraphicsItem * itemAt(const QPoint &pos) const const
virtual void resizeEvent(QResizeEvent *event) override
QGraphicsScene * scene() const const
void setScene(QGraphicsScene *scene)
void setViewportUpdateMode(QGraphicsView::ViewportUpdateMode mode)
QAction * addAction(const QString &text)
QAction * addMenu(QMenu *menu)
QAction * addSeparator()
QAction * exec()
bool isEmpty() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
int row() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QObject * parent() const const
T qobject_cast(QObject *object)
const QRect & rect() const const
qreal height() const const
qreal left() const const
void setHeight(qreal height)
void setLeft(qreal x)
void setSize(const QSizeF &size)
void setTop(qreal y)
void setWidth(qreal width)
QSizeF size() const const
qreal width() const const
QSizeF expandedTo(const QSizeF &otherSize) const const
SH_ItemView_ActivateItemOnSingleClick
ContextMenuPolicy
typedef KeyboardModifiers
virtual bool event(QEvent *event) override

© 2001 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-chart/
Generated on Thu Apr 11 2024 00:04:50 for KD Chart API Documentation by doxygen 1.9.8