KGantt

kganttitemdelegate.cpp
1 /*
2  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3  *
4  * This file is part of the KGantt library.
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "kganttitemdelegate_p.h"
10 #include "kganttglobal.h"
11 #include "kganttstyleoptionganttitem.h"
12 #include "kganttconstraint.h"
13 
14 #include <QPainter>
15 #include <QPainterPath>
16 #include <QPen>
17 #include <QModelIndex>
18 #include <QAbstractItemModel>
19 #include <QApplication>
20 
21 #ifndef QT_NO_DEBUG_STREAM
22 
23 #define PRINT_INTERACTIONSTATE(x) \
24  case x: dbg << #x; break;
25 
26 
27 QDebug operator<<( QDebug dbg, KGantt::ItemDelegate::InteractionState state )
28 {
29  switch ( state ) {
30  PRINT_INTERACTIONSTATE( KGantt::ItemDelegate::State_None );
31  PRINT_INTERACTIONSTATE( KGantt::ItemDelegate::State_Move );
32  PRINT_INTERACTIONSTATE( KGantt::ItemDelegate::State_ExtendLeft );
33  PRINT_INTERACTIONSTATE( KGantt::ItemDelegate::State_ExtendRight );
34  default:
35  break;
36  }
37  return dbg;
38 }
39 
40 #undef PRINT_INTERACTIONSTATE
41 
42 #endif /* QT_NO_DEBUG_STREAM */
43 
44 using namespace KGantt;
45 
46 
47 ItemDelegate::Private::Private()
48 {
49  // Brushes
50  QLinearGradient taskgrad( 0., 0., 0., QApplication::fontMetrics().height() );
51  taskgrad.setColorAt( 0., Qt::green );
52  taskgrad.setColorAt( 1., Qt::darkGreen );
53 
54  QLinearGradient summarygrad( 0., 0., 0., QApplication::fontMetrics().height() );
55  summarygrad.setColorAt( 0., Qt::blue );
56  summarygrad.setColorAt( 1., Qt::darkBlue );
57 
58  QLinearGradient eventgrad( 0., 0., 0., QApplication::fontMetrics().height() );
59  eventgrad.setColorAt( 0., Qt::red );
60  eventgrad.setColorAt( 1., Qt::darkRed );
61 
62  defaultbrush[TypeTask] = taskgrad;
63  defaultbrush[TypeSummary] = summarygrad;
64  defaultbrush[TypeEvent] = eventgrad;
65 
66  // Pens
67  QPen pen( QGuiApplication::palette().windowText(), 1. );
68 
69  defaultpen[TypeTask] = pen;
70  defaultpen[TypeSummary] = pen;
71  defaultpen[TypeEvent] = pen;
72 }
73 
74 QPen ItemDelegate::Private::constraintPen( const QPointF& start, const QPointF& end, const Constraint& constraint, const QStyleOptionGraphicsItem& opt )
75 {
76  QPen pen;
77  QVariant dataPen;
78 
79  // Use default pens...
80  if ( start.x() <= end.x() ) {
81  pen = QPen( opt.palette.windowText().color() );
82  dataPen = constraint.data( Constraint::ValidConstraintPen );
83  } else {
84  pen = QPen( Qt::red );
85  dataPen = constraint.data( Constraint::InvalidConstraintPen );
86  }
87 
88  // ... unless constraint.data() returned a valid pen for this case
89  if ( dataPen.canConvert( QVariant::Pen ) ) {
90  pen = dataPen.value< QPen >();
91  }
92 
93  return pen;
94 }
95 
96 
98  : QItemDelegate( parent ), _d( new Private )
99 {
100 }
101 
102 
104 {
105  delete _d;
106 }
107 
108 #define d d_func()
109 
110 
112 {
113  d->defaultbrush[type] = brush;
114 }
115 
116 
118 {
119  return d->defaultbrush[type];
120 }
121 
122 
123 void ItemDelegate::setDefaultPen( ItemType type, const QPen& pen )
124 {
125  d->defaultpen[type]=pen;
126 }
127 
128 
130 {
131  return d->defaultpen[type];
132 }
133 
134 
136 {
137  if ( !idx.isValid() ) return QString();
138 
139  const QAbstractItemModel* model = idx.model();
140  if ( !model ) return QString();
141  QString tip = model->data( idx, Qt::ToolTipRole ).toString();
142  if ( !tip.isNull() ) return tip;
143  else return tr( "%1 -> %2: %3", "start time -> end time: item name" )
144  .arg( model->data( idx, StartTimeRole ).toString() )
145  .arg( model->data( idx, EndTimeRole ).toString() )
146  .arg( model->data( idx, Qt::DisplayRole ).toString() );
147 }
148 
149 
151  const QModelIndex& idx ) const
152 {
153  if ( !idx.isValid() ) return Span();
154 
155  const QString txt = idx.model()->data( idx, Qt::DisplayRole ).toString();
156  const int typ = idx.model()->data( idx, ItemTypeRole ).toInt();
157  QRectF itemRect = opt.itemRect;
158 
159 
160  if ( typ == TypeEvent ) {
161  itemRect = QRectF( itemRect.left()-itemRect.height()/2.,
162  itemRect.top(),
163  itemRect.height(),
164  itemRect.height() );
165  }
166 
167  int tw = opt.fontMetrics.boundingRect( txt ).width();
168  tw += static_cast<int>( itemRect.height()/2. );
169  Span s;
170  switch ( opt.displayPosition ) {
172  s = Span( itemRect.left()-tw, itemRect.width()+tw ); break;
174  s = Span( itemRect.left(), itemRect.width()+tw ); break;
175  case StyleOptionGanttItem::Hidden: // fall through
176  case StyleOptionGanttItem::Center:
177  s = Span( itemRect.left(), itemRect.width() ); break;
178  }
179  return s;
180 }
181 
182 
183 ItemDelegate::InteractionState ItemDelegate::interactionStateFor( const QPointF& pos,
184  const StyleOptionGanttItem& opt,
185  const QModelIndex& idx ) const
186 {
187  if ( !idx.isValid() ) return State_None;
188  if ( !( idx.model()->flags( idx ) & Qt::ItemIsEditable ) ) return State_None;
189 
190  const int typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
191 
192  QRectF itemRect( opt.itemRect );
193 
194  // An event item is infinitely thin, basically just a line, because it has only one date instead of two.
195  // It is painted with an offset of -height/2, which is taken into account here.
196  if ( typ == TypeEvent )
197  itemRect = QRectF( itemRect.topLeft() - QPointF( itemRect.height() / 2.0, 0 ), QSizeF( itemRect.height(),
198  itemRect.height() ) );
199 
200  if ( typ == TypeNone || typ == TypeSummary ) return State_None;
201  if ( !itemRect.contains(pos) ) return State_None;
202  if ( typ == TypeEvent )
203  return State_Move;
204 
205  qreal delta = 5.;
206  if ( itemRect.width() < 15 ) delta = 1.;
207  if ( pos.x() >= itemRect.left() && pos.x() < itemRect.left()+delta ) {
208  return State_ExtendLeft;
209  } else if ( pos.x() <= itemRect.right() && pos.x() > itemRect.right()-delta ) {
210  return State_ExtendRight;
211  } else {
212  return State_Move;
213  }
214 }
215 
216 
218  const StyleOptionGanttItem& opt,
219  const QModelIndex& idx )
220 {
221  if ( !idx.isValid() ) return;
222  const ItemType typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
223  const QString& txt = opt.text;
224  QRectF itemRect = opt.itemRect;
225  QRectF boundingRect = opt.boundingRect;
226  boundingRect.setY( itemRect.y() );
227  boundingRect.setHeight( itemRect.height() );
228 
229  //qDebug() << "itemRect="<<itemRect<<", boundingRect="<<boundingRect;
230  //qDebug() << painter->font() << opt.fontMetrics.height() << painter->device()->width() << painter->device()->height();
231 
232  painter->save();
233 
234  QPen pen = defaultPen( typ );
235  if ( opt.state & QStyle::State_Selected ) pen.setWidth( 2*pen.width() );
236  painter->setPen( pen );
237  painter->setBrush( defaultBrush( typ ) );
238 
239  bool drawText = true;
240  qreal pw = painter->pen().width()/2.;
241  switch ( typ ) {
242  case TypeTask:
243  if ( itemRect.isValid() ) {
244  // TODO
245  qreal pw = painter->pen().width()/2.;
246  pw-=1;
247  QRectF r = itemRect;
248  r.translate( 0., r.height()/6. );
249  r.setHeight( 2.*r.height()/3. );
250  painter->setBrushOrigin( itemRect.topLeft() );
251  painter->save();
252  painter->translate( 0.5, 0.5 );
253  painter->drawRect( r );
254  bool ok;
255  qreal completion = idx.model()->data( idx, KGantt::TaskCompletionRole ).toReal( &ok );
256  if ( ok ) {
257  qreal h = r.height();
258  QRectF cr( r.x(), r.y()+h/4.,
259  r.width()*completion/100., h/2.+1 /*??*/ );
260  QColor compcolor( painter->pen().color() );
261  compcolor.setAlpha( 150 );
262  painter->fillRect( cr, compcolor );
263  }
264  painter->restore();
265  }
266  break;
267  case TypeSummary:
268  if ( opt.itemRect.isValid() ) {
269  // TODO
270  pw-=1;
271  const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw );
272  QPainterPath path;
273  const qreal deltaY = r.height()/2.;
274  const qreal deltaXBezierControl = .25*qMin( r.width(), r.height() );
275  const qreal deltaX = qMin( r.width()/2, r.height() );
276  path.moveTo( r.topLeft() );
277  path.lineTo( r.topRight() );
278  path.lineTo( QPointF( r.right(), r.top() + 2.*deltaY ) );
279  //path.lineTo( QPointF( r.right()-3./2.*delta, r.top() + delta ) );
280  path.quadTo( QPointF( r.right()-deltaXBezierControl, r.top() + deltaY ), QPointF( r.right()-deltaX, r.top() + deltaY ) );
281  //path.lineTo( QPointF( r.left()+3./2.*delta, r.top() + delta ) );
282  path.lineTo( QPointF( r.left() + deltaX, r.top() + deltaY ) );
283  path.quadTo( QPointF( r.left()+deltaXBezierControl, r.top() + deltaY ), QPointF( r.left(), r.top() + 2.*deltaY ) );
284  path.closeSubpath();
285  painter->setBrushOrigin( itemRect.topLeft() );
286  painter->save();
287  painter->translate( 0.5, 0.5 );
288  painter->drawPath( path );
289  painter->restore();
290  }
291  break;
292  case TypeEvent: /* TODO */
293  //qDebug() << opt.boundingRect << opt.itemRect;
294  if ( opt.boundingRect.isValid() ) {
295  const qreal pw = painter->pen().width() / 2. - 1;
296  const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw ).translated( -opt.itemRect.height()/2, 0 );
297  QPainterPath path;
298  const qreal delta = static_cast< int >( r.height() / 2 );
299  path.moveTo( delta, 0. );
300  path.lineTo( 2.*delta, delta );
301  path.lineTo( delta, 2.*delta );
302  path.lineTo( 0., delta );
303  path.closeSubpath();
304  painter->save();
305  painter->translate( r.topLeft() );
306  painter->translate( 0, 0.5 );
307  painter->drawPath( path );
308  painter->restore();
309 #if 0
310  painter->setBrush( Qt::NoBrush );
311  painter->setPen( Qt::black );
312  painter->drawRect( opt.boundingRect );
313  painter->setPen( Qt::red );
314  painter->drawRect( r );
315 #endif
316  }
317  break;
318  default:
319  drawText = false;
320  break;
321  }
322 
323  Qt::Alignment ta;
324  switch ( opt.displayPosition ) {
325  case StyleOptionGanttItem::Left: ta = Qt::AlignLeft; break;
327  case StyleOptionGanttItem::Center: ta = Qt::AlignCenter; break;
328  case StyleOptionGanttItem::Hidden: drawText = false; break;
329  }
330  if ( drawText ) {
331  pen = painter->pen();
332  pen.setColor(opt.palette.text().color());
333  painter->setPen(pen);
334  painter->drawText( boundingRect, ta | Qt::AlignVCenter, txt );
335  }
336 
337  painter->restore();
338 }
339 
340 static const qreal TURN = 10.;
341 static const qreal PW = 1.5;
342 
343 
344 QRectF ItemDelegate::constraintBoundingRect( const QPointF& start, const QPointF& end, const Constraint &constraint ) const
345 {
346  QPolygonF poly;
347  switch ( constraint.relationType() ) {
348  case Constraint::FinishStart:
349  poly = finishStartLine( start, end ) + finishStartArrow( start, end );
350  break;
351  case Constraint::FinishFinish:
352  poly = finishFinishLine( start, end ) + finishFinishArrow( start, end );
353  break;
354  case Constraint::StartStart:
355  poly = startStartLine( start, end ) + startStartArrow( start, end );
356  break;
357  case Constraint::StartFinish:
358  poly = startFinishLine( start, end ) + startFinishArrow( start, end );
359  break;
360  }
361  return poly.boundingRect().adjusted( -PW, -PW, PW, PW );
362 }
363 
364 
365 
367  const QPointF& start, const QPointF& end, const Constraint &constraint )
368 {
369  //qDebug()<<"ItemDelegate::paintConstraintItem"<<start<<end<<constraint;
370  switch ( constraint.relationType() ) {
371  case Constraint::FinishStart:
372  paintFinishStartConstraint( painter, opt, start, end, constraint );
373  break;
374  case Constraint::FinishFinish:
375  paintFinishFinishConstraint( painter, opt, start, end, constraint );
376  break;
377  case Constraint::StartStart:
378  paintStartStartConstraint( painter, opt, start, end, constraint );
379  break;
380  case Constraint::StartFinish:
381  paintStartFinishConstraint( painter, opt, start, end, constraint );
382  break;
383  }
384 }
385 
386 void ItemDelegate::paintFinishStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
387 {
388  Q_UNUSED( opt );
389 
390  const QPen pen = d->constraintPen( start, end, constraint, opt );
391 
392  painter->setPen( pen );
393  painter->setBrush( pen.color() );
394 
395  painter->drawPolyline( finishStartLine( start, end ) );
396  painter->drawPolygon( finishStartArrow( start, end ) );
397 }
398 
399 QPolygonF ItemDelegate::finishStartLine( const QPointF& start, const QPointF& end ) const
400 {
401  QPolygonF poly;
402  qreal midx = end.x() - TURN;
403  qreal midy = ( end.y()-start.y() )/2. + start.y();
404 
405  if ( start.x() > end.x()-TURN ) {
406  poly << start
407  << QPointF( start.x()+TURN, start.y() )
408  << QPointF( start.x()+TURN, midy )
409  << QPointF( end.x()-TURN, midy )
410  << QPointF( end.x()-TURN, end.y() )
411  << end;
412  } else {
413  poly << start
414  << QPointF( midx, start.y() )
415  << QPointF( midx, end.y() )
416  << end;
417  }
418  return poly;
419 }
420 
421 QPolygonF ItemDelegate::finishStartArrow( const QPointF& start, const QPointF& end ) const
422 {
423  Q_UNUSED(start);
424 
425  QPolygonF poly;
426  poly << end
427  << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
428  << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
429  return poly;
430 }
431 
432 void ItemDelegate::paintFinishFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
433 {
434  Q_UNUSED( opt );
435 
436  const QPen pen = d->constraintPen( start, end, constraint, opt );
437 
438  painter->setPen( pen );
439  painter->setBrush( pen.color() );
440 
441  painter->drawPolyline( finishFinishLine( start, end ) );
442  painter->drawPolygon( finishFinishArrow( start, end ) );
443 }
444 
445 QPolygonF ItemDelegate::finishFinishLine( const QPointF& start, const QPointF& end ) const
446 {
447  QPolygonF poly;
448  qreal midx = end.x() + TURN;
449  qreal midy = ( end.y()-start.y() )/2. + start.y();
450 
451  if ( start.x() > end.x()+TURN ) {
452  poly << start
453  << QPointF( start.x()+TURN, start.y() )
454  << QPointF( start.x()+TURN, end.y() )
455  << end;
456  } else {
457  poly << start
458  << QPointF( midx, start.y() )
459  << QPointF( midx, midy )
460  << QPointF( end.x()+TURN, midy )
461  << QPointF( end.x()+TURN, end.y() )
462  << end;
463  }
464  return poly;
465 }
466 
467 QPolygonF ItemDelegate::finishFinishArrow( const QPointF& start, const QPointF& end ) const
468 {
469  Q_UNUSED(start);
470 
471  QPolygonF poly;
472  poly << end
473  << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
474  << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
475  return poly;
476 }
477 
478 void ItemDelegate::paintStartStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
479 {
480  Q_UNUSED( opt );
481 
482  const QPen pen = d->constraintPen( start, end, constraint, opt );
483 
484  painter->setPen( pen );
485  painter->setBrush( pen.color() );
486 
487  painter->drawPolyline( startStartLine( start, end ) );
488  painter->drawPolygon( startStartArrow( start, end ) );
489 
490 }
491 
492 QPolygonF ItemDelegate::startStartLine( const QPointF& start, const QPointF& end ) const
493 {
494  Q_UNUSED(start);
495 
496  QPolygonF poly;
497 
498  if ( start.x() > end.x() ) {
499  poly << start
500  << QPointF( end.x()-TURN, start.y() )
501  << QPointF( end.x()-TURN, end.y() )
502  << end;
503  } else {
504  poly << start
505  << QPointF( start.x()-TURN, start.y() )
506  << QPointF( start.x()-TURN, end.y() )
507  << QPointF( end.x()-TURN, end.y() )
508  << end;
509  }
510  return poly;
511 }
512 
513 QPolygonF ItemDelegate::startStartArrow( const QPointF& start, const QPointF& end ) const
514 {
515  Q_UNUSED(start);
516 
517  QPolygonF poly;
518  poly << end
519  << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
520  << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
521  return poly;
522 }
523 
524 void ItemDelegate::paintStartFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
525 {
526  Q_UNUSED( opt );
527 
528  const QPen pen = d->constraintPen( start, end, constraint, opt);
529 
530  painter->setPen( pen );
531  painter->setBrush( pen.color() );
532 
533  painter->drawPolyline( startFinishLine( start, end ) );
534  painter->drawPolygon( startFinishArrow( start, end ) );
535 }
536 
537 QPolygonF ItemDelegate::startFinishLine( const QPointF& start, const QPointF& end ) const
538 {
539  Q_UNUSED(start);
540 
541  QPolygonF poly;
542  qreal midx = end.x() + TURN;
543  qreal midy = ( end.y()-start.y() )/2. + start.y();
544 
545  if ( start.x()-TURN > end.x()+TURN ) {
546  poly << start
547  << QPointF( midx, start.y() )
548  << QPointF( midx, end.y() )
549  << end;
550  } else {
551  poly << start
552  << QPointF( start.x()-TURN, start.y() )
553  << QPointF( start.x()-TURN, midy )
554  << QPointF( midx, midy )
555  << QPointF( end.x()+TURN, end.y() )
556  << end;
557  }
558  return poly;
559 }
560 
561 QPolygonF ItemDelegate::startFinishArrow( const QPointF& start, const QPointF& end ) const
562 {
563  Q_UNUSED(start);
564 
565  QPolygonF poly;
566  poly << end
567  << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
568  << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
569  return poly;
570 }
571 
572 
573 #include "moc_kganttitemdelegate.cpp"
The item type.
Definition: kganttglobal.h:217
RelationType relationType() const
void fillRect(const QRectF &rectangle, const QBrush &brush)
qreal x() const const
qreal y() const const
void closeSubpath()
QStyleOption subclass for gantt items.
void drawPolyline(const QPointF *points, int pointCount)
virtual InteractionState interactionStateFor(const QPointF &pos, const StyleOptionGanttItem &opt, const QModelIndex &idx) const
Start time (or other start value) for a gantt item.
Definition: kganttglobal.h:214
virtual void paintConstraintItem(QPainter *p, const QStyleOptionGraphicsItem &opt, const QPointF &start, const QPointF &end, const Constraint &constraint)
Left
bool contains(const QRectF &rectangle) const const
void save()
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
void setAlpha(int alpha)
void moveTo(const QPointF &point)
qreal top() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
qreal left() const const
void setBrushOrigin(int x, int y)
typedef Alignment
void setDefaultPen(ItemType type, const QPen &pen)
bool isNull() const const
void setHeight(qreal height)
A class used to represent a dependency.
void drawRect(const QRectF &rectangle)
virtual QString toolTip(const QModelIndex &idx) const
QColor color() const const
bool isValid() const const
qreal x() const const
qreal y() const const
void translate(qreal dx, qreal dy)
void setDefaultBrush(ItemType type, const QBrush &brush)
int toInt(bool *ok) const const
void setPen(const QColor &color)
void lineTo(const QPointF &endPoint)
QPalette palette()
virtual QRectF constraintBoundingRect(const QPointF &start, const QPointF &end, const Constraint &constraint) const
ToolTipRole
QPointF topLeft() const const
End time (or other end value) for a gantt item.
Definition: kganttglobal.h:215
void setBrush(const QBrush &brush)
void drawText(const QPointF &position, const QString &text)
QPointF topRight() const const
virtual QVariant data(const QModelIndex &index, int role) const const =0
Global namespace.
void setColor(const QColor &color)
QBrush defaultBrush(ItemType type) const
qreal right() const const
virtual void paintGanttItem(QPainter *p, const StyleOptionGanttItem &opt, const QModelIndex &idx)
Right
void restore()
bool isValid() const const
QDataStream & operator<<(QDataStream &out, const KDateTime::Spec &spec)
QRectF boundingRect() const const
qreal width() const const
const QAbstractItemModel * model() const const
void drawPath(const QPainterPath &path)
int width() const const
void setWidth(int width)
QRectF translated(qreal dx, qreal dy) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QFontMetrics fontMetrics()
void translate(const QPointF &offset)
ItemDelegate(QObject *parent=nullptr)
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const const
qreal height() const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
QVariant data(int role) const
void setY(qreal y)
qreal toReal(bool *ok) const const
void quadTo(const QPointF &c, const QPointF &endPoint)
Contains KGantt macros.
const QPen & pen() const const
QString toString() const const
QPen defaultPen(ItemType type) const
virtual Span itemBoundingSpan(const StyleOptionGanttItem &opt, const QModelIndex &idx) const
A class representing a start point and a length.
Definition: kganttglobal.h:240
Task completion percentage used by Task items. Should be an integer og a qreal between 0 and 100...
Definition: kganttglobal.h:216
ItemIsEditable
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Dec 5 2021 22:32:42 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.