kdganttitemdelegate.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
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 /* 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 
00100 QPen ItemDelegate::Private::constraintPen( const QPointF& start, const QPointF& end, const Constraint& constraint )
00101 {
00102     QPen pen;
00103     QVariant dataPen;
00104 
00105     // Use default pens...
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     // ... unless constraint.data() returned a valid pen for this case
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: // fall through
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     // An event item is infinitely thin, basically just a line, because it has only one date instead of two.
00250     // It is painted with an offset of -height/2, which is taken into account here.
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     //qDebug() << "itemRect="<<itemRect<<", boundingRect="<<boundingRect;
00286     //qDebug() << painter->font() << opt.fontMetrics.height() << painter->device()->width() << painter->device()->height();
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             // TODO
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             // TODO
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             //path.lineTo( QPointF( r.right()-3./2.*delta, r.top() + delta ) );
00336             path.quadTo( QPointF( r.right()-deltaXBezierControl, r.top() + deltaY ), QPointF( r.right()-deltaX, r.top() + deltaY ) );
00337             //path.lineTo( QPointF( r.left()+3./2.*delta, r.top() + delta ) );
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: /* TODO */
00349         //qDebug() << opt.boundingRect << opt.itemRect;
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     //qDebug()<<"ItemDelegate::paintConstraintItem"<<start<<end<<constraint;
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"
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/