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