KD Chart API Documentation  3.1
kdganttitemdelegate.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 "kdganttconstraint.h"
12 #include "kdganttglobal.h"
13 #include "kdganttitemdelegate_p.h"
15 
16 #include <QAbstractItemModel>
17 #include <QApplication>
18 #include <QModelIndex>
19 #include <QPainter>
20 #include <QPainterPath>
21 #include <QPen>
22 
23 #ifndef QT_NO_DEBUG_STREAM
24 
25 #define PRINT_INTERACTIONSTATE(x) \
26  case x: \
27  dbg << #x; \
28  break;
29 
31 {
32  switch (state) {
37  default:
38  break;
39  }
40  return dbg;
41 }
42 
43 #undef PRINT_INTERACTIONSTATE
44 
45 #endif /* QT_NO_DEBUG_STREAM */
46 
47 using namespace KDGantt;
48 
62 ItemDelegate::Private::Private()
63 {
64  // Brushes
65  QLinearGradient taskgrad(0., 0., 0., QApplication::fontMetrics().height());
66  taskgrad.setColorAt(0., Qt::green);
67  taskgrad.setColorAt(1., Qt::darkGreen);
68 
69  QLinearGradient summarygrad(0., 0., 0., QApplication::fontMetrics().height());
70  summarygrad.setColorAt(0., Qt::blue);
71  summarygrad.setColorAt(1., Qt::darkBlue);
72 
73  QLinearGradient eventgrad(0., 0., 0., QApplication::fontMetrics().height());
74  eventgrad.setColorAt(0., Qt::red);
75  eventgrad.setColorAt(1., Qt::darkRed);
76 
77  defaultbrush[TypeTask] = taskgrad;
78  defaultbrush[TypeSummary] = summarygrad;
79  defaultbrush[TypeEvent] = eventgrad;
80 
81  // Pens
82  QPen pen(Qt::black, 1.);
83 
84  defaultpen[TypeTask] = pen;
85  defaultpen[TypeSummary] = pen;
86  defaultpen[TypeEvent] = pen;
87 }
88 
89 QPen ItemDelegate::Private::constraintPen(const QPointF &start, const QPointF &end, const Constraint &constraint)
90 {
91  QPen pen;
92  QVariant dataPen;
93 
94  // Use default pens...
95  if (start.x() < end.x()) {
96  pen = QPen(Qt::black);
97  dataPen = constraint.data(Constraint::ValidConstraintPen);
98  } else {
99  pen = QPen(Qt::red);
100  dataPen = constraint.data(Constraint::InvalidConstraintPen);
101  }
102 
103  // ... unless constraint.data() returned a valid pen for this case
104  if (dataPen.canConvert(QVariant::Pen)) {
105  pen = dataPen.value<QPen>();
106  }
107 
108  return pen;
109 }
110 
113  : QItemDelegate(parent)
114  , _d(new Private)
115 {
116 }
117 
120 {
121  delete _d;
122 }
123 
124 #define d d_func()
125 
132 void ItemDelegate::setDefaultBrush(ItemType type, const QBrush &brush)
133 {
134  d->defaultbrush[type] = brush;
135 }
136 
142 {
143  return d->defaultbrush[type];
144 }
145 
152 void ItemDelegate::setDefaultPen(ItemType type, const QPen &pen)
153 {
154  d->defaultpen[type] = pen;
155 }
156 
162 {
163  return d->defaultpen[type];
164 }
165 
168 QString ItemDelegate::toolTip(const QModelIndex &idx) const
169 {
170  if (!idx.isValid())
171  return QString();
172 
173  const QAbstractItemModel *model = idx.model();
174  if (!model)
175  return QString();
176  QString tip = model->data(idx, Qt::ToolTipRole).toString();
177  if (!tip.isNull())
178  return tip;
179  else
180  return tr("%1 -> %2: %3")
181  .arg(model->data(idx, StartTimeRole).toString())
182  .arg(model->data(idx, EndTimeRole).toString())
183  .arg(model->data(idx, Qt::DisplayRole).toString());
184 }
185 
195  const QModelIndex &idx) const
196 {
197  if (!idx.isValid())
198  return Span();
199 
200  const QString txt = idx.model()->data(idx, Qt::DisplayRole).toString();
201  const int typ = idx.model()->data(idx, ItemTypeRole).toInt();
202  QRectF itemRect = opt.itemRect;
203 
204  if (typ == TypeEvent) {
205  itemRect = QRectF(itemRect.left() - itemRect.height() / 2.,
206  itemRect.top(),
207  itemRect.height(),
208  itemRect.height());
209  }
210 
211  int tw = opt.fontMetrics.horizontalAdvance(txt);
212  tw += static_cast<int>(itemRect.height() / 2.);
213  Span s;
214  switch (opt.displayPosition) {
216  s = Span(itemRect.left() - tw, itemRect.width() + tw);
217  break;
219  s = Span(itemRect.left(), itemRect.width() + tw);
220  break;
221  case StyleOptionGanttItem::Hidden: // fall through
223  s = Span(itemRect.left(), itemRect.width());
224  break;
225  }
226  return s;
227 }
228 
236  const StyleOptionGanttItem &opt,
237  const QModelIndex &idx) const
238 {
239  if (!idx.isValid())
240  return State_None;
241  if (!(idx.model()->flags(idx) & Qt::ItemIsEditable))
242  return State_None;
243 
244  const int typ = static_cast<ItemType>(idx.model()->data(idx, ItemTypeRole).toInt());
245 
246  QRectF itemRect(opt.itemRect);
247 
248  // An event item is infinitely thin, basically just a line, because it has only one date instead of two.
249  // It is painted with an offset of -height/2, which is taken into account here.
250  if (typ == TypeEvent)
251  itemRect = QRectF(itemRect.topLeft() - QPointF(itemRect.height() / 2.0, 0), QSizeF(itemRect.height(), itemRect.height()));
252 
253  if (typ == TypeNone || typ == TypeSummary)
254  return State_None;
255  if (!itemRect.contains(pos))
256  return State_None;
257  if (typ == TypeEvent)
258  return State_Move;
259 
260  qreal delta = 5.;
261  if (itemRect.width() < 15)
262  delta = 1.;
263  if (pos.x() >= itemRect.left() && pos.x() < itemRect.left() + delta) {
264  return State_ExtendLeft;
265  } else if (pos.x() <= itemRect.right() && pos.x() > itemRect.right() - delta) {
266  return State_ExtendRight;
267  } else {
268  return State_Move;
269  }
270 }
271 
274 void ItemDelegate::paintGanttItem(QPainter *painter,
275  const StyleOptionGanttItem &opt,
276  const QModelIndex &idx)
277 {
278  if (!idx.isValid())
279  return;
280  const ItemType typ = static_cast<ItemType>(idx.model()->data(idx, ItemTypeRole).toInt());
281  const QString &txt = opt.text;
282  QRectF itemRect = opt.itemRect;
283  QRectF boundingRect = opt.boundingRect;
284  boundingRect.setY(itemRect.y());
285  boundingRect.setHeight(itemRect.height());
286 
287  // qDebug() << "itemRect="<<itemRect<<", boundingRect="<<boundingRect;
288  // qDebug() << painter->font() << opt.fontMetrics.height() << painter->device()->width() << painter->device()->height();
289 
290  painter->save();
291 
292  QPen pen = defaultPen(typ);
293  if (opt.state & QStyle::State_Selected)
294  pen.setWidth(2 * pen.width());
295  painter->setPen(pen);
296  painter->setBrush(defaultBrush(typ));
297 
298  bool drawText = true;
299  qreal pw = painter->pen().width() / 2.;
300  switch (typ) {
301  case TypeTask:
302  if (itemRect.isValid()) {
303  // TODO
304  qreal pw = painter->pen().width() / 2.;
305  pw -= 1;
306  QRectF r = itemRect;
307  r.translate(0., r.height() / 6.);
308  r.setHeight(2. * r.height() / 3.);
309  painter->setBrushOrigin(itemRect.topLeft());
310  painter->save();
311  painter->translate(0.5, 0.5);
312  painter->drawRect(r);
313  bool ok;
314  qreal completion = idx.model()->data(idx, KDGantt::TaskCompletionRole).toReal(&ok);
315  if (ok) {
316  qreal h = r.height();
317  QRectF cr(r.x(), r.y() + h / 4.,
318  r.width() * completion / 100., h / 2. + 1 /*??*/);
319  QColor compcolor(painter->pen().color());
320  compcolor.setAlpha(150);
321  painter->fillRect(cr, compcolor);
322  }
323  painter->restore();
324  }
325  break;
326  case TypeSummary:
327  if (opt.itemRect.isValid()) {
328  // TODO
329  pw -= 1;
330  const QRectF r = QRectF(opt.itemRect).adjusted(-pw, -pw, pw, pw);
331  QPainterPath path;
332  const qreal deltaY = r.height() / 2.;
333  const qreal deltaXBezierControl = .25 * qMin(r.width(), r.height());
334  const qreal deltaX = qMin(r.width() / ( qreal )2., r.height());
335  path.moveTo(r.topLeft());
336  path.lineTo(r.topRight());
337  path.lineTo(QPointF(r.right(), r.top() + 2. * deltaY));
338  // path.lineTo( QPointF( r.right()-3./2.*delta, r.top() + delta ) );
339  path.quadTo(QPointF(r.right() - deltaXBezierControl, r.top() + deltaY), QPointF(r.right() - deltaX, r.top() + deltaY));
340  // path.lineTo( QPointF( r.left()+3./2.*delta, r.top() + delta ) );
341  path.lineTo(QPointF(r.left() + deltaX, r.top() + deltaY));
342  path.quadTo(QPointF(r.left() + deltaXBezierControl, r.top() + deltaY), QPointF(r.left(), r.top() + 2. * deltaY));
343  path.closeSubpath();
344  painter->setBrushOrigin(itemRect.topLeft());
345  painter->save();
346  painter->translate(0.5, 0.5);
347  painter->drawPath(path);
348  painter->restore();
349  }
350  break;
351  case TypeEvent: /* TODO */
352  // qDebug() << opt.boundingRect << opt.itemRect;
353  if (opt.boundingRect.isValid()) {
354  const qreal pw = painter->pen().width() / 2. - 1;
355  const QRectF r = QRectF(opt.itemRect).adjusted(-pw, -pw, pw, pw).translated(-opt.itemRect.height() / 2, 0);
356  QPainterPath path;
357  const qreal delta = static_cast<int>(r.height() / 2);
358  path.moveTo(delta, 0.);
359  path.lineTo(2. * delta, delta);
360  path.lineTo(delta, 2. * delta);
361  path.lineTo(0., delta);
362  path.closeSubpath();
363  painter->save();
364  painter->translate(r.topLeft());
365  painter->translate(0, 0.5);
366  painter->drawPath(path);
367  painter->restore();
368 #if 0
369  painter->setBrush( Qt::NoBrush );
370  painter->setPen( Qt::black );
371  painter->drawRect( opt.boundingRect );
372  painter->setPen( Qt::red );
373  painter->drawRect( r );
374 #endif
375  }
376  break;
377  default:
378  drawText = false;
379  break;
380  }
381 
382  Qt::Alignment ta;
383  switch (opt.displayPosition) {
385  ta = Qt::AlignLeft;
386  break;
388  ta = Qt::AlignRight;
389  break;
391  ta = Qt::AlignCenter;
392  break;
394  drawText = false;
395  break;
396  }
397  if (drawText) {
398  painter->drawText(boundingRect, ta | Qt::AlignVCenter, txt);
399  }
400 
401  painter->restore();
402 }
403 
404 static const qreal TURN = 10.;
405 static const qreal PW = 1.5;
406 
411 QRectF ItemDelegate::constraintBoundingRect(const QPointF &start, const QPointF &end, const Constraint &constraint) const
412 {
413  QPolygonF poly;
414  switch (constraint.relationType()) {
416  poly = finishStartLine(start, end) + finishStartArrow(start, end);
417  break;
419  poly = finishFinishLine(start, end) + finishFinishArrow(start, end);
420  break;
422  poly = startStartLine(start, end) + startStartArrow(start, end);
423  break;
425  poly = startFinishLine(start, end) + startFinishArrow(start, end);
426  break;
427  }
428  return poly.boundingRect().adjusted(-PW, -PW, PW, PW);
429 }
430 
436 void ItemDelegate::paintConstraintItem(QPainter *painter, const QStyleOptionGraphicsItem &opt,
437  const QPointF &start, const QPointF &end, const Constraint &constraint)
438 {
439  // qDebug()<<"ItemDelegate::paintConstraintItem"<<start<<end<<constraint;
440  switch (constraint.relationType()) {
442  paintFinishStartConstraint(painter, opt, start, end, constraint);
443  break;
445  paintFinishFinishConstraint(painter, opt, start, end, constraint);
446  break;
448  paintStartStartConstraint(painter, opt, start, end, constraint);
449  break;
451  paintStartFinishConstraint(painter, opt, start, end, constraint);
452  break;
453  }
454 }
455 
456 void ItemDelegate::paintFinishStartConstraint(QPainter *painter, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
457 {
458  Q_UNUSED(opt);
459 
460  const QPen pen = d->constraintPen(start, end, constraint);
461 
462  painter->setPen(pen);
463  painter->setBrush(pen.color());
464 
465  painter->drawPolyline(finishStartLine(start, end));
466  painter->drawPolygon(finishStartArrow(start, end));
467 }
468 
469 QPolygonF ItemDelegate::finishStartLine(const QPointF &start, const QPointF &end) const
470 {
471  QPolygonF poly;
472  qreal midx = end.x() - TURN;
473  qreal midy = (end.y() - start.y()) / 2. + start.y();
474 
475  if (start.x() > end.x() - TURN) {
476  poly << start
477  << QPointF(start.x() + TURN, start.y())
478  << QPointF(start.x() + TURN, midy)
479  << QPointF(end.x() - TURN, midy)
480  << QPointF(end.x() - TURN, end.y())
481  << end;
482  } else {
483  poly << start
484  << QPointF(midx, start.y())
485  << QPointF(midx, end.y())
486  << end;
487  }
488  return poly;
489 }
490 
491 QPolygonF ItemDelegate::finishStartArrow(const QPointF &start, const QPointF &end) const
492 {
493  Q_UNUSED(start);
494 
495  QPolygonF poly;
496  poly << end
497  << QPointF(end.x() - TURN / 2., end.y() - TURN / 2.)
498  << QPointF(end.x() - TURN / 2., end.y() + TURN / 2.);
499  return poly;
500 }
501 
502 void ItemDelegate::paintFinishFinishConstraint(QPainter *painter, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
503 {
504  Q_UNUSED(opt);
505 
506  const QPen pen = d->constraintPen(start, end, constraint);
507 
508  painter->setPen(pen);
509  painter->setBrush(pen.color());
510 
511  painter->drawPolyline(finishFinishLine(start, end));
512  painter->drawPolygon(finishFinishArrow(start, end));
513 }
514 
515 QPolygonF ItemDelegate::finishFinishLine(const QPointF &start, const QPointF &end) const
516 {
517  QPolygonF poly;
518  qreal midx = end.x() + TURN;
519  qreal midy = (end.y() - start.y()) / 2. + start.y();
520 
521  if (start.x() > end.x() + TURN) {
522  poly << start
523  << QPointF(start.x() + TURN, start.y())
524  << QPointF(start.x() + TURN, end.y())
525  << end;
526  } else {
527  poly << start
528  << QPointF(midx, start.y())
529  << QPointF(midx, midy)
530  << QPointF(end.x() + TURN, midy)
531  << QPointF(end.x() + TURN, end.y())
532  << end;
533  }
534  return poly;
535 }
536 
537 QPolygonF ItemDelegate::finishFinishArrow(const QPointF &start, const QPointF &end) const
538 {
539  Q_UNUSED(start);
540 
541  QPolygonF poly;
542  poly << end
543  << QPointF(end.x() + TURN / 2., end.y() - TURN / 2.)
544  << QPointF(end.x() + TURN / 2., end.y() + TURN / 2.);
545  return poly;
546 }
547 
548 void ItemDelegate::paintStartStartConstraint(QPainter *painter, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
549 {
550  Q_UNUSED(opt);
551 
552  const QPen pen = d->constraintPen(start, end, constraint);
553 
554  painter->setPen(pen);
555  painter->setBrush(pen.color());
556 
557  painter->drawPolyline(startStartLine(start, end));
558  painter->drawPolygon(startStartArrow(start, end));
559 }
560 
561 QPolygonF ItemDelegate::startStartLine(const QPointF &start, const QPointF &end) const
562 {
563  Q_UNUSED(start);
564 
565  QPolygonF poly;
566 
567  if (start.x() > end.x()) {
568  poly << start
569  << QPointF(end.x() - TURN, start.y())
570  << QPointF(end.x() - TURN, end.y())
571  << end;
572  } else {
573  poly << start
574  << QPointF(start.x() - TURN, start.y())
575  << QPointF(start.x() - TURN, end.y())
576  << QPointF(end.x() - TURN, end.y())
577  << end;
578  }
579  return poly;
580 }
581 
582 QPolygonF ItemDelegate::startStartArrow(const QPointF &start, const QPointF &end) const
583 {
584  Q_UNUSED(start);
585 
586  QPolygonF poly;
587  poly << end
588  << QPointF(end.x() - TURN / 2., end.y() - TURN / 2.)
589  << QPointF(end.x() - TURN / 2., end.y() + TURN / 2.);
590  return poly;
591 }
592 
593 void ItemDelegate::paintStartFinishConstraint(QPainter *painter, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
594 {
595  Q_UNUSED(opt);
596 
597  const QPen pen = d->constraintPen(start, end, constraint);
598 
599  painter->setPen(pen);
600  painter->setBrush(pen.color());
601 
602  painter->drawPolyline(startFinishLine(start, end));
603  painter->drawPolygon(startFinishArrow(start, end));
604 }
605 
606 QPolygonF ItemDelegate::startFinishLine(const QPointF &start, const QPointF &end) const
607 {
608  Q_UNUSED(start);
609 
610  QPolygonF poly;
611  qreal midx = end.x() + TURN;
612  qreal midy = (end.y() - start.y()) / 2. + start.y();
613 
614  if (start.x() - TURN > end.x() + TURN) {
615  poly << start
616  << QPointF(midx, start.y())
617  << QPointF(midx, end.y())
618  << end;
619  } else {
620  poly << start
621  << QPointF(start.x() - TURN, start.y())
622  << QPointF(start.x() - TURN, midy)
623  << QPointF(midx, midy)
624  << QPointF(end.x() + TURN, end.y())
625  << end;
626  }
627  return poly;
628 }
629 
630 QPolygonF ItemDelegate::startFinishArrow(const QPointF &start, const QPointF &end) const
631 {
632  Q_UNUSED(start);
633 
634  QPolygonF poly;
635  poly << end
636  << QPointF(end.x() + TURN / 2., end.y() - TURN / 2.)
637  << QPointF(end.x() + TURN / 2., end.y() + TURN / 2.);
638  return poly;
639 }
640 
641 #include "moc_kdganttitemdelegate.cpp"
A class used to represent a dependency.
QVariant data(int role) const
RelationType relationType() const
This is unused for now.
virtual void paintConstraintItem(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
Paints the constraint between points start and end using painter and opt.
void paintFinishStartConstraint(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
QPen defaultPen(ItemType type) const
QPolygonF startFinishArrow(const QPointF &start, const QPointF &end) const
virtual QString toolTip(const QModelIndex &idx) const
QPolygonF startStartLine(const QPointF &start, const QPointF &end) const
~ItemDelegate() override
Destructor.
virtual Span itemBoundingSpan(const StyleOptionGanttItem &opt, const QModelIndex &idx) const
virtual InteractionState interactionStateFor(const QPointF &pos, const StyleOptionGanttItem &opt, const QModelIndex &idx) const
void paintStartStartConstraint(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
void setDefaultBrush(ItemType type, const QBrush &brush)
Sets the default brush used for items of type type to brush.
void paintStartFinishConstraint(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
QPolygonF finishFinishLine(const QPointF &start, const QPointF &end) const
InteractionState
This enum is used for communication between the view and the delegate about user interaction with gan...
void setDefaultPen(ItemType type, const QPen &pen)
Sets the default pen used for items of type type to pen.
QPolygonF startFinishLine(const QPointF &start, const QPointF &end) const
QPolygonF finishStartArrow(const QPointF &start, const QPointF &end) const
virtual void paintGanttItem(QPainter *p, const StyleOptionGanttItem &opt, const QModelIndex &idx)
Paints the gantt item idx using painter and opt.
QPolygonF finishFinishArrow(const QPointF &start, const QPointF &end) const
QBrush defaultBrush(ItemType type) const
void paintFinishFinishConstraint(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
virtual QRectF constraintBoundingRect(const QPointF &start, const QPointF &end, const Constraint &constraint) const
ItemDelegate(QObject *parent=nullptr)
Constructor.
QPolygonF startStartArrow(const QPointF &start, const QPointF &end) const
QPolygonF finishStartLine(const QPointF &start, const QPointF &end) const
A class representing a start point and a length.
QStyleOption subclass for gantt items.
QRectF boundingRect
Contains the bounding rectangle for the item.
QString text
Contains a string printed to the item.
QRectF itemRect
Contains the "active" item rectangle that corresponds to the values from the model.
Contains KDGantt macros.
#define d
static const qreal PW
static const qreal TURN
#define PRINT_INTERACTIONSTATE(x)
QDebug operator<<(QDebug dbg, KDGantt::ItemDelegate::InteractionState state)
@ TaskCompletionRole
ItemType
The values of this enum are used to represent the different types of gantt items that KDGantt underst...

© 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 by doxygen 1.9.1