KD Chart API Documentation 3.1
Loading...
Searching...
No Matches
kdganttgraphicsitem.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 "kdganttgraphicsitem.h"
12#include "kdganttabstractgrid.h"
14#include "kdganttconstraint.h"
18#include "kdganttgraphicsview.h"
19#include "kdganttitemdelegate.h"
20
21#include <algorithm>
22#include <cassert>
23#include <cmath>
24#include <iterator>
25
26#include <QAbstractItemModel>
27#include <QAbstractProxyModel>
28#include <QGraphicsLineItem>
29#include <QGraphicsSceneMouseEvent>
30#include <QItemSelectionModel>
31#include <QPainter>
32
33#include <QDebug>
34
39using namespace KDGantt;
40
42
43namespace {
44class Updater
45{
46 bool *u_ptr;
47 bool oldval;
48
49public:
50 Updater(bool *u)
51 : u_ptr(u)
52 , oldval(*u)
53 {
54 *u = true;
55 }
56 ~Updater()
57 {
58 *u_ptr = oldval;
59 }
60};
61}
63 : BASE(parent)
64{
65 if (scene)
66 scene->addItem(this);
67 init();
68}
69
71 GraphicsScene *scene)
72 : BASE(parent)
73 , m_index(idx)
74{
75 init();
76 if (scene)
77 scene->addItem(this);
78}
79
83
84void GraphicsItem::init()
85{
90 setZValue(100.);
91 m_dragline = nullptr;
92}
93
95{
96 return Type;
97}
98
99StyleOptionGanttItem GraphicsItem::getStyleOption() const
100{
102 opt.itemRect = rect();
104 QVariant tp = m_index.model()->data(m_index, TextPositionRole);
105 if (tp.isValid()) {
107 } else {
108#if 0
109 qDebug() << "Item" << m_index.model()->data( m_index, Qt::DisplayRole ).toString()
110 << ", ends="<<m_endConstraints.size() << ", starts="<<m_startConstraints.size();
111#endif
112 opt.displayPosition = m_endConstraints.size() < m_startConstraints.size() ? StyleOptionGanttItem::Left : StyleOptionGanttItem::Right;
113#if 0
114 qDebug() << "choosing" << opt.displayPosition;
115#endif
116 }
117 QVariant da = m_index.model()->data(m_index, Qt::TextAlignmentRole);
118 if (da.isValid()) {
119 opt.displayAlignment = static_cast<Qt::Alignment>(da.toInt());
120 } else {
121 switch (opt.displayPosition) {
123 opt.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter;
124 break;
126 opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
127 break;
128 case StyleOptionGanttItem::Hidden: // fall through
130 opt.displayAlignment = Qt::AlignCenter;
131 break;
132 }
133 }
134 opt.grid = scene()->grid();
135 opt.text = m_index.model()->data(m_index, Qt::DisplayRole).toString();
136 if (isEnabled())
137 opt.state |= QStyle::State_Enabled;
138 if (isSelected())
139 opt.state |= QStyle::State_Selected;
140 if (hasFocus())
141 opt.state |= QStyle::State_HasFocus;
142 return opt;
143}
144
146{
147 return qobject_cast<GraphicsScene *>(QGraphicsItem::scene());
148}
149
151{
152#if 0
153 qDebug() << "GraphicsItem::setRect("<<r<<"), txt="<<m_index.model()->data( m_index, Qt::DisplayRole ).toString();
154 if ( m_index.model()->data( m_index, Qt::DisplayRole ).toString() == QLatin1String("Code Freeze" ) ) {
155 qDebug() << "gotcha";
156 }
157#endif
158
160 m_rect = r;
161 updateConstraintItems();
162 update();
163}
164
166{
168 m_boundingrect = r;
169 update();
170}
171
173{
174 return !scene()->isReadOnly() && m_index.model()->flags(m_index) & Qt::ItemIsEditable;
175}
176
178 QWidget *widget)
179{
180 Q_UNUSED(widget);
181 if (boundingRect().isValid() && scene()) {
182 StyleOptionGanttItem opt = getStyleOption();
183 *static_cast<QStyleOption *>(&opt) = *static_cast<const QStyleOption *>(option);
184 // opt.fontMetrics = painter->fontMetrics();
185 scene()->itemDelegate()->paintGanttItem(painter, opt, index());
186 }
187}
188
190{
191 m_index = idx;
192 update();
193}
194
196{
197 return scene()->itemDelegate()->toolTip(index());
198}
199
201{
202 return m_boundingrect;
203}
204
205QPointF GraphicsItem::startConnector(int relationType) const
206{
207 switch (relationType) {
210 return mapToScene(m_rect.left(), m_rect.top() + m_rect.height() / 2.);
211 default:
212 break;
213 }
214 return mapToScene(m_rect.right(), m_rect.top() + m_rect.height() / 2.);
215}
216
217QPointF GraphicsItem::endConnector(int relationType) const
218{
219 switch (relationType) {
222 return mapToScene(m_rect.right(), m_rect.top() + m_rect.height() / 2.);
223 default:
224 break;
225 }
226 return mapToScene(m_rect.left(), m_rect.top() + m_rect.height() / 2.);
227}
228
229void GraphicsItem::constraintsChanged()
230{
231 if (!scene() || !scene()->itemDelegate())
232 return;
233 const Span bs = scene()->itemDelegate()->itemBoundingSpan(getStyleOption(), index());
234 const QRectF br = boundingRect();
235 setBoundingRect(QRectF(bs.start(), 0., bs.length(), br.height()));
236}
237
239{
240 assert(item);
241 m_startConstraints << item;
242 item->setStart(startConnector(item->constraint().relationType()));
243 constraintsChanged();
244}
245
247{
248 assert(item);
249 m_endConstraints << item;
250 item->setEnd(endConnector(item->constraint().relationType()));
251 constraintsChanged();
252}
253
255{
256 assert(item);
257 m_startConstraints.removeAll(item);
258 constraintsChanged();
259}
260
262{
263 assert(item);
264 m_endConstraints.removeAll(item);
265 constraintsChanged();
266}
267
268void GraphicsItem::updateConstraintItems()
269{
270 {
271 // Workaround for multiple definition error with MSVC6
272 for (ConstraintGraphicsItem *item : qAsConst(m_startConstraints)) {
273 QPointF s = startConnector(item->constraint().relationType());
274 item->setStart(s);
275 }
276 }
277 {
278 // Workaround for multiple definition error with MSVC6
279 for (ConstraintGraphicsItem *item : qAsConst(m_endConstraints)) {
280 QPointF e = endConnector(item->constraint().relationType());
281 item->setEnd(e);
282 }
283 }
284}
285
286void GraphicsItem::updateItem(const Span &rowGeometry, const QPersistentModelIndex &idx)
287{
288 // qDebug() << "GraphicsItem::updateItem("<<rowGeometry<<idx<<")";
289 Updater updater(&m_isupdating);
290 if (!idx.isValid() || idx.data(ItemTypeRole) == TypeMulti) {
291 setRect(QRectF());
292 hide();
293 return;
294 }
295
296 /* Use explicit type cast to avoid ambiguity */
297 const Span s = scene()->grid()->mapToChart(static_cast<const QModelIndex &>(idx));
298 setPos(QPointF(s.start(), rowGeometry.start()));
299 setRect(QRectF(0., 0., s.length(), rowGeometry.length()));
300 setIndex(idx);
301 const Span bs = scene()->itemDelegate()->itemBoundingSpan(getStyleOption(), index());
302 // qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs;
303 setBoundingRect(QRectF(bs.start(), 0., bs.length(), rowGeometry.length()));
304 const int maxh = scene()->rowController()->maximumItemHeight();
305 if (maxh < rowGeometry.length()) {
306 QRectF r = rect();
307 const Qt::Alignment align = getStyleOption().displayAlignment;
308 if (align & Qt::AlignTop) {
309 // Do nothing
310 } else if (align & Qt::AlignBottom) {
311 r.setY(rowGeometry.length() - maxh);
312 } else {
313 // Center
314 r.setY((rowGeometry.length() - maxh) / 2.);
315 }
316 r.setHeight(maxh);
317 setRect(r);
318 }
319
320 // scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) );
321 // updateConstraintItems();
322}
323
325{
326 if (!isUpdating() && change == ItemPositionChange && scene()) {
327 QPointF newPos = value.toPointF();
328 if (isEditable()) {
329 newPos.setY(pos().y());
330 return newPos;
331 } else {
332 return pos();
333 }
334 } else if (change == QGraphicsItem::ItemSelectedChange) {
335 if (index().isValid() && !(index().model()->flags(index()) & Qt::ItemIsSelectable)) {
336 // Reject selection attempt
337 return QVariant::fromValue(false);
338 }
339
340 if (value.toBool()) {
342 } else {
344 }
345 }
346
347 return QGraphicsItem::itemChange(change, value);
348}
349
355
356void GraphicsItem::updateModel()
357{
358 // qDebug() << "GraphicsItem::updateModel()";
359 if (isEditable()) {
360 auto *model = const_cast<QAbstractItemModel *>(index().model());
361#if !defined(NDEBUG)
363#endif
364 assert(model);
365 assert(cmodel);
366 if (model) {
367 // ItemType typ = static_cast<ItemType>( model->data( index(),
368 // ItemTypeRole ).toInt() );
369 QList<Constraint> constraints;
370 for (QList<ConstraintGraphicsItem *>::iterator it1 = m_startConstraints.begin();
371 it1 != m_startConstraints.end();
372 ++it1)
373 constraints.push_back((*it1)->proxyConstraint());
374 for (QList<ConstraintGraphicsItem *>::iterator it2 = m_endConstraints.begin();
375 it2 != m_endConstraints.end();
376 ++it2)
377 constraints.push_back((*it2)->proxyConstraint());
378 if (scene()->grid()->mapFromChart(Span(scenePos().x(), rect().width()),
379 index(),
380 constraints)) {
381 scene()->updateRow(index().parent());
382 }
383 }
384 }
385}
386
388{
389 if (!isEditable())
390 return;
391 StyleOptionGanttItem opt = getStyleOption();
393 switch (istate) {
395#ifndef QT_NO_CURSOR
397#endif
398 scene()->itemEntered(index());
399 break;
401#ifndef QT_NO_CURSOR
403#endif
404 scene()->itemEntered(index());
405 break;
407#ifndef QT_NO_CURSOR
409#endif
410 scene()->itemEntered(index());
411 break;
412 default:
413#ifndef QT_NO_CURSOR
414 unsetCursor();
415#endif
416 break;
417 };
418}
419
421{
422#ifndef QT_NO_CURSOR
423 unsetCursor();
424#endif
425}
426
428{
429 // qDebug() << "GraphicsItem::mousePressEvent("<<event<<")";
430 StyleOptionGanttItem opt = getStyleOption();
431 int istate = scene()->itemDelegate()->interactionStateFor(event->pos(), opt, index());
432 // If State_None is returned by interactionStateFor(), we ignore this event so that
433 // it can get forwarded to another item that's below this one. Needed, for example,
434 // to allow items to be moved that are placed below the label of another item.
435 if (istate != ItemDelegate::State_None) {
436 m_istate = istate;
437 m_presspos = event->pos();
438 m_pressscenepos = event->scenePos();
439 scene()->itemPressed(index());
440
441 switch (m_istate) {
444 default: /* State_Move */
445 BASE::mousePressEvent(event);
446 break;
447 }
448 } else {
449 event->ignore();
450 }
451}
452
454{
455 // qDebug() << "GraphicsItem::mouseReleaseEvent("<<event << ")";
456 if (!m_presspos.isNull()) {
457 scene()->itemClicked(index());
458 }
459 delete m_dragline;
460 m_dragline = nullptr;
461 if (scene()->dragSource()) {
462 // Create a new constraint
463 auto *other = qgraphicsitem_cast<GraphicsItem *>(scene()->itemAt(event->scenePos(), QTransform()));
464 if (other && scene()->dragSource() != other && other->index().data(KDGantt::ItemTypeRole) == KDGantt::TypeEvent) {
465 // The code below fixes bug KDCH-696.
466 // Modified the code to add constraint even if the user drags and drops
467 // constraint on left part of the TypeEvent symbol(i.e diamond symbol)
468 QRectF itemRect = other->rect().adjusted(-other->rect().height() / 2.0, 0, 0, 0);
469 if (other->mapToScene(itemRect).boundingRect().contains(event->scenePos())) {
470 auto *view = qobject_cast<GraphicsView *>(event->widget()->parentWidget());
471 if (view) {
472 view->addConstraint(scene()->summaryHandlingModel()->mapToSource(scene()->dragSource()->index()),
473 scene()->summaryHandlingModel()->mapToSource(other->index()), event->modifiers());
474 }
475 }
476 } else {
477 if (other && scene()->dragSource() != other && other->mapToScene(other->rect()).boundingRect().contains(event->scenePos())) {
478 auto *view = qobject_cast<GraphicsView *>(event->widget()->parentWidget());
479 if (view) {
480 view->addConstraint(scene()->summaryHandlingModel()->mapToSource(scene()->dragSource()->index()),
481 scene()->summaryHandlingModel()->mapToSource(other->index()), event->modifiers());
482 }
483 }
484 }
485
486 scene()->setDragSource(nullptr);
487 // scene()->update();
488 } else {
489 if (isEditable()) {
490 updateItemFromMouse(event->scenePos());
491
492 // It is important to set m_presspos to null here because
493 // when the sceneRect updates because we move the item
494 // a MouseMoveEvent will be delivered, and we have to
495 // protect against that
496 m_presspos = QPointF();
497 updateModel();
498 // without this command we sometimes get a white area at the left side of a task item
499 // after we moved that item right-ways into a grey weekend section of the scene:
500 scene()->update();
501 }
502 }
503
504 m_presspos = QPointF();
505 BASE::mouseReleaseEvent(event);
506}
507
509{
510 const int typ = static_cast<ItemType>(index().model()->data(index(), ItemTypeRole).toInt());
511 StyleOptionGanttItem opt = getStyleOption();
513 if ((istate != ItemDelegate::State_None) || (typ == TypeSummary)) {
515 }
516 BASE::mouseDoubleClickEvent(event);
517}
518
519void GraphicsItem::updateItemFromMouse(const QPointF &scenepos)
520{
521 // qDebug() << "GraphicsItem::updateItemFromMouse("<<scenepos<<")";
522 const QPointF p = scenepos - m_presspos;
523 QRectF r = rect();
524 QRectF br = boundingRect();
525 switch (m_istate) {
527 setPos(p.x(), pos().y());
528 break;
530 const qreal brr = br.right();
531 const qreal rr = r.right();
532 const qreal delta = pos().x() - p.x();
533 setPos(p.x(), QGraphicsItem::pos().y());
534 br.setRight(brr + delta);
535 r.setRight(rr + delta);
536 break;
537 }
539 const qreal rr = r.right();
540 r.setRight(scenepos.x() - pos().x());
541 br.setWidth(br.width() + r.right() - rr);
542 break;
543 }
544 default:
545 return;
546 }
547 setRect(r);
548 setBoundingRect(br);
549}
550
552{
553 if (!isEditable())
554 return;
555 if (m_presspos.isNull())
556 return;
557
558 // qDebug() << "GraphicsItem::mouseMoveEvent("<<event<<"), m_istate="<< static_cast<ItemDelegate::InteractionState>( m_istate );
559 switch (m_istate) {
563 // Check for constraint drag
564 if (qAbs(m_pressscenepos.x() - event->scenePos().x()) < 10.
565 && qAbs(m_pressscenepos.y() - event->scenePos().y()) > 5.) {
567 m_dragline = new QGraphicsLineItem(this);
568 m_dragline->setPen(QPen(Qt::DashLine));
569 m_dragline->setLine(QLineF(rect().center(), event->pos()));
570 scene()->setDragSource(this);
571 break;
572 }
573
575 updateItemFromMouse(event->scenePos());
576 // BASE::mouseMoveEvent(event);
577 break;
579 QLineF line = m_dragline->line();
580 m_dragline->setLine(QLineF(line.p1(), event->pos()));
581 // QGraphicsItem* item = scene()->itemAt( event->scenePos() );
582 break;
583 }
584 }
585}
virtual Span mapToChart(const QModelIndex &idx) const =0
Implement this to map from the data in the model to the location of the corresponding item in the vie...
virtual bool mapFromChart(const Span &span, const QModelIndex &idx, const QList< Constraint > &constraints=QList< Constraint >()) const =0
Implement this to update the model data based on the location of the item.
virtual int maximumItemHeight() const =0
The ConstraintModel keeps track of the interdependencies between gantt items in a View.
RelationType relationType() const
This is unused for now.
int type() const override
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget=nullptr) override
QRectF boundingRect() const override
void removeStartConstraint(ConstraintGraphicsItem *)
GraphicsItem(QGraphicsItem *parent=nullptr, GraphicsScene *scene=nullptr)
void addEndConstraint(ConstraintGraphicsItem *)
void mousePressEvent(QGraphicsSceneMouseEvent *) override
void updateItem(const Span &rowgeometry, const QPersistentModelIndex &idx)
void hoverMoveEvent(QGraphicsSceneHoverEvent *) override
void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override
virtual QString ganttToolTip() const
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override
void removeEndConstraint(ConstraintGraphicsItem *)
void mouseMoveEvent(QGraphicsSceneMouseEvent *) override
void focusInEvent(QFocusEvent *event) override
void setIndex(const QPersistentModelIndex &idx)
const QPersistentModelIndex & index() const
GraphicsScene * scene() const
void setRect(const QRectF &r)
void addStartConstraint(ConstraintGraphicsItem *)
void setBoundingRect(const QRectF &r)
void hoverLeaveEvent(QGraphicsSceneHoverEvent *) override
QVariant itemChange(GraphicsItemChange, const QVariant &value) override
void itemDoubleClicked(const QModelIndex &)
AbstractRowController * rowController() const
ItemDelegate * itemDelegate() const
void itemClicked(const QModelIndex &)
ConstraintModel * constraintModel() const
void itemEntered(const QModelIndex &)
void updateRow(const QModelIndex &idx)
void itemPressed(const QModelIndex &)
AbstractGrid * grid() const
QItemSelectionModel * selectionModel() const
void setDragSource(GraphicsItem *item)
virtual QString toolTip(const QModelIndex &idx) const
virtual Span itemBoundingSpan(const StyleOptionGanttItem &opt, const QModelIndex &idx) const
virtual InteractionState interactionStateFor(const QPointF &pos, const StyleOptionGanttItem &opt, const QModelIndex &idx) const
InteractionState
This enum is used for communication between the view and the delegate about user interaction with gan...
virtual void paintGanttItem(QPainter *p, const StyleOptionGanttItem &opt, const QModelIndex &idx)
Paints the gantt item idx using painter and opt.
A class representing a start point and a length.
qreal length() const
qreal start() const
QStyleOption subclass for gantt items.
Position
This enum is used to describe where the Qt::DisplayRole (the label) should be located relative to the...
QRectF boundingRect
Contains the bounding rectangle for the item.
AbstractGrid * grid
Contains a pointer to the AbstractGrid used by the view.
QString text
Contains a string printed to the item.
QRectF itemRect
Contains the "active" item rectangle that corresponds to the values from the model.
QGraphicsItem BASE
virtual QVariant data(const QModelIndex &index, int role) const const=0
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
void setHandlesChildEvents(bool enabled)
QGraphicsItem::GraphicsItemFlags flags() const const
bool hasFocus() const const
bool isEnabled() const const
bool isSelected() const const
virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
QPointF mapToScene(const QPointF &point) const const
QPointF pos() const const
void prepareGeometryChange()
QGraphicsScene * scene() const const
QPointF scenePos() const const
void setAcceptHoverEvents(bool enabled)
void setCacheMode(QGraphicsItem::CacheMode mode, const QSize &logicalCacheSize)
void setCursor(const QCursor &cursor)
void setFlags(QGraphicsItem::GraphicsItemFlags flags)
void setPos(const QPointF &pos)
void setZValue(qreal z)
void unsetCursor()
void update(const QRectF &rect)
qreal x() const const
qreal y() const const
QLineF line() const const
void setLine(const QLineF &line)
void setPen(const QPen &pen)
void addItem(QGraphicsItem *item)
void update(qreal x, qreal y, qreal w, qreal h)
QWidget * widget() const const
QPointF pos() const const
Qt::KeyboardModifiers modifiers() const const
QPointF pos() const const
QPointF scenePos() const const
virtual void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
virtual void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
QPointF p1() const const
void push_back(const T &value)
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
bool isNull() const const
void setY(qreal y)
qreal x() const const
qreal y() const const
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
qreal height() const const
qreal left() const const
qreal right() const const
void setHeight(qreal height)
void setRight(qreal x)
void setWidth(qreal width)
void setY(qreal y)
qreal top() const const
qreal width() const const
typedef Alignment
SizeHorCursor
DisplayRole
ItemIsEditable
DashLine
QVariant fromValue(const T &value)
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const
QPointF toPointF() const const
QString toString() const const
QWidget * parentWidget() const const

© 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 Mon Jul 8 2024 00:00:52 for KD Chart API Documentation by doxygen 1.9.8