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

KDE's Doxygen guidelines are available online.