00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "kdganttitemdelegate_p.h"
00024 #include "kdganttglobal.h"
00025 #include "kdganttstyleoptionganttitem.h"
00026 #include "kdganttconstraint.h"
00027
00028 #include <QPainter>
00029 #include <QPainterPath>
00030 #include <QPen>
00031 #include <QModelIndex>
00032 #include <QAbstractItemModel>
00033 #include <QApplication>
00034
00035 #ifndef QT_NO_DEBUG_STREAM
00036
00037 #define PRINT_INTERACTIONSTATE(x) \
00038 case x: dbg << #x; break;
00039
00040
00041 QDebug operator<<( QDebug dbg, KDGantt::ItemDelegate::InteractionState state )
00042 {
00043 switch( state ) {
00044 PRINT_INTERACTIONSTATE( KDGantt::ItemDelegate::State_None );
00045 PRINT_INTERACTIONSTATE( KDGantt::ItemDelegate::State_Move );
00046 PRINT_INTERACTIONSTATE( KDGantt::ItemDelegate::State_ExtendLeft );
00047 PRINT_INTERACTIONSTATE( KDGantt::ItemDelegate::State_ExtendRight );
00048 default:
00049 break;
00050 }
00051 return dbg;
00052 }
00053
00054 #undef PRINT_INTERACTIONSTATE
00055
00056 #endif
00057
00058 using namespace KDGantt;
00059
00073 ItemDelegate::Private::Private()
00074 {
00075
00076 QLinearGradient taskgrad( 0., 0., 0., QApplication::fontMetrics().height() );
00077 taskgrad.setColorAt( 0., Qt::green );
00078 taskgrad.setColorAt( 1., Qt::darkGreen );
00079
00080 QLinearGradient summarygrad( 0., 0., 0., QApplication::fontMetrics().height() );
00081 summarygrad.setColorAt( 0., Qt::blue );
00082 summarygrad.setColorAt( 1., Qt::darkBlue );
00083
00084 QLinearGradient eventgrad( 0., 0., 0., QApplication::fontMetrics().height() );
00085 eventgrad.setColorAt( 0., Qt::red );
00086 eventgrad.setColorAt( 1., Qt::darkRed );
00087
00088 defaultbrush[TypeTask] = taskgrad;
00089 defaultbrush[TypeSummary] = summarygrad;
00090 defaultbrush[TypeEvent] = eventgrad;
00091
00092
00093 QPen pen( Qt::black, 1. );
00094
00095 defaultpen[TypeTask] = pen;
00096 defaultpen[TypeSummary] = pen;
00097 defaultpen[TypeEvent] = pen;
00098 }
00099
00100 QPen ItemDelegate::Private::constraintPen( const QPointF& start, const QPointF& end, const Constraint& constraint )
00101 {
00102 QPen pen;
00103 QVariant dataPen;
00104
00105
00106 if ( start.x() <= end.x() ) {
00107 pen = QPen( Qt::black );
00108 dataPen = constraint.data( Constraint::ValidConstraintPen );
00109 } else {
00110 pen = QPen( Qt::red );
00111 dataPen = constraint.data( Constraint::InvalidConstraintPen );
00112 }
00113
00114
00115 if( qVariantCanConvert< QPen >( dataPen ) ) {
00116 pen = qVariantValue< QPen >( dataPen );
00117 }
00118
00119 return pen;
00120 }
00121
00123 ItemDelegate::ItemDelegate( QObject* parent )
00124 : QItemDelegate( parent ), _d( new Private )
00125 {
00126 }
00127
00129 ItemDelegate::~ItemDelegate()
00130 {
00131 delete _d;
00132 }
00133
00134 #define d d_func()
00135
00142 void ItemDelegate::setDefaultBrush( ItemType type, const QBrush& brush )
00143 {
00144 d->defaultbrush[type] = brush;
00145 }
00146
00151 QBrush ItemDelegate::defaultBrush( ItemType type ) const
00152 {
00153 return d->defaultbrush[type];
00154 }
00155
00162 void ItemDelegate::setDefaultPen( ItemType type, const QPen& pen )
00163 {
00164 d->defaultpen[type]=pen;
00165 }
00166
00171 QPen ItemDelegate::defaultPen( ItemType type ) const
00172 {
00173 return d->defaultpen[type];
00174 }
00175
00178 QString ItemDelegate::toolTip( const QModelIndex &idx ) const
00179 {
00180 if ( !idx.isValid() ) return QString();
00181
00182 const QAbstractItemModel* model = idx.model();
00183 if ( !model ) return QString();
00184 QString tip = model->data( idx, Qt::ToolTipRole ).toString();
00185 if ( !tip.isNull() ) return tip;
00186 else return tr( "%1 -> %2: %3" )
00187 .arg( model->data( idx, StartTimeRole ).toString() )
00188 .arg( model->data( idx, EndTimeRole ).toString() )
00189 .arg( model->data( idx, Qt::DisplayRole ).toString() );
00190 }
00191
00200 Span ItemDelegate::itemBoundingSpan( const StyleOptionGanttItem& opt,
00201 const QModelIndex& idx ) const
00202 {
00203 if ( !idx.isValid() ) return Span();
00204
00205 const QString txt = idx.model()->data( idx, Qt::DisplayRole ).toString();
00206 const int typ = idx.model()->data( idx, ItemTypeRole ).toInt();
00207 QRectF itemRect = opt.itemRect;
00208
00209
00210 if ( typ == TypeEvent ) {
00211 itemRect = QRectF( itemRect.left()-itemRect.height()/2.,
00212 itemRect.top(),
00213 itemRect.height(),
00214 itemRect.height() );
00215 }
00216
00217 int tw = opt.fontMetrics.width( txt );
00218 tw += static_cast<int>( itemRect.height()/2. );
00219 Span s;
00220 switch ( opt.displayPosition ) {
00221 case StyleOptionGanttItem::Left:
00222 s = Span( itemRect.left()-tw, itemRect.width()+tw ); break;
00223 case StyleOptionGanttItem::Right:
00224 s = Span( itemRect.left(), itemRect.width()+tw ); break;
00225 case StyleOptionGanttItem::Hidden:
00226 case StyleOptionGanttItem::Center:
00227 s = Span( itemRect.left(), itemRect.width() ); break;
00228 }
00229 return s;
00230 }
00231
00238 ItemDelegate::InteractionState ItemDelegate::interactionStateFor( const QPointF& pos,
00239 const StyleOptionGanttItem& opt,
00240 const QModelIndex& idx ) const
00241 {
00242 if ( !idx.isValid() ) return State_None;
00243 if ( !( idx.model()->flags( idx ) & Qt::ItemIsEditable ) ) return State_None;
00244
00245 const int typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
00246
00247 QRectF itemRect( opt.itemRect );
00248
00249
00250
00251 if ( typ == TypeEvent )
00252 itemRect = QRectF( itemRect.topLeft() - QPointF( itemRect.height() / 2.0, 0 ), QSizeF( itemRect.height(),
00253 itemRect.height() ) );
00254
00255 if ( typ == TypeNone || typ == TypeSummary ) return State_None;
00256 if ( !itemRect.contains(pos) ) return State_None;
00257 if ( typ == TypeEvent )
00258 return State_Move;
00259
00260 qreal delta = 5.;
00261 if ( itemRect.width() < 15 ) delta = 1.;
00262 if( pos.x() >= itemRect.left() && pos.x() < itemRect.left()+delta ) {
00263 return State_ExtendLeft;
00264 } else if( pos.x() <= itemRect.right() && pos.x() > itemRect.right()-delta ) {
00265 return State_ExtendRight;
00266 } else {
00267 return State_Move;
00268 }
00269 }
00270
00273 void ItemDelegate::paintGanttItem( QPainter* painter,
00274 const StyleOptionGanttItem& opt,
00275 const QModelIndex& idx )
00276 {
00277 if ( !idx.isValid() ) return;
00278 const ItemType typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
00279 const QString& txt = opt.text;
00280 QRectF itemRect = opt.itemRect;
00281 QRectF boundingRect = opt.boundingRect;
00282 boundingRect.setY( itemRect.y() );
00283 boundingRect.setHeight( itemRect.height() );
00284
00285
00286
00287
00288 painter->save();
00289
00290 QPen pen = defaultPen( typ );
00291 if ( opt.state & QStyle::State_Selected ) pen.setWidth( 2*pen.width() );
00292 painter->setPen( pen );
00293 painter->setBrush( defaultBrush( typ ) );
00294
00295 bool drawText = true;
00296 qreal pw = painter->pen().width()/2.;
00297 switch( typ ) {
00298 case TypeTask:
00299 if ( itemRect.isValid() ) {
00300
00301 qreal pw = painter->pen().width()/2.;
00302 pw-=1;
00303 QRectF r = itemRect;
00304 r.translate( 0., r.height()/6. );
00305 r.setHeight( 2.*r.height()/3. );
00306 painter->setBrushOrigin( itemRect.topLeft() );
00307 painter->save();
00308 painter->translate( 0.5, 0.5 );
00309 painter->drawRect( r );
00310 bool ok;
00311 qreal completion = idx.model()->data( idx, KDGantt::TaskCompletionRole ).toDouble( &ok );
00312 if ( ok ) {
00313 qreal h = r.height();
00314 QRectF cr( r.x(), r.y()+h/4.,
00315 r.width()*completion/100., h/2.+1 );
00316 QColor compcolor( painter->pen().color() );
00317 compcolor.setAlpha( 150 );
00318 painter->fillRect( cr, compcolor );
00319 }
00320 painter->restore();
00321 }
00322 break;
00323 case TypeSummary:
00324 if ( opt.itemRect.isValid() ) {
00325
00326 pw-=1;
00327 const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw );
00328 QPainterPath path;
00329 const qreal deltaY = r.height()/2.;
00330 const qreal deltaXBezierControl = .25*qMin( r.width(), r.height() );
00331 const qreal deltaX = qMin( r.width()/2., r.height() );
00332 path.moveTo( r.topLeft() );
00333 path.lineTo( r.topRight() );
00334 path.lineTo( QPointF( r.right(), r.top() + 2.*deltaY ) );
00335
00336 path.quadTo( QPointF( r.right()-deltaXBezierControl, r.top() + deltaY ), QPointF( r.right()-deltaX, r.top() + deltaY ) );
00337
00338 path.lineTo( QPointF( r.left() + deltaX, r.top() + deltaY ) );
00339 path.quadTo( QPointF( r.left()+deltaXBezierControl, r.top() + deltaY ), QPointF( r.left(), r.top() + 2.*deltaY ) );
00340 path.closeSubpath();
00341 painter->setBrushOrigin( itemRect.topLeft() );
00342 painter->save();
00343 painter->translate( 0.5, 0.5 );
00344 painter->drawPath( path );
00345 painter->restore();
00346 }
00347 break;
00348 case TypeEvent:
00349
00350 if ( opt.boundingRect.isValid() ) {
00351 const qreal pw = painter->pen().width() / 2. - 1;
00352 const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw ).translated( -opt.itemRect.height()/2, 0 );
00353 QPainterPath path;
00354 const qreal delta = static_cast< int >( r.height() / 2 );
00355 path.moveTo( delta, 0. );
00356 path.lineTo( 2.*delta, delta );
00357 path.lineTo( delta, 2.*delta );
00358 path.lineTo( 0., delta );
00359 path.closeSubpath();
00360 painter->save();
00361 painter->translate( r.topLeft() );
00362 painter->translate( 0, 0.5 );
00363 painter->drawPath( path );
00364 painter->restore();
00365 #if 0
00366 painter->setBrush( Qt::NoBrush );
00367 painter->setPen( Qt::black );
00368 painter->drawRect( opt.boundingRect );
00369 painter->setPen( Qt::red );
00370 painter->drawRect( r );
00371 #endif
00372 }
00373 break;
00374 default:
00375 drawText = false;
00376 break;
00377 }
00378
00379 Qt::Alignment ta;
00380 switch( opt.displayPosition ) {
00381 case StyleOptionGanttItem::Left: ta = Qt::AlignLeft; break;
00382 case StyleOptionGanttItem::Right: ta = Qt::AlignRight; break;
00383 case StyleOptionGanttItem::Center: ta = Qt::AlignCenter; break;
00384 case StyleOptionGanttItem::Hidden: drawText = false; break;
00385 }
00386 if ( drawText ) {
00387 painter->drawText( boundingRect, ta | Qt::AlignVCenter, txt );
00388 }
00389
00390 painter->restore();
00391 }
00392
00393 static const qreal TURN = 10.;
00394 static const qreal PW = 1.5;
00395
00400 QRectF ItemDelegate::constraintBoundingRect( const QPointF& start, const QPointF& end, const Constraint &constraint ) const
00401 {
00402 QPolygonF poly;
00403 switch ( constraint.relationType() ) {
00404 case Constraint::FinishStart:
00405 poly = finishStartLine( start, end ) + finishStartArrow( start, end );
00406 break;
00407 case Constraint::FinishFinish:
00408 poly = finishFinishLine( start, end ) + finishFinishArrow( start, end );
00409 break;
00410 case Constraint::StartStart:
00411 poly = startStartLine( start, end ) + startStartArrow( start, end );
00412 break;
00413 case Constraint::StartFinish:
00414 poly = startFinishLine( start, end ) + startFinishArrow( start, end );
00415 break;
00416 default:
00417 break;
00418 }
00419 return poly.boundingRect().adjusted( -PW, -PW, PW, PW );
00420 }
00421
00422
00428 void ItemDelegate::paintConstraintItem( QPainter* painter, const QStyleOptionGraphicsItem& opt,
00429 const QPointF& start, const QPointF& end, const Constraint &constraint )
00430 {
00431
00432 switch ( constraint.relationType() ) {
00433 case Constraint::FinishStart:
00434 paintFinishStartConstraint( painter, opt, start, end, constraint );
00435 break;
00436 case Constraint::FinishFinish:
00437 paintFinishFinishConstraint( painter, opt, start, end, constraint );
00438 break;
00439 case Constraint::StartStart:
00440 paintStartStartConstraint( painter, opt, start, end, constraint );
00441 break;
00442 case Constraint::StartFinish:
00443 paintStartFinishConstraint( painter, opt, start, end, constraint );
00444 break;
00445 default:
00446 break;
00447 }
00448 }
00449
00450 void ItemDelegate::paintFinishStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00451 {
00452 Q_UNUSED( opt );
00453
00454 const QPen pen = d->constraintPen( start, end, constraint );
00455
00456 painter->setPen( pen );
00457 painter->setBrush( pen.color() );
00458
00459 painter->drawPolyline( finishStartLine( start, end ) );
00460 painter->drawPolygon( finishStartArrow( start, end ) );
00461 }
00462
00463 QPolygonF ItemDelegate::finishStartLine( const QPointF& start, const QPointF& end ) const
00464 {
00465 QPolygonF poly;
00466 qreal midx = end.x() - TURN;
00467 qreal midy = ( end.y()-start.y() )/2. + start.y();
00468
00469 if ( start.x() > end.x()-TURN ) {
00470 poly << start
00471 << QPointF( start.x()+TURN, start.y() )
00472 << QPointF( start.x()+TURN, midy )
00473 << QPointF( end.x()-TURN, midy )
00474 << QPointF( end.x()-TURN, end.y() )
00475 << end;
00476 } else {
00477 poly << start
00478 << QPointF( midx, start.y() )
00479 << QPointF( midx, end.y() )
00480 << end;
00481 }
00482 return poly;
00483 }
00484
00485 QPolygonF ItemDelegate::finishStartArrow( const QPointF& start, const QPointF& end ) const
00486 {
00487 Q_UNUSED(start);
00488
00489 QPolygonF poly;
00490 poly << end
00491 << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
00492 << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
00493 return poly;
00494 }
00495
00496 void ItemDelegate::paintFinishFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00497 {
00498 Q_UNUSED( opt );
00499
00500 const QPen pen = d->constraintPen( start, end, constraint );
00501
00502 painter->setPen( pen );
00503 painter->setBrush( pen.color() );
00504
00505 painter->drawPolyline( finishFinishLine( start, end ) );
00506 painter->drawPolygon( finishFinishArrow( start, end ) );
00507 }
00508
00509 QPolygonF ItemDelegate::finishFinishLine( const QPointF& start, const QPointF& end ) const
00510 {
00511 QPolygonF poly;
00512 qreal midx = end.x() + TURN;
00513 qreal midy = ( end.y()-start.y() )/2. + start.y();
00514
00515 if ( start.x() > end.x()+TURN ) {
00516 poly << start
00517 << QPointF( start.x()+TURN, start.y() )
00518 << QPointF( start.x()+TURN, end.y() )
00519 << end;
00520 } else {
00521 poly << start
00522 << QPointF( midx, start.y() )
00523 << QPointF( midx, midy )
00524 << QPointF( end.x()+TURN, midy )
00525 << QPointF( end.x()+TURN, end.y() )
00526 << end;
00527 }
00528 return poly;
00529 }
00530
00531 QPolygonF ItemDelegate::finishFinishArrow( const QPointF& start, const QPointF& end ) const
00532 {
00533 Q_UNUSED(start);
00534
00535 QPolygonF poly;
00536 poly << end
00537 << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
00538 << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
00539 return poly;
00540 }
00541
00542 void ItemDelegate::paintStartStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00543 {
00544 Q_UNUSED( opt );
00545
00546 const QPen pen = d->constraintPen( start, end, constraint );
00547
00548 painter->setPen( pen );
00549 painter->setBrush( pen.color() );
00550
00551 painter->drawPolyline( startStartLine( start, end ) );
00552 painter->drawPolygon( startStartArrow( start, end ) );
00553
00554 }
00555
00556 QPolygonF ItemDelegate::startStartLine( const QPointF& start, const QPointF& end ) const
00557 {
00558 Q_UNUSED(start);
00559
00560 QPolygonF poly;
00561
00562 if ( start.x() > end.x() ) {
00563 poly << start
00564 << QPointF( end.x()-TURN, start.y() )
00565 << QPointF( end.x()-TURN, end.y() )
00566 << end;
00567 } else {
00568 poly << start
00569 << QPointF( start.x()-TURN, start.y() )
00570 << QPointF( start.x()-TURN, end.y() )
00571 << QPointF( end.x()-TURN, end.y() )
00572 << end;
00573 }
00574 return poly;
00575 }
00576
00577 QPolygonF ItemDelegate::startStartArrow( const QPointF& start, const QPointF& end ) const
00578 {
00579 Q_UNUSED(start);
00580
00581 QPolygonF poly;
00582 poly << end
00583 << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
00584 << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
00585 return poly;
00586 }
00587
00588 void ItemDelegate::paintStartFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00589 {
00590 Q_UNUSED( opt );
00591
00592 const QPen pen = d->constraintPen( start, end, constraint );
00593
00594 painter->setPen( pen );
00595 painter->setBrush( pen.color() );
00596
00597 painter->drawPolyline( startFinishLine( start, end ) );
00598 painter->drawPolygon( startFinishArrow( start, end ) );
00599 }
00600
00601 QPolygonF ItemDelegate::startFinishLine( const QPointF& start, const QPointF& end ) const
00602 {
00603 Q_UNUSED(start);
00604
00605 QPolygonF poly;
00606 qreal midx = end.x() + TURN;
00607 qreal midy = ( end.y()-start.y() )/2. + start.y();
00608
00609 if ( start.x()-TURN > end.x()+TURN ) {
00610 poly << start
00611 << QPointF( midx, start.y() )
00612 << QPointF( midx, end.y() )
00613 << end;
00614 } else {
00615 poly << start
00616 << QPointF( start.x()-TURN, start.y() )
00617 << QPointF( start.x()-TURN, midy )
00618 << QPointF( midx, midy )
00619 << QPointF( end.x()+TURN, end.y() )
00620 << end;
00621 }
00622 return poly;
00623 }
00624
00625 QPolygonF ItemDelegate::startFinishArrow( const QPointF& start, const QPointF& end ) const
00626 {
00627 Q_UNUSED(start);
00628
00629 QPolygonF poly;
00630 poly << end
00631 << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
00632 << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
00633 return poly;
00634 }
00635
00636
00637 #include "moc_kdganttitemdelegate.cpp"