KD Chart 2
[rev.2.5]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 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 ).toReal( &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 } 00417 return poly.boundingRect().adjusted( -PW, -PW, PW, PW ); 00418 } 00419 00420 00426 void ItemDelegate::paintConstraintItem( QPainter* painter, const QStyleOptionGraphicsItem& opt, 00427 const QPointF& start, const QPointF& end, const Constraint &constraint ) 00428 { 00429 //qDebug()<<"ItemDelegate::paintConstraintItem"<<start<<end<<constraint; 00430 switch ( constraint.relationType() ) { 00431 case Constraint::FinishStart: 00432 paintFinishStartConstraint( painter, opt, start, end, constraint ); 00433 break; 00434 case Constraint::FinishFinish: 00435 paintFinishFinishConstraint( painter, opt, start, end, constraint ); 00436 break; 00437 case Constraint::StartStart: 00438 paintStartStartConstraint( painter, opt, start, end, constraint ); 00439 break; 00440 case Constraint::StartFinish: 00441 paintStartFinishConstraint( painter, opt, start, end, constraint ); 00442 break; 00443 } 00444 } 00445 00446 void ItemDelegate::paintFinishStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ) 00447 { 00448 Q_UNUSED( opt ); 00449 00450 const QPen pen = d->constraintPen( start, end, constraint ); 00451 00452 painter->setPen( pen ); 00453 painter->setBrush( pen.color() ); 00454 00455 painter->drawPolyline( finishStartLine( start, end ) ); 00456 painter->drawPolygon( finishStartArrow( start, end ) ); 00457 } 00458 00459 QPolygonF ItemDelegate::finishStartLine( const QPointF& start, const QPointF& end ) const 00460 { 00461 QPolygonF poly; 00462 qreal midx = end.x() - TURN; 00463 qreal midy = ( end.y()-start.y() )/2. + start.y(); 00464 00465 if ( start.x() > end.x()-TURN ) { 00466 poly << start 00467 << QPointF( start.x()+TURN, start.y() ) 00468 << QPointF( start.x()+TURN, midy ) 00469 << QPointF( end.x()-TURN, midy ) 00470 << QPointF( end.x()-TURN, end.y() ) 00471 << end; 00472 } else { 00473 poly << start 00474 << QPointF( midx, start.y() ) 00475 << QPointF( midx, end.y() ) 00476 << end; 00477 } 00478 return poly; 00479 } 00480 00481 QPolygonF ItemDelegate::finishStartArrow( const QPointF& start, const QPointF& end ) const 00482 { 00483 Q_UNUSED(start); 00484 00485 QPolygonF poly; 00486 poly << end 00487 << QPointF( end.x()-TURN/2., end.y()-TURN/2. ) 00488 << QPointF( end.x()-TURN/2., end.y()+TURN/2. ); 00489 return poly; 00490 } 00491 00492 void ItemDelegate::paintFinishFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ) 00493 { 00494 Q_UNUSED( opt ); 00495 00496 const QPen pen = d->constraintPen( start, end, constraint ); 00497 00498 painter->setPen( pen ); 00499 painter->setBrush( pen.color() ); 00500 00501 painter->drawPolyline( finishFinishLine( start, end ) ); 00502 painter->drawPolygon( finishFinishArrow( start, end ) ); 00503 } 00504 00505 QPolygonF ItemDelegate::finishFinishLine( const QPointF& start, const QPointF& end ) const 00506 { 00507 QPolygonF poly; 00508 qreal midx = end.x() + TURN; 00509 qreal midy = ( end.y()-start.y() )/2. + start.y(); 00510 00511 if ( start.x() > end.x()+TURN ) { 00512 poly << start 00513 << QPointF( start.x()+TURN, start.y() ) 00514 << QPointF( start.x()+TURN, end.y() ) 00515 << end; 00516 } else { 00517 poly << start 00518 << QPointF( midx, start.y() ) 00519 << QPointF( midx, midy ) 00520 << QPointF( end.x()+TURN, midy ) 00521 << QPointF( end.x()+TURN, end.y() ) 00522 << end; 00523 } 00524 return poly; 00525 } 00526 00527 QPolygonF ItemDelegate::finishFinishArrow( const QPointF& start, const QPointF& end ) const 00528 { 00529 Q_UNUSED(start); 00530 00531 QPolygonF poly; 00532 poly << end 00533 << QPointF( end.x()+TURN/2., end.y()-TURN/2. ) 00534 << QPointF( end.x()+TURN/2., end.y()+TURN/2. ); 00535 return poly; 00536 } 00537 00538 void ItemDelegate::paintStartStartConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ) 00539 { 00540 Q_UNUSED( opt ); 00541 00542 const QPen pen = d->constraintPen( start, end, constraint ); 00543 00544 painter->setPen( pen ); 00545 painter->setBrush( pen.color() ); 00546 00547 painter->drawPolyline( startStartLine( start, end ) ); 00548 painter->drawPolygon( startStartArrow( start, end ) ); 00549 00550 } 00551 00552 QPolygonF ItemDelegate::startStartLine( const QPointF& start, const QPointF& end ) const 00553 { 00554 Q_UNUSED(start); 00555 00556 QPolygonF poly; 00557 00558 if ( start.x() > end.x() ) { 00559 poly << start 00560 << QPointF( end.x()-TURN, start.y() ) 00561 << QPointF( end.x()-TURN, end.y() ) 00562 << end; 00563 } else { 00564 poly << start 00565 << QPointF( start.x()-TURN, start.y() ) 00566 << QPointF( start.x()-TURN, end.y() ) 00567 << QPointF( end.x()-TURN, end.y() ) 00568 << end; 00569 } 00570 return poly; 00571 } 00572 00573 QPolygonF ItemDelegate::startStartArrow( const QPointF& start, const QPointF& end ) const 00574 { 00575 Q_UNUSED(start); 00576 00577 QPolygonF poly; 00578 poly << end 00579 << QPointF( end.x()-TURN/2., end.y()-TURN/2. ) 00580 << QPointF( end.x()-TURN/2., end.y()+TURN/2. ); 00581 return poly; 00582 } 00583 00584 void ItemDelegate::paintStartFinishConstraint( QPainter* painter, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ) 00585 { 00586 Q_UNUSED( opt ); 00587 00588 const QPen pen = d->constraintPen( start, end, constraint ); 00589 00590 painter->setPen( pen ); 00591 painter->setBrush( pen.color() ); 00592 00593 painter->drawPolyline( startFinishLine( start, end ) ); 00594 painter->drawPolygon( startFinishArrow( start, end ) ); 00595 } 00596 00597 QPolygonF ItemDelegate::startFinishLine( const QPointF& start, const QPointF& end ) const 00598 { 00599 Q_UNUSED(start); 00600 00601 QPolygonF poly; 00602 qreal midx = end.x() + TURN; 00603 qreal midy = ( end.y()-start.y() )/2. + start.y(); 00604 00605 if ( start.x()-TURN > end.x()+TURN ) { 00606 poly << start 00607 << QPointF( midx, start.y() ) 00608 << QPointF( midx, end.y() ) 00609 << end; 00610 } else { 00611 poly << start 00612 << QPointF( start.x()-TURN, start.y() ) 00613 << QPointF( start.x()-TURN, midy ) 00614 << QPointF( midx, midy ) 00615 << QPointF( end.x()+TURN, end.y() ) 00616 << end; 00617 } 00618 return poly; 00619 } 00620 00621 QPolygonF ItemDelegate::startFinishArrow( const QPointF& start, const QPointF& end ) const 00622 { 00623 Q_UNUSED(start); 00624 00625 QPolygonF poly; 00626 poly << end 00627 << QPointF( end.x()+TURN/2., end.y()-TURN/2. ) 00628 << QPointF( end.x()+TURN/2., end.y()+TURN/2. ); 00629 return poly; 00630 } 00631 00632 00633 #include "moc_kdganttitemdelegate.cpp"