KD Chart API Documentation 3.0
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 { // Workaround for multiple definition error with MSVC6
271 Q_FOREACH (ConstraintGraphicsItem *item, m_startConstraints) {
272 QPointF s = startConnector(item->constraint().relationType());
273 item->setStart(s);
274 }
275 }
276 { // Workaround for multiple definition error with MSVC6
277 Q_FOREACH (ConstraintGraphicsItem *item, m_endConstraints) {
278 QPointF e = endConnector(item->constraint().relationType());
279 item->setEnd(e);
280 }
281 }
282}
283
284void GraphicsItem::updateItem(const Span &rowGeometry, const QPersistentModelIndex &idx)
285{
286 // qDebug() << "GraphicsItem::updateItem("<<rowGeometry<<idx<<")";
287 Updater updater(&m_isupdating);
288 if (!idx.isValid() || idx.data(ItemTypeRole) == TypeMulti) {
289 setRect(QRectF());
290 hide();
291 return;
292 }
293
294 /* Use explicit type cast to avoid ambiguity */
295 const Span s = scene()->grid()->mapToChart(static_cast<const QModelIndex &>(idx));
296 setPos(QPointF(s.start(), rowGeometry.start()));
297 setRect(QRectF(0., 0., s.length(), rowGeometry.length()));
298 setIndex(idx);
299 const Span bs = scene()->itemDelegate()->itemBoundingSpan(getStyleOption(), index());
300 // qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs;
301 setBoundingRect(QRectF(bs.start(), 0., bs.length(), rowGeometry.length()));
302 const int maxh = scene()->rowController()->maximumItemHeight();
303 if (maxh < rowGeometry.length()) {
304 QRectF r = rect();
305 const Qt::Alignment align = getStyleOption().displayAlignment;
306 if (align & Qt::AlignTop) {
307 // Do nothing
308 } else if (align & Qt::AlignBottom) {
309 r.setY(rowGeometry.length() - maxh);
310 } else {
311 // Center
312 r.setY((rowGeometry.length() - maxh) / 2.);
313 }
314 r.setHeight(maxh);
315 setRect(r);
316 }
317
318 // scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) );
319 // updateConstraintItems();
320}
321
323{
324 if (!isUpdating() && change == ItemPositionChange && scene()) {
325 QPointF newPos = value.toPointF();
326 if (isEditable()) {
327 newPos.setY(pos().y());
328 return newPos;
329 } else {
330 return pos();
331 }
332 } else if (change == QGraphicsItem::ItemSelectedChange) {
333 if (index().isValid() && !(index().model()->flags(index()) & Qt::ItemIsSelectable)) {
334 // Reject selection attempt
335 return QVariant::fromValue(false);
336 }
337
338 if (value.toBool()) {
340 } else {
342 }
343 }
344
345 return QGraphicsItem::itemChange(change, value);
346}
347
353
354void GraphicsItem::updateModel()
355{
356 // qDebug() << "GraphicsItem::updateModel()";
357 if (isEditable()) {
358 auto *model = const_cast<QAbstractItemModel *>(index().model());
359#if !defined(NDEBUG)
361#endif
362 assert(model);
363 assert(cmodel);
364 if (model) {
365 // ItemType typ = static_cast<ItemType>( model->data( index(),
366 // ItemTypeRole ).toInt() );
367 QList<Constraint> constraints;
368 for (QList<ConstraintGraphicsItem *>::iterator it1 = m_startConstraints.begin();
369 it1 != m_startConstraints.end();
370 ++it1)
371 constraints.push_back((*it1)->proxyConstraint());
372 for (QList<ConstraintGraphicsItem *>::iterator it2 = m_endConstraints.begin();
373 it2 != m_endConstraints.end();
374 ++it2)
375 constraints.push_back((*it2)->proxyConstraint());
376 if (scene()->grid()->mapFromChart(Span(scenePos().x(), rect().width()),
377 index(),
378 constraints)) {
379 scene()->updateRow(index().parent());
380 }
381 }
382 }
383}
384
386{
387 if (!isEditable())
388 return;
389 StyleOptionGanttItem opt = getStyleOption();
391 switch (istate) {
393#ifndef QT_NO_CURSOR
395#endif
396 scene()->itemEntered(index());
397 break;
399#ifndef QT_NO_CURSOR
401#endif
402 scene()->itemEntered(index());
403 break;
405#ifndef QT_NO_CURSOR
407#endif
408 scene()->itemEntered(index());
409 break;
410 default:
411#ifndef QT_NO_CURSOR
412 unsetCursor();
413#endif
414 break;
415 };
416}
417
419{
420#ifndef QT_NO_CURSOR
421 unsetCursor();
422#endif
423}
424
426{
427 // qDebug() << "GraphicsItem::mousePressEvent("<<event<<")";
428 StyleOptionGanttItem opt = getStyleOption();
429 int istate = scene()->itemDelegate()->interactionStateFor(event->pos(), opt, index());
430 // If State_None is returned by interactionStateFor(), we ignore this event so that
431 // it can get forwarded to another item that's below this one. Needed, for example,
432 // to allow items to be moved that are placed below the label of another item.
433 if (istate != ItemDelegate::State_None) {
434 m_istate = istate;
435 m_presspos = event->pos();
436 m_pressscenepos = event->scenePos();
437 scene()->itemPressed(index());
438
439 switch (m_istate) {
442 default: /* State_Move */
443 BASE::mousePressEvent(event);
444 break;
445 }
446 } else {
447 event->ignore();
448 }
449}
450
452{
453 // qDebug() << "GraphicsItem::mouseReleaseEvent("<<event << ")";
454 if (!m_presspos.isNull()) {
455 scene()->itemClicked(index());
456 }
457 delete m_dragline;
458 m_dragline = nullptr;
459 if (scene()->dragSource()) {
460 // Create a new constraint
461 auto *other = qgraphicsitem_cast<GraphicsItem *>(scene()->itemAt(event->scenePos(), QTransform()));
462 if (other && scene()->dragSource() != other && other->index().data(KDGantt::ItemTypeRole) == KDGantt::TypeEvent) {
463 // The code below fixes bug KDCH-696.
464 // Modified the code to add constraint even if the user drags and drops
465 // constraint on left part of the TypeEvent symbol(i.e diamond symbol)
466 QRectF itemRect = other->rect().adjusted(-other->rect().height() / 2.0, 0, 0, 0);
467 if (other->mapToScene(itemRect).boundingRect().contains(event->scenePos())) {
468 auto *view = qobject_cast<GraphicsView *>(event->widget()->parentWidget());
469 if (view) {
470 view->addConstraint(scene()->summaryHandlingModel()->mapToSource(scene()->dragSource()->index()),
471 scene()->summaryHandlingModel()->mapToSource(other->index()), event->modifiers());
472 }
473 }
474 } else {
475 if (other && scene()->dragSource() != other && other->mapToScene(other->rect()).boundingRect().contains(event->scenePos())) {
476 auto *view = qobject_cast<GraphicsView *>(event->widget()->parentWidget());
477 if (view) {
478 view->addConstraint(scene()->summaryHandlingModel()->mapToSource(scene()->dragSource()->index()),
479 scene()->summaryHandlingModel()->mapToSource(other->index()), event->modifiers());
480 }
481 }
482 }
483
484 scene()->setDragSource(nullptr);
485 // scene()->update();
486 } else {
487 if (isEditable()) {
488 updateItemFromMouse(event->scenePos());
489
490 // It is important to set m_presspos to null here because
491 // when the sceneRect updates because we move the item
492 // a MouseMoveEvent will be delivered, and we have to
493 // protect against that
494 m_presspos = QPointF();
495 updateModel();
496 // without this command we sometimes get a white area at the left side of a task item
497 // after we moved that item right-ways into a grey weekend section of the scene:
498 scene()->update();
499 }
500 }
501
502 m_presspos = QPointF();
503 BASE::mouseReleaseEvent(event);
504}
505
507{
508 const int typ = static_cast<ItemType>(index().model()->data(index(), ItemTypeRole).toInt());
509 StyleOptionGanttItem opt = getStyleOption();
511 if ((istate != ItemDelegate::State_None) || (typ == TypeSummary)) {
513 }
514 BASE::mouseDoubleClickEvent(event);
515}
516
517void GraphicsItem::updateItemFromMouse(const QPointF &scenepos)
518{
519 // qDebug() << "GraphicsItem::updateItemFromMouse("<<scenepos<<")";
520 const QPointF p = scenepos - m_presspos;
521 QRectF r = rect();
522 QRectF br = boundingRect();
523 switch (m_istate) {
525 setPos(p.x(), pos().y());
526 break;
528 const qreal brr = br.right();
529 const qreal rr = r.right();
530 const qreal delta = pos().x() - p.x();
531 setPos(p.x(), QGraphicsItem::pos().y());
532 br.setRight(brr + delta);
533 r.setRight(rr + delta);
534 break;
535 }
537 const qreal rr = r.right();
538 r.setRight(scenepos.x() - pos().x());
539 br.setWidth(br.width() + r.right() - rr);
540 break;
541 }
542 default:
543 return;
544 }
545 setRect(r);
546 setBoundingRect(br);
547}
548
550{
551 if (!isEditable())
552 return;
553 if (m_presspos.isNull())
554 return;
555
556 // qDebug() << "GraphicsItem::mouseMoveEvent("<<event<<"), m_istate="<< static_cast<ItemDelegate::InteractionState>( m_istate );
557 switch (m_istate) {
561 // Check for constraint drag
562 if (qAbs(m_pressscenepos.x() - event->scenePos().x()) < 10.
563 && qAbs(m_pressscenepos.y() - event->scenePos().y()) > 5.) {
565 m_dragline = new QGraphicsLineItem(this);
566 m_dragline->setPen(QPen(Qt::DashLine));
567 m_dragline->setLine(QLineF(rect().center(), event->pos()));
568 scene()->setDragSource(this);
569 break;
570 }
571
573 updateItemFromMouse(event->scenePos());
574 // BASE::mouseMoveEvent(event);
575 break;
577 QLineF line = m_dragline->line();
578 m_dragline->setLine(QLineF(line.p1(), event->pos()));
579 // QGraphicsItem* item = scene()->itemAt( event->scenePos() );
580 break;
581 }
582 }
583}
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 Sat Mar 23 2024 00:06:52 for KD Chart API Documentation by doxygen 1.9.8