00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "kdganttdatetimegrid.h"
00026 #include "kdganttdatetimegrid_p.h"
00027
00028 #include "kdganttabstractrowcontroller.h"
00029
00030 #include <QApplication>
00031 #include <QDateTime>
00032 #include <QPainter>
00033 #include <QStyle>
00034 #include <QStyleOptionHeader>
00035 #include <QWidget>
00036 #include <QDebug>
00037
00038 #include <cassert>
00039
00040 using namespace KDGantt;
00041
00042 QDebug operator<<( QDebug dbg, KDGantt::DateTimeScaleFormatter::Range range )
00043 {
00044 switch( range ) {
00045 case KDGantt::DateTimeScaleFormatter::Second: dbg << "KDGantt::DateTimeScaleFormatter::Second"; break;
00046 case KDGantt::DateTimeScaleFormatter::Minute: dbg << "KDGantt::DateTimeScaleFormatter::Minute"; break;
00047 case KDGantt::DateTimeScaleFormatter::Hour: dbg << "KDGantt::DateTimeScaleFormatter::Hour"; break;
00048 case KDGantt::DateTimeScaleFormatter::Day: dbg << "KDGantt::DateTimeScaleFormatter::Day"; break;
00049 case KDGantt::DateTimeScaleFormatter::Week: dbg << "KDGantt::DateTimeScaleFormatter::Week"; break;
00050 case KDGantt::DateTimeScaleFormatter::Month: dbg << "KDGantt::DateTimeScaleFormatter::Month"; break;
00051 case KDGantt::DateTimeScaleFormatter::Year: dbg << "KDGantt::DateTimeScaleFormatter::Year"; break;
00052 }
00053 return dbg;
00054 }
00055
00056
00064
00065
00066
00067 qreal DateTimeGrid::Private::dateTimeToChartX( const QDateTime& dt ) const
00068 {
00069 assert( startDateTime.isValid() );
00070 qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
00071 result += startDateTime.time().msecsTo(dt.time())/1000.;
00072 result *= dayWidth/( 24.*60.*60. );
00073
00074 return result;
00075 }
00076
00077 QDateTime DateTimeGrid::Private::chartXtoDateTime( qreal x ) const
00078 {
00079 assert( startDateTime.isValid() );
00080 int days = static_cast<int>( x/dayWidth );
00081 qreal secs = x*( 24.*60.*60. )/dayWidth;
00082 QDateTime dt = startDateTime;
00083 QDateTime result = dt.addDays( days )
00084 .addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) )
00085 .addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
00086 return result;
00087 }
00088
00089 #define d d_func()
00090
00118 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format,
00119 const QString& templ, Qt::Alignment alignment )
00120 : _d( new Private( range, format, templ, alignment ) )
00121 {
00122 }
00123
00124 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format, Qt::Alignment alignment )
00125 : _d( new Private( range, format, QString::fromLatin1( "%1" ), alignment ) )
00126 {
00127 }
00128
00129 DateTimeScaleFormatter::DateTimeScaleFormatter( const DateTimeScaleFormatter& other )
00130 : _d( new Private( other.range(), other.format(), other.d->templ, other.alignment() ) )
00131 {
00132 }
00133
00134 DateTimeScaleFormatter::~DateTimeScaleFormatter()
00135 {
00136 delete _d;
00137 }
00138
00139 DateTimeScaleFormatter& DateTimeScaleFormatter::operator=( const DateTimeScaleFormatter& other )
00140 {
00141 delete _d;
00142 _d = new Private( other.range(), other.format(), other.d->templ, other.alignment() );
00143 return *this;
00144 }
00145
00148 QString DateTimeScaleFormatter::format() const
00149 {
00150 return d->format;
00151 }
00152
00155 QString DateTimeScaleFormatter::format( const QDateTime& datetime ) const
00156 {
00157 QString result = d->format;
00158
00159 const QString shortWeekNumber = QString::number( datetime.date().weekNumber() );
00160 const QString longWeekNumber = ( shortWeekNumber.length() == 1 ? QString::fromLatin1( "0" ) : QString() ) + shortWeekNumber;
00161 result.replace( QString::fromLatin1( "ww" ), longWeekNumber );
00162 result.replace( QString::fromLatin1( "w" ), shortWeekNumber );
00163 result = datetime.toLocalTime().toString( result );
00164 return result;
00165 }
00166
00167 QString DateTimeScaleFormatter::text( const QDateTime& datetime ) const
00168 {
00169 return d->templ.arg( format( datetime ) );
00170 }
00171
00174 DateTimeScaleFormatter::Range DateTimeScaleFormatter::range() const
00175 {
00176 return d->range;
00177 }
00178
00179 Qt::Alignment DateTimeScaleFormatter::alignment() const
00180 {
00181 return d->alignment;
00182 }
00183
00187 QDateTime DateTimeScaleFormatter::nextRangeBegin( const QDateTime& datetime ) const
00188 {
00189 QDateTime result = datetime;
00190 switch( d->range )
00191 {
00192 case Second:
00193 result = result.addSecs( 60 );
00194 break;
00195 case Minute:
00196
00197 result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00198 result = result.addSecs( 60 );
00199 break;
00200 case Hour:
00201
00202 result.setTime( QTime( result.time().hour(), 0 ) );
00203 result = result.addSecs( 60 * 60 );
00204 break;
00205 case Day:
00206
00207 result.setTime( QTime( 0, 0 ) );
00208 result = result.addDays( 1 );
00209 break;
00210 case Week:
00211
00212 result.setTime( QTime( 0, 0 ) );
00213
00214 {
00215 const int weekNumber = result.date().weekNumber();
00216 while( weekNumber == result.date().weekNumber() )
00217 result = result.addDays( 1 );
00218 }
00219 break;
00220 case Month:
00221
00222 result.setTime( QTime( 0, 0 ) );
00223
00224 result.setDate( QDate( result.date().year(), result.date().month(), 1 ).addMonths( 1 ) );
00225 break;
00226 case Year:
00227
00228 result.setTime( QTime( 0, 0 ) );
00229
00230 result.setDate( QDate( result.date().year(), 1, 1 ).addYears( 1 ) );
00231 break;
00232 }
00233
00234 assert( result != datetime );
00235
00236 return result;
00237 }
00238
00242 QDateTime DateTimeScaleFormatter::currentRangeBegin( const QDateTime& datetime ) const
00243 {
00244 QDateTime result = datetime;
00245 switch( d->range )
00246 {
00247 case Second:
00248 break;
00249 case Minute:
00250
00251 result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00252 break;
00253 case Hour:
00254
00255 result.setTime( QTime( result.time().hour(), 0 ) );
00256 break;
00257 case Day:
00258
00259 result.setTime( QTime( 0, 0 ) );
00260 break;
00261 case Week:
00262
00263 result.setTime( QTime( 0, 0 ) );
00264
00265 {
00266 const int weekNumber = result.date().weekNumber();
00267 while( weekNumber == result.date().addDays( -1 ).weekNumber() )
00268 result = result.addDays( -1 );
00269 }
00270 break;
00271 case Month:
00272
00273 result.setTime( QTime( 0, 0 ) );
00274
00275 result.setDate( QDate( result.date().year(), result.date().month(), 1 ) );
00276 break;
00277 case Year:
00278
00279 result.setTime( QTime( 0, 0 ) );
00280
00281 result.setDate( QDate( result.date().year(), 1, 1 ) );
00282 break;
00283 }
00284 return result;
00285 }
00286
00287 DateTimeGrid::DateTimeGrid() : AbstractGrid( new Private )
00288 {
00289 }
00290
00291 DateTimeGrid::~DateTimeGrid()
00292 {
00293 }
00294
00299 QDateTime DateTimeGrid::startDateTime() const
00300 {
00301 return d->startDateTime;
00302 }
00303
00309 void DateTimeGrid::setStartDateTime( const QDateTime& dt )
00310 {
00311 d->startDateTime = dt;
00312 emit gridChanged();
00313 }
00314
00319 qreal DateTimeGrid::dayWidth() const
00320 {
00321 return d->dayWidth;
00322 }
00323
00326 qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
00327 {
00328 return d->dateTimeToChartX( dt );
00329 }
00330
00333 QDateTime DateTimeGrid::mapToDateTime( qreal x ) const
00334 {
00335 return d->chartXtoDateTime( x );
00336 }
00337
00342 void DateTimeGrid::setDayWidth( qreal w )
00343 {
00344 assert( w>0 );
00345 d->dayWidth = w;
00346 emit gridChanged();
00347 }
00348
00354 void DateTimeGrid::setScale( Scale s )
00355 {
00356 d->scale = s;
00357 emit gridChanged();
00358 }
00359
00366 DateTimeGrid::Scale DateTimeGrid::scale() const
00367 {
00368 return d->scale;
00369 }
00370
00378 void DateTimeGrid::setUserDefinedLowerScale( DateTimeScaleFormatter* lower )
00379 {
00380 delete d->lower;
00381 d->lower = lower;
00382 emit gridChanged();
00383 }
00384
00392 void DateTimeGrid::setUserDefinedUpperScale( DateTimeScaleFormatter* upper )
00393 {
00394 delete d->upper;
00395 d->upper = upper;
00396 emit gridChanged();
00397 }
00398
00401 DateTimeScaleFormatter* DateTimeGrid::userDefinedLowerScale() const
00402 {
00403 return d->lower;
00404 }
00405
00408 DateTimeScaleFormatter* DateTimeGrid::userDefinedUpperScale() const
00409 {
00410 return d->upper;
00411 }
00412
00418 void DateTimeGrid::setWeekStart( Qt::DayOfWeek ws )
00419 {
00420 d->weekStart = ws;
00421 emit gridChanged();
00422 }
00423
00425 Qt::DayOfWeek DateTimeGrid::weekStart() const
00426 {
00427 return d->weekStart;
00428 }
00429
00436 void DateTimeGrid::setFreeDays( const QSet<Qt::DayOfWeek>& fd )
00437 {
00438 d->freeDays = fd;
00439 emit gridChanged();
00440 }
00441
00443 QSet<Qt::DayOfWeek> DateTimeGrid::freeDays() const
00444 {
00445 return d->freeDays;
00446 }
00447
00449 bool DateTimeGrid::rowSeparators() const
00450 {
00451 return d->rowSeparators;
00452 }
00454 void DateTimeGrid::setRowSeparators( bool enable )
00455 {
00456 d->rowSeparators = enable;
00457 }
00458
00463 void DateTimeGrid::setNoInformationBrush( const QBrush& brush )
00464 {
00465 d->noInformationBrush = brush;
00466 emit gridChanged();
00467 }
00468
00471 QBrush DateTimeGrid::noInformationBrush() const
00472 {
00473 return d->noInformationBrush;
00474 }
00475
00479 Span DateTimeGrid::mapToChart( const QModelIndex& idx ) const
00480 {
00481 assert( model() );
00482 if ( !idx.isValid() ) return Span();
00483 assert( idx.model()==model() );
00484 const QVariant sv = model()->data( idx, StartTimeRole );
00485 const QVariant ev = model()->data( idx, EndTimeRole );
00486 if( qVariantCanConvert<QDateTime>(sv) &&
00487 qVariantCanConvert<QDateTime>(ev) &&
00488 !(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) &&
00489 !(ev.type() == QVariant::String && qVariantValue<QString>(ev).isEmpty())
00490 ) {
00491 QDateTime st = sv.toDateTime();
00492 QDateTime et = ev.toDateTime();
00493 if ( et.isValid() && st.isValid() ) {
00494 qreal sx = d->dateTimeToChartX( st );
00495 qreal ex = d->dateTimeToChartX( et )-sx;
00496
00497 return Span( sx, ex);
00498 }
00499 }
00500
00501 if( qVariantCanConvert<QDateTime>(sv) && !(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) ) {
00502 QDateTime st = sv.toDateTime();
00503 if ( st.isValid() ) {
00504 qreal sx = d->dateTimeToChartX( st );
00505 return Span( sx, 0 );
00506 }
00507 }
00508 return Span();
00509 }
00510
00511 #if 0
00512 static void debug_print_idx( const QModelIndex& idx )
00513 {
00514 if ( !idx.isValid() ) {
00515 qDebug() << "[Invalid]";
00516 return;
00517 }
00518 QDateTime st = idx.data( StartTimeRole ).toDateTime();
00519 QDateTime et = idx.data( StartTimeRole ).toDateTime();
00520 qDebug() << idx << "["<<st<<et<<"]";
00521 }
00522 #endif
00523
00538 bool DateTimeGrid::mapFromChart( const Span& span, const QModelIndex& idx,
00539 const QList<Constraint>& constraints ) const
00540 {
00541 assert( model() );
00542 if ( !idx.isValid() ) return false;
00543 assert( idx.model()==model() );
00544
00545 QDateTime st = d->chartXtoDateTime(span.start());
00546 QDateTime et = d->chartXtoDateTime(span.start()+span.length());
00547
00548 Q_FOREACH( const Constraint& c, constraints ) {
00549 if ( c.type() != Constraint::TypeHard || !isSatisfiedConstraint( c )) continue;
00550 if ( c.startIndex() == idx ) {
00551 QDateTime tmpst = model()->data( c.endIndex(), StartTimeRole ).toDateTime();
00552
00553 if ( tmpst<et ) return false;
00554 } else if ( c.endIndex() == idx ) {
00555 QDateTime tmpet = model()->data( c.startIndex(), EndTimeRole ).toDateTime();
00556
00557 if ( tmpet>st ) return false;
00558 }
00559 }
00560 return model()->setData( idx, qVariantFromValue(st), StartTimeRole )
00561 && model()->setData( idx, qVariantFromValue(et), EndTimeRole );
00562 }
00563
00564 Qt::PenStyle DateTimeGrid::Private::gridLinePenStyle( QDateTime dt, Private::HeaderType headerType ) const
00565 {
00566 switch ( headerType ) {
00567 case Private::HeaderHour:
00568
00569 if ( dt.time().hour() == 0 )
00570 return Qt::SolidLine;
00571 return Qt::DashLine;
00572 case Private::HeaderDay:
00573
00574 if ( dt.date().dayOfWeek() == weekStart )
00575 return Qt::SolidLine;
00576 return Qt::DashLine;
00577 case Private::HeaderWeek:
00578
00579 if ( dt.date().day() == 1 )
00580 return Qt::SolidLine;
00581
00582 if ( dt.date().dayOfWeek() == weekStart )
00583 return Qt::DashLine;
00584 return Qt::NoPen;
00585 case Private::HeaderMonth:
00586
00587 if ( dt.date().dayOfYear() == 1 )
00588 return Qt::SolidLine;
00589
00590 if ( dt.date().day() == 1 )
00591 return Qt::DashLine;
00592 return Qt::NoPen;
00593 default:
00594
00595 break;
00596 }
00597
00598
00599 return Qt::NoPen;
00600 }
00601
00602 QDateTime DateTimeGrid::Private::adjustDateTimeForHeader( QDateTime dt, Private::HeaderType headerType ) const
00603 {
00604
00605 dt.setTime( QTime( 0, 0, 0, 0 ) );
00606
00607 switch ( headerType ) {
00608 case Private::HeaderWeek:
00609
00610 while ( dt.date().dayOfWeek() != weekStart )
00611 dt = dt.addDays( -1 );
00612 break;
00613 case Private::HeaderMonth:
00614
00615 dt = dt.addDays( 1 - dt.date().day() );
00616 break;
00617 case Private::HeaderYear:
00618
00619 dt = dt.addDays( 1 - dt.date().dayOfYear() );
00620 break;
00621 default:
00622
00623 break;
00624 }
00625
00626 return dt;
00627 }
00628
00629 void DateTimeGrid::Private::paintVerticalLines( QPainter* painter,
00630 const QRectF& sceneRect,
00631 const QRectF& exposedRect,
00632 QWidget* widget,
00633 Private::HeaderType headerType )
00634 {
00635 QDateTime dt = chartXtoDateTime( exposedRect.left() );
00636 dt = adjustDateTimeForHeader( dt, headerType );
00637
00638 int offsetSeconds = 0;
00639 int offsetDays = 0;
00640
00641 if ( headerType == Private::HeaderHour )
00642 offsetSeconds = 60*60;
00643 else
00644 offsetDays = 1;
00645
00646 for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
00647 dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), x = dateTimeToChartX( dt ) ) {
00648 if ( x >= exposedRect.left() ) {
00649 QPen pen = painter->pen();
00650 pen.setBrush( QApplication::palette().dark() );
00651 pen.setStyle( gridLinePenStyle( dt, headerType ) );
00652 painter->setPen( pen );
00653 if ( freeDays.contains( static_cast<Qt::DayOfWeek>( dt.date().dayOfWeek() ) ) ) {
00654 painter->setBrush( widget?widget->palette().midlight()
00655 :QApplication::palette().midlight() );
00656 painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() );
00657 }
00658 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00659 }
00660 }
00661 }
00662
00663 void DateTimeGrid::Private::paintVerticalUserDefinedLines( QPainter* painter,
00664 const QRectF& sceneRect,
00665 const QRectF& exposedRect,
00666 const DateTimeScaleFormatter* formatter,
00667 QWidget* widget )
00668 {
00669 Q_UNUSED( widget );
00670 QDateTime dt = chartXtoDateTime( exposedRect.left() );
00671 dt = formatter->currentRangeBegin( dt );
00672 QPen pen = painter->pen();
00673 pen.setBrush( QApplication::palette().dark() );
00674 pen.setStyle( Qt::DashLine );
00675 painter->setPen( pen );
00676 for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
00677 dt = formatter->nextRangeBegin( dt ),x=dateTimeToChartX( dt ) ) {
00678 if ( x >= exposedRect.left() ) {
00679
00680 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00681 }
00682 }
00683 }
00684
00685 DateTimeGrid::Private::HeaderType DateTimeGrid::Private::headerTypeForScale( DateTimeGrid::Scale scale )
00686 {
00687 switch ( scale ) {
00688 case ScaleHour:
00689 return Private::HeaderHour;
00690 case ScaleDay:
00691 return Private::HeaderDay;
00692 case ScaleWeek:
00693 return Private::HeaderWeek;
00694 case ScaleMonth:
00695 return Private::HeaderMonth;
00696 default:
00697
00698 assert( false );
00699 break;
00700 }
00701 }
00702
00703 void DateTimeGrid::paintGrid( QPainter* painter,
00704 const QRectF& sceneRect,
00705 const QRectF& exposedRect,
00706 AbstractRowController* rowController,
00707 QWidget* widget )
00708 {
00709
00710 switch( scale() ) {
00711 case ScaleHour:
00712 case ScaleDay:
00713 case ScaleWeek:
00714 case ScaleMonth:
00715 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, d->headerTypeForScale( scale() ) );
00716 break;
00717 case ScaleAuto: {
00718 const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00719 const qreal dayw = dayWidth();
00720 if ( dayw > 24*60*60*tabw ) {
00721 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->minute_lower, widget );
00722 } else if ( dayw > 24*60*tabw ) {
00723 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderHour );
00724 } else if ( dayw > 24*tabw ) {
00725 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderDay );
00726 } else if ( dayw > tabw ) {
00727 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->week_lower, widget );
00728 } else if ( 4*dayw > tabw ) {
00729 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->month_lower, widget );
00730 } else {
00731 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->year_lower, widget );
00732 }
00733 break;
00734 }
00735 case ScaleUserDefined:
00736 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, d->lower, widget );
00737 break;
00738 }
00739 if ( rowController ) {
00740
00741 QPen pen = painter->pen();
00742 pen.setBrush( QApplication::palette().dark() );
00743 pen.setStyle( Qt::DashLine );
00744 painter->setPen( pen );
00745 QModelIndex idx = rowController->indexAt( qRound( exposedRect.top() ) );
00746 if ( rowController->indexAbove( idx ).isValid() ) idx = rowController->indexAbove( idx );
00747 qreal y = 0;
00748 while ( y < exposedRect.bottom() && idx.isValid() ) {
00749 const Span s = rowController->rowGeometry( idx );
00750 y = s.start()+s.length();
00751 if ( d->rowSeparators ) {
00752 painter->drawLine( QPointF( sceneRect.left(), y ),
00753 QPointF( sceneRect.right(), y ) );
00754 }
00755 if ( !idx.data( ItemTypeRole ).isValid() && d->noInformationBrush.style() != Qt::NoBrush ) {
00756 painter->fillRect( QRectF( exposedRect.left(), s.start(), exposedRect.width(), s.length() ), d->noInformationBrush );
00757 }
00758
00759
00760 idx = rowController->indexBelow( idx );
00761 }
00762 }
00763 }
00764
00765 int DateTimeGrid::Private::tabHeight( const QString& txt, QWidget* widget ) const
00766 {
00767 QStyleOptionHeader opt;
00768 if ( widget ) opt.initFrom( widget );
00769 opt.text = txt;
00770 QStyle* style;
00771 if ( widget ) style = widget->style();
00772 else style = QApplication::style();
00773 QSize s = style->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), widget);
00774 return s.height();
00775 }
00776
00777 void DateTimeGrid::Private::getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper)
00778 {
00779 const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00780 const qreal dayw = dayWidth;
00781 if ( dayw > 24*60*60*tabw ) {
00782 *lower = &minute_lower;
00783 *upper = &minute_upper;
00784 } else if ( dayw > 24*60*tabw ) {
00785 *lower = &hour_lower;
00786 *upper = &hour_upper;
00787 } else if ( dayw > 24*tabw ) {
00788 *lower = &day_lower;
00789 *upper = &day_upper;
00790 } else if ( dayw > tabw ) {
00791 *lower = &week_lower;
00792 *upper = &week_upper;
00793 } else if ( 4*dayw > tabw ) {
00794 *lower = &month_lower;
00795 *upper = &month_upper;
00796 } else {
00797 *lower = &year_lower;
00798 *upper = &year_upper;
00799 }
00800 }
00801
00802
00803 void DateTimeGrid::paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
00804 qreal offset, QWidget* widget )
00805 {
00806 painter->save();
00807 QPainterPath clipPath;
00808 clipPath.addRect( headerRect );
00809 painter->setClipPath( clipPath, Qt::IntersectClip );
00810 switch( scale() )
00811 {
00812 case ScaleHour:
00813 paintHourScaleHeader( painter, headerRect, exposedRect, offset, widget );
00814 break;
00815 case ScaleDay:
00816 paintDayScaleHeader( painter, headerRect, exposedRect, offset, widget );
00817 break;
00818 case ScaleWeek:
00819 paintWeekScaleHeader( painter, headerRect, exposedRect, offset, widget );
00820 break;
00821 case ScaleMonth:
00822 paintMonthScaleHeader( painter, headerRect, exposedRect, offset, widget );
00823 break;
00824 case ScaleAuto:
00825 {
00826 DateTimeScaleFormatter *lower, *upper;
00827 d->getAutomaticFormatters( &lower, &upper );
00828 const qreal lowerHeight = d->tabHeight( lower->text( startDateTime() ) );
00829 const qreal upperHeight = d->tabHeight( upper->text( startDateTime() ) );
00830 const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00831
00832 const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00833 const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 );
00834
00835 paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, lower, widget );
00836 paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, upper, widget );
00837 break;
00838 }
00839 case ScaleUserDefined:
00840 {
00841 const qreal lowerHeight = d->tabHeight( d->lower->text( startDateTime() ) );
00842 const qreal upperHeight = d->tabHeight( d->upper->text( startDateTime() ) );
00843 const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00844
00845 const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00846 const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 );
00847
00848 paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, d->lower, widget );
00849 paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, d->upper, widget );
00850 }
00851 break;
00852 }
00853 painter->restore();
00854 }
00855
00856 void DateTimeGrid::paintUserDefinedHeader( QPainter* painter,
00857 const QRectF& headerRect, const QRectF& exposedRect,
00858 qreal offset, const DateTimeScaleFormatter* formatter,
00859 QWidget* widget )
00860 {
00861 const QStyle* const style = widget ? widget->style() : QApplication::style();
00862
00863 QDateTime dt = formatter->currentRangeBegin( d->chartXtoDateTime( offset + exposedRect.left() ) ).toUTC();
00864 qreal x = d->dateTimeToChartX( dt );
00865
00866 while( x < exposedRect.right() + offset )
00867 {
00868 const QDateTime next = formatter->nextRangeBegin( dt );
00869 const qreal nextx = d->dateTimeToChartX( next );
00870
00871 QStyleOptionHeader opt;
00872 if ( widget ) opt.init( widget );
00873 opt.rect = QRectF( x - offset+1, headerRect.top(), qMax( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect();
00874 opt.textAlignment = formatter->alignment();
00875 opt.text = formatter->text( dt );
00876 style->drawControl( QStyle::CE_Header, &opt, painter, widget );
00877
00878 dt = next;
00879 x = nextx;
00880 }
00881 }
00882
00883 void DateTimeGrid::Private::paintHeader( QPainter* painter,
00884 const QRectF& headerRect, const QRectF& exposedRect,
00885 qreal offset, QWidget* widget,
00886 Private::HeaderType headerType,
00887 DateTextFormatter *formatter )
00888 {
00889 QStyle* style = widget?widget->style():QApplication::style();
00890
00891 const qreal left = exposedRect.left() + offset;
00892 const qreal right = exposedRect.right() + offset;
00893
00894
00895 QDateTime dt = chartXtoDateTime( left );
00896 dt = adjustDateTimeForHeader( dt, headerType );
00897
00898 int offsetSeconds = 0;
00899 int offsetDays = 0;
00900 int offsetMonths = 0;
00901
00902 switch ( headerType ) {
00903 case Private::HeaderHour:
00904 offsetSeconds = 60*60;
00905 break;
00906 case Private::HeaderDay:
00907 offsetDays = 1;
00908 break;
00909 case Private::HeaderWeek:
00910 offsetDays = 7;
00911 break;
00912 case Private::HeaderMonth:
00913 offsetMonths = 1;
00914 break;
00915 case Private::HeaderYear:
00916 offsetMonths = 12;
00917 break;
00918 default:
00919
00920 assert( false );
00921 break;
00922 }
00923
00924 for ( qreal x = dateTimeToChartX( dt ); x < right;
00925 dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), dt = dt.addMonths( offsetMonths ),
00926 x = dateTimeToChartX( dt ) ) {
00927 QStyleOptionHeader opt;
00928 if ( widget ) opt.init( widget );
00929 opt.rect = formatter->textRect( x, offset, dayWidth, headerRect, dt );
00930 opt.text = formatter->format( dt );
00931 opt.textAlignment = Qt::AlignCenter;
00932 style->drawControl(QStyle::CE_Header, &opt, painter, widget);
00933 }
00934 }
00935
00939 void DateTimeGrid::paintHourScaleHeader( QPainter* painter,
00940 const QRectF& headerRect, const QRectF& exposedRect,
00941 qreal offset, QWidget* widget )
00942 {
00943 class HourFormatter : public Private::DateTextFormatter {
00944 public:
00945 QString format( const QDateTime& dt ) {
00946 return dt.time().toString( QString::fromAscii( "hh" ) );
00947 }
00948 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
00949 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
00950 QSizeF( dayWidth / 24.0, headerRect.height() / 2.0 ) ).toAlignedRect();
00951 }
00952 };
00953 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
00954 Private::HeaderHour, new HourFormatter );
00955
00956 class DayFormatter : public Private::DateTextFormatter {
00957 public:
00958 QString format( const QDateTime& dt ) {
00959 return dt.date().toString();
00960 }
00961 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
00962 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
00963 QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toRect();
00964 }
00965 };
00966 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
00967 Private::HeaderDay, new DayFormatter );
00968 }
00969
00973 void DateTimeGrid::paintDayScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
00974 qreal offset, QWidget* widget )
00975 {
00976 class DayFormatter : public Private::DateTextFormatter {
00977 public:
00978 QString format( const QDateTime& dt ) {
00979 return dt.toString( QString::fromAscii( "ddd" ) ).left( 1 );
00980 }
00981 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
00982 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
00983 QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toAlignedRect();
00984 }
00985 };
00986 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
00987 Private::HeaderDay, new DayFormatter );
00988
00989 class WeekFormatter : public Private::DateTextFormatter {
00990 public:
00991 QString format( const QDateTime& dt ) {
00992 return QString::number( dt.date().weekNumber() );
00993 }
00994 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
00995 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
00996 QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
00997 }
00998 };
00999 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01000 Private::HeaderWeek, new WeekFormatter );
01001 }
01002
01006 void DateTimeGrid::paintWeekScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01007 qreal offset, QWidget* widget )
01008 {
01009 class WeekFormatter : public Private::DateTextFormatter {
01010 public:
01011 QString format( const QDateTime& dt ) {
01012 return QString::number( dt.date().weekNumber() );
01013 }
01014 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01015 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01016 QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
01017 }
01018 };
01019 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01020 Private::HeaderWeek, new WeekFormatter );
01021
01022 class MonthFormatter : public Private::DateTextFormatter {
01023 public:
01024 QString format( const QDateTime& dt ) {
01025 return QDate::longMonthName( dt.date().month() );
01026 }
01027 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01028 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01029 QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01030 }
01031 };
01032 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01033 Private::HeaderMonth, new MonthFormatter );
01034 }
01035
01039 void DateTimeGrid::paintMonthScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01040 qreal offset, QWidget* widget )
01041 {
01042 class MonthFormatter : public Private::DateTextFormatter {
01043 public:
01044 QString format( const QDateTime& dt ) {
01045 return QDate::shortMonthName( dt.date().month() );
01046 }
01047 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01048 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01049 QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01050 }
01051 };
01052 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01053 Private::HeaderMonth, new MonthFormatter );
01054
01055 class YearFormatter : public Private::DateTextFormatter {
01056 public:
01057 QString format( const QDateTime& dt ) {
01058 return QString::number( dt.date().year() );
01059 }
01060 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01061 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01062 QSizeF( dayWidth * dt.date().daysInYear(), headerRect.height() / 2.0 ) ).toRect();
01063 }
01064 };
01065 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01066 Private::HeaderYear, new YearFormatter );
01067 }
01068
01069 #undef d
01070
01071 #ifndef KDAB_NO_UNIT_TESTS
01072
01073 #include <QStandardItemModel>
01074 #include "unittest/test.h"
01075
01076 namespace {
01077 std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
01078 {
01079 #ifdef QT_NO_STL
01080 os << dt.toString().toLatin1().constData();
01081 #else
01082 os << dt.toString().toStdString();
01083 #endif
01084 return os;
01085 }
01086 }
01087
01088 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, DateTimeGrid, "test" ) {
01089 QStandardItemModel model( 3, 2 );
01090 DateTimeGrid grid;
01091 QDateTime dt = QDateTime::currentDateTime();
01092 grid.setModel( &model );
01093 QDateTime startdt = dt.addDays( -10 );
01094 grid.setStartDateTime( startdt );
01095
01096 model.setData( model.index( 0, 0 ), dt, StartTimeRole );
01097 model.setData( model.index( 0, 0 ), dt.addDays( 17 ), EndTimeRole );
01098
01099 model.setData( model.index( 2, 0 ), dt.addDays( 18 ), StartTimeRole );
01100 model.setData( model.index( 2, 0 ), dt.addDays( 19 ), EndTimeRole );
01101
01102 Span s = grid.mapToChart( model.index( 0, 0 ) );
01103
01104
01105 assertTrue( s.start()>0 );
01106 assertTrue( s.length()>0 );
01107
01108 assertTrue( startdt == grid.mapToDateTime( grid.mapFromDateTime( startdt ) ) );
01109
01110 grid.mapFromChart( s, model.index( 1, 0 ) );
01111
01112 QDateTime s1 = model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime();
01113 QDateTime e1 = model.data( model.index( 0, 0 ), EndTimeRole ).toDateTime();
01114 QDateTime s2 = model.data( model.index( 1, 0 ), StartTimeRole ).toDateTime();
01115 QDateTime e2 = model.data( model.index( 1, 0 ), EndTimeRole ).toDateTime();
01116
01117 assertTrue( s1.isValid() );
01118 assertTrue( e1.isValid() );
01119 assertTrue( s2.isValid() );
01120 assertTrue( e2.isValid() );
01121
01122 assertEqual( s1, s2 );
01123 assertEqual( e1, e2 );
01124
01125 assertTrue( grid.isSatisfiedConstraint( Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ) );
01126 assertFalse( grid.isSatisfiedConstraint( Constraint( model.index( 2, 0 ), model.index( 0, 0 ) ) ) );
01127
01128 s = grid.mapToChart( model.index( 0, 0 ) );
01129 s.setEnd( s.end()+100000. );
01130 bool rc = grid.mapFromChart( s, model.index( 0, 0 ) );
01131 assertTrue( rc );
01132 assertEqual( s1, model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime() );
01133 Span newspan = grid.mapToChart( model.index( 0, 0 ) );
01134 assertEqual( newspan.start(), s.start() );
01135 assertEqual( newspan.length(), s.length() );
01136
01137 {
01138 QDateTime startDateTime = QDateTime::currentDateTime();
01139 qreal dayWidth = 100;
01140 QDate currentDate = QDate::currentDate();
01141 QDateTime dt( QDate(currentDate.year(), 1, 1), QTime( 0, 0, 0, 0 ) );
01142 assert( dt.isValid() );
01143 qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
01144 result += startDateTime.time().msecsTo(dt.time())/1000.;
01145 result *= dayWidth/( 24.*60.*60. );
01146
01147 int days = static_cast<int>( result/dayWidth );
01148 qreal secs = result*( 24.*60.*60. )/dayWidth;
01149 QDateTime dt2 = startDateTime;
01150 QDateTime result2 = dt2.addDays( days ).addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) ).addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
01151
01152 assertEqual( dt, result2 );
01153 }
01154 }
01155
01156 #endif
01157
01158 #include "moc_kdganttdatetimegrid.cpp"