KD Chart 2  [rev.2.7]
kdganttitemdelegate.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2020 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
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.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "kdganttitemdelegate_p.h"
24 #include "kdganttglobal.h"
26 #include "kdganttconstraint.h"
27 
28 #include <QPainter>
29 #include <QPainterPath>
30 #include <QPen>
31 #include <QModelIndex>
32 #include <QAbstractItemModel>
33 #include <QApplication>
34 
35 #ifndef QT_NO_DEBUG_STREAM
36 
37 #define PRINT_INTERACTIONSTATE(x) \
38  case x: dbg << #x; break;
39 
40 
42 {
43  switch ( state ) {
48  default:
49  break;
50  }
51  return dbg;
52 }
53 
54 #undef PRINT_INTERACTIONSTATE
55 
56 #endif /* QT_NO_DEBUG_STREAM */
57 
58 using namespace KDGantt;
59 
73 ItemDelegate::Private::Private()
74 {
75  // Brushes
76  QLinearGradient taskgrad( 0., 0., 0., QApplication::fontMetrics().height() );
77  taskgrad.setColorAt( 0., Qt::green );
78  taskgrad.setColorAt( 1., Qt::darkGreen );
79 
80  QLinearGradient summarygrad( 0., 0., 0., QApplication::fontMetrics().height() );
81  summarygrad.setColorAt( 0., Qt::blue );
82  summarygrad.setColorAt( 1., Qt::darkBlue );
83 
84  QLinearGradient eventgrad( 0., 0., 0., QApplication::fontMetrics().height() );
85  eventgrad.setColorAt( 0., Qt::red );
86  eventgrad.setColorAt( 1., Qt::darkRed );
87 
88  defaultbrush[TypeTask] = taskgrad;
89  defaultbrush[TypeSummary] = summarygrad;
90  defaultbrush[TypeEvent] = eventgrad;
91 
92  // Pens
93  QPen pen( Qt::black, 1. );
94 
95  defaultpen[TypeTask] = pen;
96  defaultpen[TypeSummary] = pen;
97  defaultpen[TypeEvent] = pen;
98 }
99 
100 QPen ItemDelegate::Private::constraintPen( const QPointF& start, const QPointF& end, const Constraint& constraint )
101 {
102  QPen pen;
103  QVariant dataPen;
104 
105  // Use default pens...
106  if ( start.x() < end.x() ) {
107  pen = QPen( Qt::black );
108  dataPen = constraint.data( Constraint::ValidConstraintPen );
109  } else {
110  pen = QPen( Qt::red );
111  dataPen = constraint.data( Constraint::InvalidConstraintPen );
112  }
113 
114  // ... unless constraint.data() returned a valid pen for this case
115  if ( dataPen.canConvert( QVariant::Pen ) ) {
116  pen = dataPen.value< QPen >();
117  }
118 
119  return pen;
120 }
121 
124  : QItemDelegate( parent ), _d( new Private )
125 {
126 }
127 
130 {
131  delete _d;
132 }
133 
134 #define d d_func()
135 
142 void ItemDelegate::setDefaultBrush( ItemType type, const QBrush& brush )
143 {
144  d->defaultbrush[type] = brush;
145 }
146 
152 {
153  return d->defaultbrush[type];
154 }
155 
162 void ItemDelegate::setDefaultPen( ItemType type, const QPen& pen )
163 {
164  d->defaultpen[type]=pen;
165 }
166 
172 {
173  return d->defaultpen[type];
174 }
175 
178 QString ItemDelegate::toolTip( const QModelIndex &idx ) const
179 {
180  if ( !idx.isValid() ) return QString();
181 
182  const QAbstractItemModel* model = idx.model();
183  if ( !model ) return QString();
184  QString tip = model->data( idx, Qt::ToolTipRole ).toString();
185  if ( !tip.isNull() ) return tip;
186  else return tr( "%1 -> %2: %3" )
187  .arg( model->data( idx, StartTimeRole ).toString() )
188  .arg( model->data( idx, EndTimeRole ).toString() )
189  .arg( model->data( idx, Qt::DisplayRole ).toString() );
190 }
191 
201  const QModelIndex& idx ) const
202 {
203  if ( !idx.isValid() ) return Span();
204 
205  const QString txt = idx.model()->data( idx, Qt::DisplayRole ).toString();
206  const int typ = idx.model()->data( idx, ItemTypeRole ).toInt();
207  QRectF itemRect = opt.itemRect;
208 
209 
210  if ( typ == TypeEvent ) {
211  itemRect = QRectF( itemRect.left()-itemRect.height()/2.,
212  itemRect.top(),
213  itemRect.height(),
214  itemRect.height() );
215  }
216 
217  int tw = opt.fontMetrics.width( txt );
218  tw += static_cast<int>( itemRect.height()/2. );
219  Span s;
220  switch ( opt.displayPosition ) {
222  s = Span( itemRect.left()-tw, itemRect.width()+tw ); break;
224  s = Span( itemRect.left(), itemRect.width()+tw ); break;
225  case StyleOptionGanttItem::Hidden: // fall through
227  s = Span( itemRect.left(), itemRect.width() ); break;
228  }
229  return s;
230 }
231 
239  const StyleOptionGanttItem& opt,
240  const QModelIndex& idx ) const
241 {
242  if ( !idx.isValid() ) return State_None;
243  if ( !( idx.model()->flags( idx ) & Qt::ItemIsEditable ) ) return State_None;
244 
245  const int typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
246 
247  QRectF itemRect( opt.itemRect );
248 
249  // An event item is infinitely thin, basically just a line, because it has only one date instead of two.
250  // It is painted with an offset of -height/2, which is taken into account here.
251  if ( typ == TypeEvent )
252  itemRect = QRectF( itemRect.topLeft() - QPointF( itemRect.height() / 2.0, 0 ), QSizeF( itemRect.height(),
253  itemRect.height() ) );
254 
255  if ( typ == TypeNone || typ == TypeSummary ) return State_None;
256  if ( !itemRect.contains(pos) ) return State_None;
257  if ( typ == TypeEvent )
258  return State_Move;
259 
260  qreal delta = 5.;
261  if ( itemRect.width() < 15 ) delta = 1.;
262  if ( pos.x() >= itemRect.left() && pos.x() < itemRect.left()+delta ) {
263  return State_ExtendLeft;
264  } else if ( pos.x() <= itemRect.right() && pos.x() > itemRect.right()-delta ) {
265  return State_ExtendRight;
266  } else {
267  return State_Move;
268  }
269 }
270 
273 void ItemDelegate::paintGanttItem( QPainter* painter,
274  const StyleOptionGanttItem& opt,
275  const QModelIndex& idx )
276 {
277  if ( !idx.isValid() ) return;
278  const ItemType typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
279  const QString& txt = opt.text;
280  QRectF itemRect = opt.itemRect;
281  QRectF boundingRect = opt.boundingRect;
282  boundingRect.setY( itemRect.y() );
283  boundingRect.setHeight( itemRect.height() );
284 
285  //qDebug() << "itemRect="<<itemRect<<", boundingRect="<<boundingRect;
286  //qDebug() << painter->font() << opt.fontMetrics.height() << painter->device()->width() << painter->device()->height();
287 
288  painter->save();
289 
290  QPen pen = defaultPen( typ );
291  if ( opt.state & QStyle::State_Selected ) pen.setWidth( 2*pen.width() );
292  painter->setPen( pen );
293  painter->setBrush( defaultBrush( typ ) );
294 
295  bool drawText = true;
296  qreal pw = painter->pen().width()/2.;
297  switch ( typ ) {
298  case TypeTask:
299  if ( itemRect.isValid() ) {
300  // TODO
301  qreal pw = painter->pen().width()/2.;
302  pw-=1;
303  QRectF r = itemRect;
304  r.translate( 0., r.height()/6. );
305  r.setHeight( 2.*r.height()/3. );
306  painter->setBrushOrigin( itemRect.topLeft() );
307  painter->save();
308  painter->translate( 0.5, 0.5 );
309  painter->drawRect( r );
310  bool ok;
311  qreal completion = idx.model()->data( idx, KDGantt::TaskCompletionRole ).toReal( &ok );
312  if ( ok ) {
313  qreal h = r.height();
314  QRectF cr( r.x(), r.y()+h/4.,
315  r.width()*completion/100., h/2.+1 /*??*/ );
316  QColor compcolor( painter->pen().color() );
317  compcolor.setAlpha( 150 );
318  painter->fillRect( cr, compcolor );
319  }
320  painter->restore();
321  }
322  break;
323  case TypeSummary:
324  if ( opt.itemRect.isValid() ) {
325  // TODO
326  pw-=1;
327  const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw );
328  QPainterPath path;
329  const qreal deltaY = r.height()/2.;
330  const qreal deltaXBezierControl = .25*qMin( r.width(), r.height() );
331  const qreal deltaX = qMin( r.width()/(qreal)2., r.height() );
332  path.moveTo( r.topLeft() );
333  path.lineTo( r.topRight() );
334  path.lineTo( QPointF( r.right(), r.top() + 2.*deltaY ) );
335  //path.lineTo( QPointF( r.right()-3./2.*delta, r.top() + delta ) );
336  path.quadTo( QPointF( r.right()-deltaXBezierControl, r.top() + deltaY ), QPointF( r.right()-deltaX, r.top() + deltaY ) );
337  //path.lineTo( QPointF( r.left()+3./2.*delta, r.top() + delta ) );
338  path.lineTo( QPointF( r.left() + deltaX, r.top() + deltaY ) );
339  path.quadTo( QPointF( r.left()+deltaXBezierControl, r.top() + deltaY ), QPointF( r.left(), r.top() + 2.*deltaY ) );
340  path.closeSubpath();
341  painter->setBrushOrigin( itemRect.topLeft() );
342  painter->save();
343  painter->translate( 0.5, 0.5 );
344  painter->drawPath( path );
345  painter->restore();
346  }
347  break;
348  case TypeEvent: /* TODO */
349  //qDebug() << opt.boundingRect << opt.itemRect;
350  if ( opt.boundingRect.isValid() ) {
351  const qreal pw = painter->pen().width() / 2. - 1;
352  const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw ).translated( -opt.itemRect.height()/2, 0 );
353  QPainterPath path;
354  const qreal delta = static_cast< int >( r.height() / 2 );
355  path.moveTo( delta, 0. );
356  path.lineTo( 2.*delta, delta );
357  path.lineTo( delta, 2.*delta );
358  path.lineTo( 0., delta );
359  path.closeSubpath();
360  painter->save();
361  painter->translate( r.topLeft() );
362  painter->translate( 0, 0.5 );
363  painter->drawPath( path );
364  painter->restore();
365 #if 0
366  painter->setBrush( Qt::NoBrush );
367  painter->setPen( Qt::black );
368  painter->drawRect( opt.boundingRect );
369  painter->setPen( Qt::red );
370  painter->drawRect( r );
371 #endif
372  }
373  break;
374  default:
375  drawText = false;
376  break;
377  }
378 
379  Qt::Alignment ta;
380  switch ( opt.displayPosition ) {
381  case StyleOptionGanttItem::Left: ta = Qt::AlignLeft; break;
382  case StyleOptionGanttItem::Right: ta = Qt::AlignRight; break;
383  case StyleOptionGanttItem::Center: ta = Qt::AlignCenter; break;
384  case StyleOptionGanttItem::Hidden: drawText = false; break;
385  }
386  if ( drawText ) {
387  painter->drawText( boundingRect, ta | Qt::AlignVCenter, txt );
388  }
389 
390  painter->restore();
391 }
392 
393 static const qreal TURN = 10.;
394 static const qreal PW = 1.5;
395 
400 QRectF ItemDelegate::constraintBoundingRect( const QPointF& start, const QPointF& end, const Constraint &constraint ) const
401 {
402  QPolygonF poly;
403  switch ( constraint.relationType() ) {
405  poly = finishStartLine( start, end ) + finishStartArrow( start, end );
406  break;
408  poly = finishFinishLine( start, end ) + finishFinishArrow( start, end );
409  break;
411  poly = startStartLine( start, end ) + startStartArrow( start, end );
412  break;
414  poly = startFinishLine( start, end ) + startFinishArrow( start, end );
415  break;
416  }
417  return poly.boundingRect().adjusted( -PW, -PW, PW, PW );
418 }
419 
420 
426 void ItemDelegate::paintConstraintItem( QPainter* painter, const QStyleOptionGraphicsItem& opt,
427  const QPointF& start, const QPointF& end, const Constraint &constraint )
428 {
429  //qDebug()<<"ItemDelegate::paintConstraintItem"<<start<<end<<constraint;
430  switch ( constraint.relationType() ) {
432  paintFinishStartConstraint( painter, opt, start, end, constraint );
433  break;
435  paintFinishFinishConstraint( painter, opt, start, end, constraint );
436  break;
438  paintStartStartConstraint( painter, opt, start, end, constraint );
439  break;
441  paintStartFinishConstraint( painter, opt, start, end, constraint );
442  break;
443  }
444 }
445 
446 void ItemDelegate::paintFinishStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
447 {
448  Q_UNUSED( opt );
449 
450  const QPen pen = d->constraintPen( start, end, constraint );
451 
452  painter->setPen( pen );
453  painter->setBrush( pen.color() );
454 
455  painter->drawPolyline( finishStartLine( start, end ) );
456  painter->drawPolygon( finishStartArrow( start, end ) );
457 }
458 
459 QPolygonF ItemDelegate::finishStartLine( const QPointF& start, const QPointF& end ) const
460 {
461  QPolygonF poly;
462  qreal midx = end.x() - TURN;
463  qreal midy = ( end.y()-start.y() )/2. + start.y();
464 
465  if ( start.x() > end.x()-TURN ) {
466  poly << start
467  << QPointF( start.x()+TURN, start.y() )
468  << QPointF( start.x()+TURN, midy )
469  << QPointF( end.x()-TURN, midy )
470  << QPointF( end.x()-TURN, end.y() )
471  << end;
472  } else {
473  poly << start
474  << QPointF( midx, start.y() )
475  << QPointF( midx, end.y() )
476  << end;
477  }
478  return poly;
479 }
480 
481 QPolygonF ItemDelegate::finishStartArrow( const QPointF& start, const QPointF& end ) const
482 {
483  Q_UNUSED(start);
484 
485  QPolygonF poly;
486  poly << end
487  << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
488  << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
489  return poly;
490 }
491 
492 void ItemDelegate::paintFinishFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
493 {
494  Q_UNUSED( opt );
495 
496  const QPen pen = d->constraintPen( start, end, constraint );
497 
498  painter->setPen( pen );
499  painter->setBrush( pen.color() );
500 
501  painter->drawPolyline( finishFinishLine( start, end ) );
502  painter->drawPolygon( finishFinishArrow( start, end ) );
503 }
504 
505 QPolygonF ItemDelegate::finishFinishLine( const QPointF& start, const QPointF& end ) const
506 {
507  QPolygonF poly;
508  qreal midx = end.x() + TURN;
509  qreal midy = ( end.y()-start.y() )/2. + start.y();
510 
511  if ( start.x() > end.x()+TURN ) {
512  poly << start
513  << QPointF( start.x()+TURN, start.y() )
514  << QPointF( start.x()+TURN, end.y() )
515  << end;
516  } else {
517  poly << start
518  << QPointF( midx, start.y() )
519  << QPointF( midx, midy )
520  << QPointF( end.x()+TURN, midy )
521  << QPointF( end.x()+TURN, end.y() )
522  << end;
523  }
524  return poly;
525 }
526 
527 QPolygonF ItemDelegate::finishFinishArrow( const QPointF& start, const QPointF& end ) const
528 {
529  Q_UNUSED(start);
530 
531  QPolygonF poly;
532  poly << end
533  << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
534  << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
535  return poly;
536 }
537 
538 void ItemDelegate::paintStartStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
539 {
540  Q_UNUSED( opt );
541 
542  const QPen pen = d->constraintPen( start, end, constraint );
543 
544  painter->setPen( pen );
545  painter->setBrush( pen.color() );
546 
547  painter->drawPolyline( startStartLine( start, end ) );
548  painter->drawPolygon( startStartArrow( start, end ) );
549 
550 }
551 
552 QPolygonF ItemDelegate::startStartLine( const QPointF& start, const QPointF& end ) const
553 {
554  Q_UNUSED(start);
555 
556  QPolygonF poly;
557 
558  if ( start.x() > end.x() ) {
559  poly << start
560  << QPointF( end.x()-TURN, start.y() )
561  << QPointF( end.x()-TURN, end.y() )
562  << end;
563  } else {
564  poly << start
565  << QPointF( start.x()-TURN, start.y() )
566  << QPointF( start.x()-TURN, end.y() )
567  << QPointF( end.x()-TURN, end.y() )
568  << end;
569  }
570  return poly;
571 }
572 
573 QPolygonF ItemDelegate::startStartArrow( const QPointF& start, const QPointF& end ) const
574 {
575  Q_UNUSED(start);
576 
577  QPolygonF poly;
578  poly << end
579  << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
580  << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
581  return poly;
582 }
583 
584 void ItemDelegate::paintStartFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
585 {
586  Q_UNUSED( opt );
587 
588  const QPen pen = d->constraintPen( start, end, constraint );
589 
590  painter->setPen( pen );
591  painter->setBrush( pen.color() );
592 
593  painter->drawPolyline( startFinishLine( start, end ) );
594  painter->drawPolygon( startFinishArrow( start, end ) );
595 }
596 
597 QPolygonF ItemDelegate::startFinishLine( const QPointF& start, const QPointF& end ) const
598 {
599  Q_UNUSED(start);
600 
601  QPolygonF poly;
602  qreal midx = end.x() + TURN;
603  qreal midy = ( end.y()-start.y() )/2. + start.y();
604 
605  if ( start.x()-TURN > end.x()+TURN ) {
606  poly << start
607  << QPointF( midx, start.y() )
608  << QPointF( midx, end.y() )
609  << end;
610  } else {
611  poly << start
612  << QPointF( start.x()-TURN, start.y() )
613  << QPointF( start.x()-TURN, midy )
614  << QPointF( midx, midy )
615  << QPointF( end.x()+TURN, end.y() )
616  << end;
617  }
618  return poly;
619 }
620 
621 QPolygonF ItemDelegate::startFinishArrow( const QPointF& start, const QPointF& end ) const
622 {
623  Q_UNUSED(start);
624 
625  QPolygonF poly;
626  poly << end
627  << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
628  << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
629  return poly;
630 }
631 
632 
633 #include "moc_kdganttitemdelegate.cpp"
void paintFinishStartConstraint(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
QDebug operator<<(QDebug dbg, KDGantt::ItemDelegate::InteractionState state)
virtual InteractionState interactionStateFor(const QPointF &pos, const StyleOptionGanttItem &opt, const QModelIndex &idx) const
QPolygonF finishFinishLine(const QPointF &start, const QPointF &end) const
QPolygonF startFinishLine(const QPointF &start, const QPointF &end) const
virtual QString toolTip(const QModelIndex &idx) const
QVariant data(int role) const
A class used to represent a dependency.
ItemDelegate(QObject *parent=0)
QPolygonF finishStartArrow(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)
A class representing a start point and a length.
virtual QRectF constraintBoundingRect(const QPointF &start, const QPointF &end, const Constraint &constraint) const
QPen defaultPen(ItemType type) const
void setDefaultBrush(ItemType type, const QBrush &brush)
virtual void paintGanttItem(QPainter *p, const StyleOptionGanttItem &opt, const QModelIndex &idx)
#define d
void paintStartStartConstraint(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
void setDefaultPen(ItemType type, const QPen &pen)
RelationType relationType() const
QStyleOption subclass for gantt items.
virtual void paintConstraintItem(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
#define PRINT_INTERACTIONSTATE(x)
static const qreal PW
QPolygonF finishFinishArrow(const QPointF &start, const QPointF &end) const
QPolygonF startStartArrow(const QPointF &start, const QPointF &end) const
static const qreal TURN
void paintStartFinishConstraint(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
Class only listed here to document inheritance of some KDChart classes.
QPolygonF startFinishArrow(const QPointF &start, const QPointF &end) const
virtual Span itemBoundingSpan(const StyleOptionGanttItem &opt, const QModelIndex &idx) const
QPolygonF startStartLine(const QPointF &start, const QPointF &end) const
QPolygonF finishStartLine(const QPointF &start, const QPointF &end) const

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/