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
00101 ItemDelegate::ItemDelegate( QObject* parent )
00102 : QItemDelegate( parent ), _d( new Private )
00103 {
00104 }
00105
00107 ItemDelegate::~ItemDelegate()
00108 {
00109 delete _d;
00110 }
00111
00112 #define d d_func()
00113
00120 void ItemDelegate::setDefaultBrush( ItemType type, const QBrush& brush )
00121 {
00122 d->defaultbrush[type] = brush;
00123 }
00124
00129 QBrush ItemDelegate::defaultBrush( ItemType type ) const
00130 {
00131 return d->defaultbrush[type];
00132 }
00133
00140 void ItemDelegate::setDefaultPen( ItemType type, const QPen& pen )
00141 {
00142 d->defaultpen[type]=pen;
00143 }
00144
00149 QPen ItemDelegate::defaultPen( ItemType type ) const
00150 {
00151 return d->defaultpen[type];
00152 }
00153
00156 QString ItemDelegate::toolTip( const QModelIndex &idx ) const
00157 {
00158 if ( !idx.isValid() ) return QString();
00159
00160 const QAbstractItemModel* model = idx.model();
00161 if ( !model ) return QString();
00162 QString tip = model->data( idx, Qt::ToolTipRole ).toString();
00163 if ( !tip.isNull() ) return tip;
00164 else return tr( "%1 -> %2: %3" )
00165 .arg( model->data( idx, StartTimeRole ).toString() )
00166 .arg( model->data( idx, EndTimeRole ).toString() )
00167 .arg( model->data( idx, Qt::DisplayRole ).toString() );
00168
00169 }
00170
00179 Span ItemDelegate::itemBoundingSpan( const StyleOptionGanttItem& opt,
00180 const QModelIndex& idx ) const
00181 {
00182
00183 if ( !idx.isValid() ) return Span();
00184
00185 QString txt = idx.model()->data( idx, Qt::DisplayRole ).toString();
00186 int typ = idx.model()->data( idx, ItemTypeRole ).toInt();
00187 QRectF itemRect = opt.itemRect;
00188
00189
00190 if ( typ == TypeEvent ) itemRect = QRectF( itemRect.left()-itemRect.height()/2.,
00191 itemRect.top(),
00192 itemRect.height(),
00193 itemRect.height() );
00194
00195 int tw = opt.fontMetrics.width( txt );
00196 tw += static_cast<int>( itemRect.height()/2. );
00197 switch ( opt.displayPosition ) {
00198 case StyleOptionGanttItem::Left:
00199 return Span( itemRect.left()-tw, itemRect.width()+tw );
00200 case StyleOptionGanttItem::Right:
00201 return Span( itemRect.left(), itemRect.width()+tw );
00202 case StyleOptionGanttItem::Center:
00203 return Span( itemRect.left(), itemRect.width() );
00204 }
00205 return Span();
00206 }
00207
00214 ItemDelegate::InteractionState ItemDelegate::interactionStateFor( const QPointF& pos,
00215 const StyleOptionGanttItem& opt,
00216 const QModelIndex& idx ) const
00217 {
00218 if ( !idx.isValid() ) return State_None;
00219 if ( !( idx.model()->flags( idx ) & Qt::ItemIsEditable ) ) return State_None;
00220
00221 int typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
00222 if ( typ == TypeNone || typ == TypeSummary ) return State_None;
00223 if ( typ == TypeEvent ) return State_Move;
00224 if ( !opt.itemRect.contains(pos) ) return State_None;
00225
00226 qreal delta = 5.;
00227 if ( opt.itemRect.width() < 15 ) delta = 1.;
00228 if( pos.x() >= opt.itemRect.left() && pos.x() < opt.itemRect.left()+delta ) {
00229 return State_ExtendLeft;
00230 } else if( pos.x() <= opt.itemRect.right() && pos.x() > opt.itemRect.right()-delta ) {
00231 return State_ExtendRight;
00232 } else {
00233 return State_Move;
00234 }
00235 }
00236
00239 void ItemDelegate::paintGanttItem( QPainter* painter,
00240 const StyleOptionGanttItem& opt,
00241 const QModelIndex& idx )
00242 {
00243 if ( !idx.isValid() ) return;
00244 const ItemType typ = static_cast<ItemType>( idx.model()->data( idx, ItemTypeRole ).toInt() );
00245 const QString& txt = opt.text;
00246 QRectF itemRect = opt.itemRect;
00247 QRectF boundingRect = opt.boundingRect;
00248 boundingRect.setY( itemRect.y() );
00249 boundingRect.setHeight( itemRect.height() );
00250
00251
00252 painter->save();
00253
00254 QPen pen = defaultPen( typ );
00255 if ( opt.state & QStyle::State_Selected ) pen.setWidth( 2*pen.width() );
00256 painter->setPen( pen );
00257 painter->setBrush( defaultBrush( typ ) );
00258
00259 qreal pw = painter->pen().width()/2.;
00260 switch( typ ) {
00261 case TypeTask:
00262 if ( itemRect.isValid() ) {
00263
00264 qreal pw = painter->pen().width()/2.;
00265 pw-=1;
00266 QRectF r = itemRect;
00267 r.translate( 0., r.height()/6. );
00268 r.setHeight( 2.*r.height()/3. );
00269 painter->setBrushOrigin( itemRect.topLeft() );
00270 painter->save();
00271 painter->translate( 0.5, 0.5 );
00272 painter->drawRect( r );
00273 bool ok;
00274 qreal completion = idx.model()->data( idx, KDGantt::TaskCompletionRole ).toDouble( &ok );
00275 if ( ok ) {
00276 qreal h = r.height();
00277 QRectF cr( r.x(), r.y()+h/4. + 1,
00278 r.width()*completion/100., h/2. - 2 );
00279 painter->fillRect( cr, painter->pen().brush() );
00280 }
00281 painter->restore();
00282 Qt::Alignment ta;
00283 switch( opt.displayPosition ) {
00284 case StyleOptionGanttItem::Left: ta = Qt::AlignLeft; break;
00285 case StyleOptionGanttItem::Right: ta = Qt::AlignRight; break;
00286 case StyleOptionGanttItem::Center: ta = Qt::AlignCenter; break;
00287 }
00288 painter->drawText( boundingRect, ta, txt );
00289 }
00290 break;
00291 case TypeSummary:
00292 if ( opt.itemRect.isValid() ) {
00293
00294 pw-=1;
00295 const QRectF r = QRectF( opt.itemRect ).adjusted( -pw, -pw, pw, pw );
00296 QPainterPath path;
00297 const qreal deltaY = r.height()/2.;
00298 const qreal deltaX = qMin( r.width()/qreal(2), deltaY );
00299 path.moveTo( r.topLeft() );
00300 path.lineTo( r.topRight() );
00301 path.lineTo( QPointF( r.right(), r.top() + 2.*deltaY ) );
00302
00303 path.quadTo( QPointF( r.right()-.5*deltaX, r.top() + deltaY ), QPointF( r.right()-2.*deltaX, r.top() + deltaY ) );
00304
00305 path.lineTo( QPointF( r.left() + 2.*deltaX, r.top() + deltaY ) );
00306 path.quadTo( QPointF( r.left()+.5*deltaX, r.top() + deltaY ), QPointF( r.left(), r.top() + 2.*deltaY ) );
00307 path.closeSubpath();
00308 painter->setBrushOrigin( itemRect.topLeft() );
00309 painter->save();
00310 painter->translate( 0.5, 0.5 );
00311 painter->drawPath( path );
00312 painter->restore();
00313 Qt::Alignment ta;
00314 switch( opt.displayPosition ) {
00315 case StyleOptionGanttItem::Left: ta = Qt::AlignLeft; break;
00316 case StyleOptionGanttItem::Right: ta = Qt::AlignRight; break;
00317 case StyleOptionGanttItem::Center: ta = Qt::AlignCenter; break;
00318 }
00319 painter->drawText( boundingRect, ta | Qt::AlignVCenter, txt );
00320 }
00321 break;
00322 case TypeEvent:
00323
00324 if ( opt.boundingRect.isValid() ) {
00325 const qreal pw = painter->pen().width() / 2. - 1;
00326 const QRectF r = QRectF( opt.rect ).adjusted( -pw, -pw, pw, pw );
00327 QPainterPath path;
00328 const qreal delta = static_cast< int >( r.height() / 2 );
00329 path.moveTo( delta, 0. );
00330 path.lineTo( 2.*delta, delta );
00331 path.lineTo( delta, 2.*delta );
00332 path.lineTo( 0., delta );
00333 path.closeSubpath();
00334 painter->save();
00335 painter->translate( r.topLeft() );
00336 painter->translate( 0.5, 0.5 );
00337 painter->drawPath( path );
00338 painter->restore();
00339 Qt::Alignment ta;
00340 switch( opt.displayPosition ) {
00341 case StyleOptionGanttItem::Left: ta = Qt::AlignLeft; break;
00342 case StyleOptionGanttItem::Right: ta = Qt::AlignRight; break;
00343 case StyleOptionGanttItem::Center: ta = Qt::AlignCenter; break;
00344 }
00345 painter->drawText( boundingRect, ta | Qt::AlignVCenter, txt );
00346 }
00347 break;
00348 default:
00349 break;
00350 }
00351 painter->restore();
00352 }
00353
00354 static const qreal TURN = 10.;
00355 static const qreal PW = 1.5;
00356
00361 QRectF ItemDelegate::constraintBoundingRect( const QPointF& start, const QPointF& end, const Constraint &constraint ) const
00362 {
00363 QPolygonF poly;
00364 switch ( constraint.relationType() ) {
00365 case Constraint::FinishStart:
00366 poly = finishStartLine( start, end ) + finishStartArrow( start, end );
00367 break;
00368 case Constraint::FinishFinish:
00369 poly = finishFinishLine( start, end ) + finishFinishArrow( start, end );
00370 break;
00371 case Constraint::StartStart:
00372 poly = startStartLine( start, end ) + startStartArrow( start, end );
00373 break;
00374 case Constraint::StartFinish:
00375 poly = startFinishLine( start, end ) + startFinishArrow( start, end );
00376 break;
00377 default:
00378 break;
00379 }
00380 return poly.boundingRect().adjusted( -PW, -PW, PW, PW );
00381 }
00382
00383
00389 void ItemDelegate::paintConstraintItem( QPainter* painter, const QStyleOptionGraphicsItem& opt,
00390 const QPointF& start, const QPointF& end, const Constraint &constraint )
00391 {
00392
00393 switch ( constraint.relationType() ) {
00394 case Constraint::FinishStart:
00395 paintFinishStartConstraint( painter, opt, start, end, constraint );
00396 break;
00397 case Constraint::FinishFinish:
00398 paintFinishFinishConstraint( painter, opt, start, end, constraint );
00399 break;
00400 case Constraint::StartStart:
00401 paintStartStartConstraint( painter, opt, start, end, constraint );
00402 break;
00403 case Constraint::StartFinish:
00404 paintStartFinishConstraint( painter, opt, start, end, constraint );
00405 break;
00406 default:
00407 break;
00408 }
00409 }
00410
00411 void ItemDelegate::paintFinishStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00412 {
00413 Q_UNUSED( opt );
00414
00415 QPen pen;
00416 QVariant dataPen;
00417
00418
00419 if ( start.x() <= end.x() ) {
00420 pen = QPen( Qt::black );
00421 dataPen = constraint.data( Constraint::ValidConstraintPen );
00422 } else {
00423 pen = QPen( Qt::red );
00424 dataPen = constraint.data( Constraint::InvalidConstraintPen );
00425 }
00426
00427
00428 if( qVariantCanConvert< QPen >( dataPen ) )
00429 pen = qVariantValue< QPen >( dataPen );
00430
00431 painter->setPen( pen );
00432 painter->setBrush( pen.color() );
00433
00434 painter->drawPolyline( finishStartLine( start, end ) );
00435 painter->drawPolygon( finishStartArrow( start, end ) );
00436 }
00437
00438 QPolygonF ItemDelegate::finishStartLine( const QPointF& start, const QPointF& end ) const
00439 {
00440 QPolygonF poly;
00441 qreal midx = end.x() - TURN;
00442 qreal midy = ( end.y()-start.y() )/2. + start.y();
00443
00444 if ( start.x() > end.x()-TURN ) {
00445 poly << start
00446 << QPointF( start.x()+TURN, start.y() )
00447 << QPointF( start.x()+TURN, midy )
00448 << QPointF( end.x()-TURN, midy )
00449 << QPointF( end.x()-TURN, end.y() )
00450 << end;
00451 } else {
00452 poly << start
00453 << QPointF( midx, start.y() )
00454 << QPointF( midx, end.y() )
00455 << end;
00456 }
00457 return poly;
00458 }
00459
00460 QPolygonF ItemDelegate::finishStartArrow( const QPointF& start, const QPointF& end ) const
00461 {
00462 QPolygonF poly;
00463 poly << end
00464 << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
00465 << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
00466 return poly;
00467 }
00468
00469 void ItemDelegate::paintFinishFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00470 {
00471 Q_UNUSED( opt );
00472
00473 QPen pen;
00474 QVariant dataPen;
00475
00476
00477 if ( start.x() <= end.x() ) {
00478 pen = QPen( Qt::black );
00479 dataPen = constraint.data( Constraint::ValidConstraintPen );
00480 } else {
00481 pen = QPen( Qt::red );
00482 dataPen = constraint.data( Constraint::InvalidConstraintPen );
00483 }
00484
00485
00486 if( qVariantCanConvert< QPen >( dataPen ) )
00487 pen = qVariantValue< QPen >( dataPen );
00488
00489 painter->setPen( pen );
00490 painter->setBrush( pen.color() );
00491
00492 painter->drawPolyline( finishFinishLine( start, end ) );
00493 painter->drawPolygon( finishFinishArrow( start, end ) );
00494 }
00495
00496 QPolygonF ItemDelegate::finishFinishLine( const QPointF& start, const QPointF& end ) const
00497 {
00498 QPolygonF poly;
00499 qreal midx = end.x() + TURN;
00500 qreal midy = ( end.y()-start.y() )/2. + start.y();
00501
00502 if ( start.x() > end.x()+TURN ) {
00503 poly << start
00504 << QPointF( start.x()+TURN, start.y() )
00505 << QPointF( start.x()+TURN, end.y() )
00506 << end;
00507 } else {
00508 poly << start
00509 << QPointF( midx, start.y() )
00510 << QPointF( midx, midy )
00511 << QPointF( end.x()+TURN, midy )
00512 << QPointF( end.x()+TURN, end.y() )
00513 << end;
00514 }
00515 return poly;
00516 }
00517
00518 QPolygonF ItemDelegate::finishFinishArrow( const QPointF& start, const QPointF& end ) const
00519 {
00520 QPolygonF poly;
00521 poly << end
00522 << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
00523 << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
00524 return poly;
00525 }
00526
00527 void ItemDelegate::paintStartStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00528 {
00529 Q_UNUSED( opt );
00530
00531 QPen pen;
00532 QVariant dataPen;
00533
00534
00535 if ( start.x() <= end.x() ) {
00536 pen = QPen( Qt::black );
00537 dataPen = constraint.data( Constraint::ValidConstraintPen );
00538 } else {
00539 pen = QPen( Qt::red );
00540 dataPen = constraint.data( Constraint::InvalidConstraintPen );
00541 }
00542
00543
00544 if( qVariantCanConvert< QPen >( dataPen ) )
00545 pen = qVariantValue< QPen >( dataPen );
00546
00547 painter->setPen( pen );
00548 painter->setBrush( pen.color() );
00549
00550 painter->drawPolyline( startStartLine( start, end ) );
00551 painter->drawPolygon( startStartArrow( start, end ) );
00552
00553 }
00554
00555 QPolygonF ItemDelegate::startStartLine( const QPointF& start, const QPointF& end ) const
00556 {
00557 QPolygonF poly;
00558 qreal midx = start.x() - TURN;
00559 qreal midy = ( end.y()-start.y() )/2. + start.y();
00560
00561 if ( start.x() > end.x() ) {
00562 poly << start
00563 << QPointF( end.x()-TURN, start.y() )
00564 << QPointF( end.x()-TURN, end.y() )
00565 << end;
00566 } else {
00567 poly << start
00568 << QPointF( start.x()-TURN, start.y() )
00569 << QPointF( start.x()-TURN, end.y() )
00570 << QPointF( end.x()-TURN, end.y() )
00571 << end;
00572 }
00573 return poly;
00574 }
00575
00576 QPolygonF ItemDelegate::startStartArrow( const QPointF& start, const QPointF& end ) const
00577 {
00578 QPolygonF poly;
00579 poly << end
00580 << QPointF( end.x()-TURN/2., end.y()-TURN/2. )
00581 << QPointF( end.x()-TURN/2., end.y()+TURN/2. );
00582 return poly;
00583 }
00584
00585 void ItemDelegate::paintStartFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint )
00586 {
00587 Q_UNUSED( opt );
00588
00589 QPen pen;
00590 QVariant dataPen;
00591
00592
00593 if ( start.x() <= end.x() ) {
00594 pen = QPen( Qt::black );
00595 dataPen = constraint.data( Constraint::ValidConstraintPen );
00596 } else {
00597 pen = QPen( Qt::red );
00598 dataPen = constraint.data( Constraint::InvalidConstraintPen );
00599 }
00600
00601
00602 if( qVariantCanConvert< QPen >( dataPen ) )
00603 pen = qVariantValue< QPen >( dataPen );
00604
00605 painter->setPen( pen );
00606 painter->setBrush( pen.color() );
00607
00608 painter->drawPolyline( startFinishLine( start, end ) );
00609 painter->drawPolygon( startFinishArrow( start, end ) );
00610 }
00611
00612 QPolygonF ItemDelegate::startFinishLine( const QPointF& start, const QPointF& end ) const
00613 {
00614 QPolygonF poly;
00615 qreal midx = end.x() + TURN;
00616 qreal midy = ( end.y()-start.y() )/2. + start.y();
00617
00618 if ( start.x()-TURN > end.x()+TURN ) {
00619 poly << start
00620 << QPointF( midx, start.y() )
00621 << QPointF( midx, end.y() )
00622 << end;
00623 } else {
00624 poly << start
00625 << QPointF( start.x()-TURN, start.y() )
00626 << QPointF( start.x()-TURN, midy )
00627 << QPointF( midx, midy )
00628 << QPointF( end.x()+TURN, end.y() )
00629 << end;
00630 }
00631 return poly;
00632 }
00633
00634 QPolygonF ItemDelegate::startFinishArrow( const QPointF& start, const QPointF& end ) const
00635 {
00636 QPolygonF poly;
00637 poly << end
00638 << QPointF( end.x()+TURN/2., end.y()-TURN/2. )
00639 << QPointF( end.x()+TURN/2., end.y()+TURN/2. );
00640 return poly;
00641 }
00642
00643
00644 #include "moc_kdganttitemdelegate.cpp"