kdgantt

kdganttitemdelegate.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002  ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB.  All rights reserved.
00003  **
00004  ** This file is part of the KD Gantt library.
00005  **
00006  ** This file may be used under the terms of the GNU General Public
00007  ** License versions 2.0 or 3.0 as published by the Free Software
00008  ** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
00009  ** included in the packaging of this file.  Alternatively you may (at
00010  ** your option) use any later version of the GNU General Public
00011  ** License if such license has been publicly approved by
00012  ** Klarälvdalens Datakonsult AB (or its successors, if any).
00013  ** 
00014  ** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
00015  ** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
00016  ** A PARTICULAR PURPOSE. Klarälvdalens Datakonsult AB reserves all rights
00017  ** not expressly granted herein.
00018  ** 
00019  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
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 /* QT_NO_DEBUG_STREAM */
00057 
00058 using namespace KDGantt;
00059 
00073 ItemDelegate::Private::Private()
00074 {
00075     // Brushes
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     // Pens
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     //qDebug() << "itemBoundingSpan("<<opt<<idx<<")";
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     //qDebug() << "itemRect="<<itemRect<<", boundingRect="<<boundingRect;
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             // TODO
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             // TODO
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             //path.lineTo( QPointF( r.right()-3./2.*delta, r.top() + delta ) );
00303             path.quadTo( QPointF( r.right()-.5*deltaX, r.top() + deltaY ), QPointF( r.right()-2.*deltaX, r.top() + deltaY ) );
00304             //path.lineTo( QPointF( r.left()+3./2.*delta, r.top() + delta ) );
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: /* TODO */
00323         //qDebug() << opt.boundingRect << opt.itemRect;
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     //qDebug()<<"ItemDelegate::paintConstraintItem"<<start<<end<<constraint;
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     // default pens
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     // data() pen
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     // default pens
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     // data() pen
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     // default pens
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     // data() pen
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     // default pens
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     // data() pen
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"