kdganttdatetimegrid.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 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 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 "kdganttdatetimegrid.h"
00024 #include "kdganttdatetimegrid_p.h"
00025 
00026 #include "kdganttabstractrowcontroller.h"
00027 
00028 #include <QApplication>
00029 #include <QDateTime>
00030 #include <QPainter>
00031 #include <QStyle>
00032 #include <QStyleOptionHeader>
00033 #include <QWidget>
00034 #include <QDebug>
00035 #include <QList>
00036 
00037 #include <cassert>
00038 
00039 using namespace KDGantt;
00040 
00041 QDebug operator<<( QDebug dbg, KDGantt::DateTimeScaleFormatter::Range range )
00042 {
00043     switch( range ) {
00044     case KDGantt::DateTimeScaleFormatter::Second: dbg << "KDGantt::DateTimeScaleFormatter::Second"; break;
00045     case KDGantt::DateTimeScaleFormatter::Minute: dbg << "KDGantt::DateTimeScaleFormatter::Minute"; break;
00046     case KDGantt::DateTimeScaleFormatter::Hour:   dbg << "KDGantt::DateTimeScaleFormatter::Hour"; break;
00047     case KDGantt::DateTimeScaleFormatter::Day:    dbg << "KDGantt::DateTimeScaleFormatter::Day"; break;
00048     case KDGantt::DateTimeScaleFormatter::Week:   dbg << "KDGantt::DateTimeScaleFormatter::Week"; break;
00049     case KDGantt::DateTimeScaleFormatter::Month:  dbg << "KDGantt::DateTimeScaleFormatter::Month"; break;
00050     case KDGantt::DateTimeScaleFormatter::Year:   dbg << "KDGantt::DateTimeScaleFormatter::Year"; break;
00051     }
00052     return dbg;
00053 }
00054 
00055 
00063 // TODO: I think maybe this class should be responsible
00064 // for unit-transformation of the scene...
00065 
00066 qreal DateTimeGrid::Private::dateTimeToChartX( const QDateTime& dt ) const
00067 {
00068     assert( startDateTime.isValid() );
00069     qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
00070     result += startDateTime.time().msecsTo(dt.time())/1000.;
00071     result *= dayWidth/( 24.*60.*60. );
00072 
00073     return result;
00074 }
00075 
00076 QDateTime DateTimeGrid::Private::chartXtoDateTime( qreal x ) const
00077 {
00078     assert( startDateTime.isValid() );
00079     int days = static_cast<int>( x/dayWidth );
00080     qreal secs = x*( 24.*60.*60. )/dayWidth;
00081     QDateTime dt = startDateTime;
00082     QDateTime result = dt.addDays( days )
00083                        .addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) )
00084                        .addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
00085     return result;
00086 }
00087 
00088 #define d d_func()
00089 
00117 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format,
00118                                                 const QString& templ, Qt::Alignment alignment )
00119     : _d( new Private( range, format, templ, alignment ) )
00120 {
00121 }
00122 
00123 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format, Qt::Alignment alignment )
00124     : _d( new Private( range, format, QString::fromLatin1( "%1" ), alignment ) )
00125 {
00126 }
00127 
00128 DateTimeScaleFormatter::DateTimeScaleFormatter( const DateTimeScaleFormatter& other )
00129     : _d( new Private( other.range(), other.format(), other.d->templ, other.alignment() ) )
00130 {
00131 }
00132 
00133 DateTimeScaleFormatter::~DateTimeScaleFormatter()
00134 {
00135     delete _d;
00136 }
00137 
00138 DateTimeScaleFormatter& DateTimeScaleFormatter::operator=( const DateTimeScaleFormatter& other )
00139 {
00140     delete _d;
00141     _d = new Private( other.range(), other.format(), other.d->templ, other.alignment() );
00142     return *this;
00143 }
00144 
00147 QString DateTimeScaleFormatter::format() const
00148 {
00149     return d->format;
00150 }
00151 
00154 QString DateTimeScaleFormatter::format( const QDateTime& datetime ) const
00155 {
00156     QString result = d->format;
00157     // additional feature: Weeknumber
00158     const QString shortWeekNumber = QString::number( datetime.date().weekNumber() );
00159     const QString longWeekNumber = ( shortWeekNumber.length() == 1 ? QString::fromLatin1( "0" ) : QString() ) + shortWeekNumber;
00160     result.replace( QString::fromLatin1( "ww" ), longWeekNumber );
00161     result.replace( QString::fromLatin1( "w" ), shortWeekNumber );
00162     result = datetime.toLocalTime().toString( result );
00163     return result;
00164 }
00165 
00166 QString DateTimeScaleFormatter::text( const QDateTime& datetime ) const
00167 {
00168     return d->templ.arg( format( datetime ) );
00169 }
00170 
00173 DateTimeScaleFormatter::Range DateTimeScaleFormatter::range() const
00174 {
00175     return d->range;
00176 }
00177 
00178 Qt::Alignment DateTimeScaleFormatter::alignment() const
00179 {
00180     return d->alignment;
00181 }
00182 
00186 QDateTime DateTimeScaleFormatter::nextRangeBegin( const QDateTime& datetime ) const
00187 {
00188     QDateTime result = datetime;
00189     switch( d->range )
00190     {
00191     case Second:
00192         result = result.addSecs( 60 );
00193         break;
00194     case Minute:
00195         // set it to the begin of the next minute
00196         result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00197         result = result.addSecs( 60 );
00198         break;
00199     case Hour:
00200         // set it to the begin of the next hour
00201         result.setTime( QTime( result.time().hour(), 0 ) );
00202         result = result.addSecs( 60 * 60 );
00203         break;
00204     case Day:
00205         // set it to midnight the next day
00206         result.setTime( QTime( 0, 0 ) );
00207         result = result.addDays( 1 );
00208         break;
00209     case Week:
00210         // set it to midnight
00211         result.setTime( QTime( 0, 0 ) );
00212         // iterate day-wise, until weekNumber changes
00213         {
00214             const int weekNumber = result.date().weekNumber();
00215             while( weekNumber == result.date().weekNumber() )
00216                 result = result.addDays( 1 );
00217         }
00218         break;
00219     case Month:
00220         // set it to midnight
00221         result.setTime( QTime( 0, 0 ) );
00222         // set it to the first of the next month
00223         result.setDate( QDate( result.date().year(), result.date().month(), 1 ).addMonths( 1 ) );
00224         break;
00225     case Year:
00226         // set it to midnight
00227         result.setTime( QTime( 0, 0 ) );
00228         // set it to the first of the next year
00229         result.setDate( QDate( result.date().year(), 1, 1 ).addYears( 1 ) );
00230         break;
00231     }
00232     //result = result.toLocalTime();
00233     assert(  result != datetime );
00234     //qDebug() << "DateTimeScaleFormatter::nextRangeBegin("<<datetime<<")="<<d->range<<result;
00235     return result;
00236 }
00237 
00241 QDateTime DateTimeScaleFormatter::currentRangeBegin( const QDateTime& datetime ) const
00242 {
00243     QDateTime result = datetime;
00244     switch( d->range )
00245     {
00246     case Second:
00247         break; // nothing
00248     case Minute:
00249         // set it to the begin of the current minute
00250         result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00251         break;
00252     case Hour:
00253         // set it to the begin of the current hour
00254         result.setTime( QTime( result.time().hour(), 0 ) );
00255         break;
00256     case Day:
00257         // set it to midnight the current day
00258         result.setTime( QTime( 0, 0 ) );
00259         break;
00260     case Week:
00261         // set it to midnight
00262         result.setTime( QTime( 0, 0 ) );
00263         // iterate day-wise, as long weekNumber is the same
00264         {
00265             const int weekNumber = result.date().weekNumber();
00266             while( weekNumber == result.date().addDays( -1 ).weekNumber() )
00267                 result = result.addDays( -1 );
00268         }
00269         break;
00270     case Month:
00271         // set it to midnight
00272         result.setTime( QTime( 0, 0 ) );
00273         // set it to the first of the current month
00274         result.setDate( QDate( result.date().year(), result.date().month(), 1 ) );
00275         break;
00276     case Year:
00277         // set it to midnight
00278         result.setTime( QTime( 0, 0 ) );
00279         // set it to the first of the current year
00280         result.setDate( QDate( result.date().year(), 1, 1 ) );
00281         break;
00282     }
00283     return result;
00284 }
00285 
00286 DateTimeGrid::DateTimeGrid() : AbstractGrid( new Private )
00287 {
00288 }
00289 
00290 DateTimeGrid::~DateTimeGrid()
00291 {
00292 }
00293 
00298 QDateTime DateTimeGrid::startDateTime() const
00299 {
00300     return d->startDateTime;
00301 }
00302 
00308 void DateTimeGrid::setStartDateTime( const QDateTime& dt )
00309 {
00310     d->startDateTime = dt;
00311     emit gridChanged();
00312 }
00313 
00318 qreal DateTimeGrid::dayWidth() const
00319 {
00320     return d->dayWidth;
00321 }
00322 
00325 qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
00326 {
00327     return d->dateTimeToChartX( dt );
00328 }
00329 
00332 QDateTime DateTimeGrid::mapToDateTime( qreal x ) const
00333 {
00334     return d->chartXtoDateTime( x );
00335 }
00336 
00341 void DateTimeGrid::setDayWidth( qreal w )
00342 {
00343     assert( w>0 );
00344     d->dayWidth = w;
00345     emit gridChanged();
00346 }
00347 
00353 void DateTimeGrid::setScale( Scale s )
00354 {
00355     d->scale = s;
00356     emit gridChanged();
00357 }
00358 
00365 DateTimeGrid::Scale DateTimeGrid::scale() const
00366 {
00367     return d->scale;
00368 }
00369 
00377 void DateTimeGrid::setUserDefinedLowerScale( DateTimeScaleFormatter* lower )
00378 {
00379     delete d->lower;
00380     d->lower = lower;
00381     emit gridChanged();
00382 }
00383 
00391 void DateTimeGrid::setUserDefinedUpperScale( DateTimeScaleFormatter* upper )
00392 {
00393     delete d->upper;
00394     d->upper = upper;
00395     emit gridChanged();
00396 }
00397 
00400 DateTimeScaleFormatter* DateTimeGrid::userDefinedLowerScale() const
00401 {
00402     return d->lower;
00403 }
00404 
00407 DateTimeScaleFormatter* DateTimeGrid::userDefinedUpperScale() const
00408 {
00409     return d->upper;
00410 }
00411 
00417 void DateTimeGrid::setWeekStart( Qt::DayOfWeek ws )
00418 {
00419     d->weekStart = ws;
00420     emit gridChanged();
00421 }
00422 
00424 Qt::DayOfWeek DateTimeGrid::weekStart() const
00425 {
00426     return d->weekStart;
00427 }
00428 
00435 void DateTimeGrid::setFreeDays( const QSet<Qt::DayOfWeek>& fd )
00436 {
00437     d->freeDays = fd;
00438     emit gridChanged();
00439 }
00440 
00442 QSet<Qt::DayOfWeek> DateTimeGrid::freeDays() const
00443 {
00444     return d->freeDays;
00445 }
00446 
00449 void DateTimeGrid::setFreeDaysBrush(const QBrush brush)
00450 {
00451     d->freeDaysBrush = brush;
00452 }
00453 
00457 QBrush DateTimeGrid::freeDaysBrush() const
00458 {
00459     return d->freeDaysBrush;
00460 }
00461 
00463 bool DateTimeGrid::rowSeparators() const
00464 {
00465     return d->rowSeparators;
00466 }
00468 void DateTimeGrid::setRowSeparators( bool enable )
00469 {
00470     d->rowSeparators = enable;
00471 }
00472 
00477 void DateTimeGrid::setNoInformationBrush( const QBrush& brush )
00478 {
00479     d->noInformationBrush = brush;
00480     emit gridChanged();
00481 }
00482 
00485 QBrush DateTimeGrid::noInformationBrush() const
00486 {
00487     return d->noInformationBrush;
00488 }
00489 
00494 qreal DateTimeGrid::mapToChart( const QVariant& value ) const
00495 {
00496     if ( ! qVariantCanConvert<QDateTime>( value ) ||
00497          ( value.type() == QVariant::String && qVariantValue<QString>(value).isEmpty() ) )
00498     {
00499         return -1.0;
00500     }
00501     return d->dateTimeToChartX( value.toDateTime() );
00502 }
00503 
00508 QVariant DateTimeGrid::mapFromChart( qreal x ) const
00509 {
00510     return d->chartXtoDateTime( x );
00511 }
00512 
00516 Span DateTimeGrid::mapToChart( const QModelIndex& idx ) const
00517 {
00518     assert( model() );
00519     if ( !idx.isValid() ) return Span();
00520     assert( idx.model()==model() );
00521     const QVariant sv = model()->data( idx, StartTimeRole );
00522     const QVariant ev = model()->data( idx, EndTimeRole );
00523     if( qVariantCanConvert<QDateTime>(sv) &&
00524     qVariantCanConvert<QDateTime>(ev) &&
00525     !(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) &&
00526     !(ev.type() == QVariant::String && qVariantValue<QString>(ev).isEmpty())
00527     ) {
00528       QDateTime st = sv.toDateTime();
00529       QDateTime et = ev.toDateTime();
00530       if ( et.isValid() && st.isValid() ) {
00531         qreal sx = d->dateTimeToChartX( st );
00532         qreal ex = d->dateTimeToChartX( et )-sx;
00533         //qDebug() << "DateTimeGrid::mapToChart("<<st<<et<<") => "<< Span( sx, ex );
00534         return Span( sx, ex);
00535       }
00536     }
00537     // Special case for Events with only a start date
00538     if( qVariantCanConvert<QDateTime>(sv) && !(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) ) {
00539       QDateTime st = sv.toDateTime();
00540       if ( st.isValid() ) {
00541         qreal sx = d->dateTimeToChartX( st );
00542         return Span( sx, 0 );
00543       }
00544     }
00545     return Span();
00546 }
00547 
00548 #if 0
00549 static void debug_print_idx( const QModelIndex& idx )
00550 {
00551     if ( !idx.isValid() ) {
00552         qDebug() << "[Invalid]";
00553         return;
00554     }
00555     QDateTime st = idx.data( StartTimeRole ).toDateTime();
00556     QDateTime et = idx.data( EndTimeRole ).toDateTime();
00557     qDebug() << idx << "["<<st<<et<<"]";
00558 }
00559 #endif
00560 
00575 bool DateTimeGrid::mapFromChart( const Span& span, const QModelIndex& idx,
00576     const QList<Constraint>& constraints ) const
00577 {
00578     assert( model() );
00579     if ( !idx.isValid() ) return false;
00580     assert( idx.model()==model() );
00581 
00582     QDateTime st = d->chartXtoDateTime(span.start());
00583     QDateTime et = d->chartXtoDateTime(span.start()+span.length());
00584     //qDebug() << "DateTimeGrid::mapFromChart("<<span<<") => "<< st << et;
00585     Q_FOREACH( const Constraint& c, constraints ) {
00586         if ( c.type() != Constraint::TypeHard || !isSatisfiedConstraint( c )) continue;
00587         if ( c.startIndex() == idx ) {
00588             QDateTime tmpst = model()->data( c.endIndex(), StartTimeRole ).toDateTime();
00589             //qDebug() << tmpst << "<" << et <<"?";
00590             if ( tmpst<et ) return false;
00591         } else if ( c.endIndex() == idx ) {
00592             QDateTime tmpet = model()->data( c.startIndex(), EndTimeRole ).toDateTime();
00593             //qDebug() << tmpet << ">" << st <<"?";
00594             if ( tmpet>st ) return false;
00595         }
00596     }
00597     return model()->setData( idx, qVariantFromValue(st), StartTimeRole )
00598         && model()->setData( idx, qVariantFromValue(et), EndTimeRole );
00599 }
00600 
00601 Qt::PenStyle DateTimeGrid::Private::gridLinePenStyle( QDateTime dt, Private::HeaderType headerType ) const
00602 {
00603     switch ( headerType ) {
00604         case Private::HeaderHour:
00605             // Midnight
00606             if ( dt.time().hour() == 0 )
00607                 return Qt::SolidLine;
00608             return Qt::DashLine;
00609         case Private::HeaderDay:
00610             // First day of the week
00611             if ( dt.date().dayOfWeek() == weekStart )
00612                 return Qt::SolidLine;
00613             return Qt::DashLine;
00614         case Private::HeaderWeek:
00615             // First day of the month
00616             if ( dt.date().day() == 1 )
00617                 return Qt::SolidLine;
00618             // First day of the week
00619             if ( dt.date().dayOfWeek() == weekStart )
00620                 return Qt::DashLine;
00621             return Qt::NoPen;
00622         case Private::HeaderMonth:
00623             // First day of the year
00624             if ( dt.date().dayOfYear() == 1 )
00625                 return Qt::SolidLine;
00626             // First day of the month
00627             if ( dt.date().day() == 1 )
00628                 return Qt::DashLine;
00629             return Qt::NoPen;
00630         default:
00631             // Nothing to do here
00632             break;
00633    }
00634 
00635     // Default
00636     return Qt::NoPen;
00637 }
00638 
00639 QDateTime DateTimeGrid::Private::adjustDateTimeForHeader( QDateTime dt, Private::HeaderType headerType ) const
00640 {
00641     // In any case, set time to 00:00:00:00
00642     dt.setTime( QTime( 0, 0, 0, 0 ) );
00643 
00644     switch ( headerType ) {
00645         case Private::HeaderWeek:
00646             // Set day to beginning of the week
00647             while ( dt.date().dayOfWeek() != weekStart )
00648                 dt = dt.addDays( -1 );
00649             break;
00650         case Private::HeaderMonth:
00651             // Set day to beginning of the month
00652             dt = dt.addDays( 1 - dt.date().day() );
00653             break;
00654         case Private::HeaderYear:
00655             // Set day to first day of the year
00656             dt = dt.addDays( 1 - dt.date().dayOfYear() );
00657             break;
00658         default:
00659             // In any other case, we don't need to adjust the date time
00660             break;
00661     }
00662 
00663     return dt;
00664 }
00665 
00666 void DateTimeGrid::Private::paintVerticalLines( QPainter* painter,
00667                                                 const QRectF& sceneRect,
00668                                                 const QRectF& exposedRect,
00669                                                 QWidget* widget,
00670                                                 Private::HeaderType headerType )
00671 {
00672         QDateTime dt = chartXtoDateTime( exposedRect.left() );
00673         dt = adjustDateTimeForHeader( dt, headerType );
00674 
00675         int offsetSeconds = 0;
00676         int offsetDays = 0;
00677         // Determine the time step per grid line
00678         if ( headerType == Private::HeaderHour )
00679             offsetSeconds = 60*60;
00680         else
00681             offsetDays = 1;
00682 
00683         for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
00684               dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), x = dateTimeToChartX( dt ) ) {
00685             if ( x >= exposedRect.left() ) {
00686                 QPen pen = painter->pen();
00687                 pen.setBrush( QApplication::palette().dark() );
00688                 pen.setStyle( gridLinePenStyle( dt, headerType ) );
00689                 painter->setPen( pen );
00690                 if ( freeDays.contains( static_cast<Qt::DayOfWeek>( dt.date().dayOfWeek() ) ) ) {
00691                     if(freeDaysBrush.style() == Qt::NoBrush)
00692                         painter->setBrush( widget?widget->palette().midlight()
00693                                            :QApplication::palette().midlight() );
00694                     else
00695                         painter->setBrush(freeDaysBrush);
00696 
00697                     painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() );
00698                 }
00699                 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00700             }
00701         }
00702 }
00703 
00704 void DateTimeGrid::Private::paintVerticalUserDefinedLines( QPainter* painter,
00705                                                            const QRectF& sceneRect,
00706                                                            const QRectF& exposedRect,
00707                                                            const DateTimeScaleFormatter* formatter,
00708                                                            QWidget* widget )
00709 {
00710     Q_UNUSED( widget );
00711     QDateTime dt = chartXtoDateTime( exposedRect.left() );
00712     dt = formatter->currentRangeBegin( dt );
00713     QPen pen = painter->pen();
00714     pen.setBrush( QApplication::palette().dark() );
00715     pen.setStyle( Qt::DashLine );
00716     painter->setPen( pen );
00717     for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
00718           dt = formatter->nextRangeBegin( dt ),x=dateTimeToChartX( dt ) ) {
00719         if ( x >= exposedRect.left() ) {
00720             // FIXME: Also fill area between this and the next vertical line to indicate free days? (Johannes)
00721             painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00722         }
00723     }
00724 }
00725 
00726 DateTimeGrid::Private::HeaderType DateTimeGrid::Private::headerTypeForScale( DateTimeGrid::Scale scale )
00727 {
00728     switch ( scale ) {
00729         case ScaleHour:
00730             return Private::HeaderHour;
00731         case ScaleDay:
00732             return Private::HeaderDay;
00733         case ScaleWeek:
00734             return Private::HeaderWeek;
00735         case ScaleMonth:
00736             return Private::HeaderMonth;
00737         default:
00738             // There are no specific header types for any other scale!
00739             assert( false );
00740             break;
00741     }
00742 }
00743 
00744 void DateTimeGrid::paintGrid( QPainter* painter,
00745                               const QRectF& sceneRect,
00746                               const QRectF& exposedRect,
00747                               AbstractRowController* rowController,
00748                               QWidget* widget )
00749 {
00750     // TODO: Support hours and weeks
00751     switch( scale() ) {
00752     case ScaleHour:
00753     case ScaleDay:
00754     case ScaleWeek:
00755     case ScaleMonth:
00756         d->paintVerticalLines( painter, sceneRect, exposedRect, widget, d->headerTypeForScale( scale() ) );
00757         break;
00758     case ScaleAuto: {
00759         const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00760         const qreal dayw = dayWidth();
00761         if ( dayw > 24*60*60*tabw ) {
00762 
00763             d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->minute_lower, widget );
00764         } else if ( dayw > 24*60*tabw ) {
00765             d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderHour );
00766         } else if ( dayw > 24*tabw ) {
00767         d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderDay );
00768         } else if ( dayw > tabw ) {
00769             d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->week_lower, widget );
00770         } else if ( 4*dayw > tabw ) {
00771             d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->month_lower, widget );
00772         } else {
00773             d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->year_lower, widget );
00774         }
00775         break;
00776     }
00777     case ScaleUserDefined:
00778         d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, d->lower, widget );
00779         break;
00780     }
00781     if ( rowController ) {
00782         // First draw the rows
00783         QPen pen = painter->pen();
00784         pen.setBrush( QApplication::palette().dark() );
00785         pen.setStyle( Qt::DashLine );
00786         painter->setPen( pen );
00787         QModelIndex idx = rowController->indexAt( qRound( exposedRect.top() ) );
00788         if ( rowController->indexAbove( idx ).isValid() ) idx = rowController->indexAbove( idx );
00789         qreal y = 0;
00790         while ( y < exposedRect.bottom() && idx.isValid() ) {
00791             const Span s = rowController->rowGeometry( idx );
00792             y = s.start()+s.length();
00793             if ( d->rowSeparators ) {
00794                 painter->drawLine( QPointF( sceneRect.left(), y ),
00795                                    QPointF( sceneRect.right(), y ) );
00796             }
00797             if ( !idx.data( ItemTypeRole ).isValid() && d->noInformationBrush.style() != Qt::NoBrush ) {
00798                 painter->fillRect( QRectF( exposedRect.left(), s.start(), exposedRect.width(), s.length() ), d->noInformationBrush );
00799             }
00800             // Is alternating background better?
00801             //if ( idx.row()%2 ) painter->fillRect( QRectF( exposedRect.x(), s.start(), exposedRect.width(), s.length() ), QApplication::palette().alternateBase() );
00802             idx =  rowController->indexBelow( idx );
00803         }
00804     }
00805 }
00806 
00807 int DateTimeGrid::Private::tabHeight( const QString& txt, QWidget* widget ) const
00808 {
00809     QStyleOptionHeader opt;
00810     if ( widget ) opt.initFrom( widget );
00811     opt.text = txt;
00812     QStyle* style;
00813     if ( widget ) style = widget->style();
00814     else style = QApplication::style();
00815     QSize s = style->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), widget);
00816     return s.height();
00817 }
00818 
00819 void DateTimeGrid::Private::getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper)
00820 {
00821     const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00822     const qreal dayw = dayWidth;
00823     if ( dayw > 24*60*60*tabw ) {
00824         *lower = &minute_lower;
00825         *upper = &minute_upper;
00826     } else if ( dayw > 24*60*tabw ) {
00827         *lower = &hour_lower;
00828         *upper = &hour_upper;
00829     } else if ( dayw > 24*tabw ) {
00830         *lower = &day_lower;
00831         *upper = &day_upper;
00832     } else if ( dayw > tabw ) {
00833         *lower = &week_lower;
00834         *upper = &week_upper;
00835     } else if ( 4*dayw > tabw ) {
00836         *lower = &month_lower;
00837         *upper = &month_upper;
00838     } else {
00839         *lower = &year_lower;
00840         *upper = &year_upper;
00841     }
00842 }
00843 
00844 
00845 void DateTimeGrid::paintHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
00846                                 qreal offset, QWidget* widget )
00847 {
00848     painter->save();
00849     QPainterPath clipPath;
00850     clipPath.addRect( headerRect );
00851     painter->setClipPath( clipPath, Qt::IntersectClip );
00852     switch( scale() )
00853     {
00854     case ScaleHour:
00855         paintHourScaleHeader( painter, headerRect, exposedRect, offset, widget );
00856         break;
00857     case ScaleDay:
00858         paintDayScaleHeader( painter, headerRect, exposedRect, offset, widget );
00859         break;
00860     case ScaleWeek:
00861         paintWeekScaleHeader( painter, headerRect, exposedRect, offset, widget );
00862         break;
00863     case ScaleMonth:
00864         paintMonthScaleHeader( painter, headerRect, exposedRect, offset, widget );
00865         break;
00866     case ScaleAuto:
00867         {
00868             DateTimeScaleFormatter *lower, *upper;
00869             d->getAutomaticFormatters( &lower, &upper );
00870             const qreal lowerHeight = d->tabHeight( lower->text( startDateTime() ) );
00871             const qreal upperHeight = d->tabHeight( upper->text( startDateTime() ) );
00872             const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00873 
00874             const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00875             const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1,  headerRect.height()-upperHeaderRect.height()-1 );
00876 
00877             paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, lower, widget );
00878             paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, upper, widget );
00879             break;
00880         }
00881     case ScaleUserDefined:
00882         {
00883             const qreal lowerHeight = d->tabHeight( d->lower->text( startDateTime() ) );
00884             const qreal upperHeight = d->tabHeight( d->upper->text( startDateTime() ) );
00885             const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00886 
00887             const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00888             const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1,  headerRect.height()-upperHeaderRect.height()-1 );
00889 
00890             paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, d->lower, widget );
00891             paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, d->upper, widget );
00892         }
00893         break;
00894     }
00895     painter->restore();
00896 }
00897 
00898 void DateTimeGrid::paintUserDefinedHeader( QPainter* painter,
00899                                            const QRectF& headerRect, const QRectF& exposedRect,
00900                                            qreal offset, const DateTimeScaleFormatter* formatter,
00901                                            QWidget* widget )
00902 {
00903     const QStyle* const style = widget ? widget->style() : QApplication::style();
00904 
00905     QDateTime dt = formatter->currentRangeBegin( d->chartXtoDateTime( offset + exposedRect.left() ));
00906     qreal x = d->dateTimeToChartX( dt );
00907 
00908     while( x < exposedRect.right() + offset ) {
00909         const QDateTime next = formatter->nextRangeBegin( dt );
00910         const qreal nextx = d->dateTimeToChartX( next );
00911 
00912         QStyleOptionHeader opt;
00913         if ( widget ) opt.init( widget );
00914         opt.rect = QRectF( x - offset+1, headerRect.top(), qMax( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect();
00915         opt.textAlignment = formatter->alignment();
00916         opt.text = formatter->text( dt );
00917         style->drawControl( QStyle::CE_Header, &opt, painter, widget );
00918 
00919         dt = next;
00920         x = nextx;
00921     }
00922 }
00923 
00924 void DateTimeGrid::Private::paintHeader( QPainter* painter,
00925                                          const QRectF& headerRect, const QRectF& exposedRect,
00926                                          qreal offset, QWidget* widget,
00927                                          Private::HeaderType headerType,
00928                                          DateTextFormatter *formatter )
00929 {
00930     QStyle* style = widget?widget->style():QApplication::style();
00931 
00932     const qreal left = exposedRect.left() + offset;
00933     const qreal right = exposedRect.right() + offset;
00934 
00935     // Paint a section for each hour
00936     QDateTime dt = chartXtoDateTime( left );
00937     dt = adjustDateTimeForHeader( dt, headerType );
00938     // Determine the time step per grid line
00939     int offsetSeconds = 0;
00940     int offsetDays = 0;
00941     int offsetMonths = 0;
00942 
00943     switch ( headerType ) {
00944         case Private::HeaderHour:
00945             offsetSeconds = 60*60;
00946             break;
00947         case Private::HeaderDay:
00948             offsetDays = 1;
00949             break;
00950         case Private::HeaderWeek:
00951             offsetDays = 7;
00952             break;
00953         case Private::HeaderMonth:
00954             offsetMonths = 1;
00955             break;
00956         case Private::HeaderYear:
00957             offsetMonths = 12;
00958             break;
00959         default:
00960             // Other scales cannot be painted with this method!
00961             assert( false );
00962             break;
00963     }
00964 
00965     for ( qreal x = dateTimeToChartX( dt ); x < right;
00966           dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), dt = dt.addMonths( offsetMonths ),
00967           x = dateTimeToChartX( dt ) ) {
00968         QStyleOptionHeader opt;
00969         if ( widget ) opt.init( widget );
00970         opt.rect = formatter->textRect( x, offset, dayWidth, headerRect, dt );
00971         opt.text = formatter->format( dt );
00972         opt.textAlignment = Qt::AlignCenter;
00973         style->drawControl(QStyle::CE_Header, &opt, painter, widget);
00974     }
00975 }
00976 
00980 void DateTimeGrid::paintHourScaleHeader( QPainter* painter,
00981                                          const QRectF& headerRect, const QRectF& exposedRect,
00982                                          qreal offset, QWidget* widget )
00983 {
00984     class HourFormatter : public Private::DateTextFormatter {
00985     public:
00986         virtual ~HourFormatter() {}
00987 
00988         QString format( const QDateTime& dt ) {
00989             return dt.time().toString( QString::fromAscii( "hh" ) );
00990         }
00991         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
00992             Q_UNUSED(dt);
00993 
00994             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
00995                            QSizeF( dayWidth / 24.0, headerRect.height() / 2.0 ) ).toAlignedRect();
00996         }
00997     };
00998     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
00999                     Private::HeaderHour, new HourFormatter ); // Custom parameters
01000 
01001     class DayFormatter : public Private::DateTextFormatter {
01002     public:
01003         virtual ~DayFormatter() {}
01004         QString format( const QDateTime& dt ) {
01005             return dt.date().toString();
01006         }
01007         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01008             Q_UNUSED(dt);
01009 
01010             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01011                            QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toRect();
01012         }
01013     };
01014     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
01015                     Private::HeaderDay, new DayFormatter ); // Custom parameters
01016 }
01017 
01021 void DateTimeGrid::paintDayScaleHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
01022                                 qreal offset, QWidget* widget )
01023 {
01024     class DayFormatter : public Private::DateTextFormatter {
01025     public:
01026         virtual ~DayFormatter() {}
01027 
01028         QString format( const QDateTime& dt ) {
01029             return dt.toString( QString::fromAscii( "ddd" ) ).left( 1 );
01030         }
01031         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01032             Q_UNUSED(dt);
01033 
01034             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
01035                            QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toAlignedRect();
01036         }
01037     };
01038     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
01039                     Private::HeaderDay, new DayFormatter ); // Custom parameters
01040 
01041     class WeekFormatter : public Private::DateTextFormatter {
01042     public:
01043         virtual ~WeekFormatter() {}
01044         QString format( const QDateTime& dt ) {
01045             return QString::number( dt.date().weekNumber() );
01046         }
01047         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01048             Q_UNUSED(dt);
01049 
01050             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01051                            QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
01052         }
01053     };
01054     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
01055                     Private::HeaderWeek, new WeekFormatter ); // Custom parameters
01056 }
01057 
01061 void DateTimeGrid::paintWeekScaleHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
01062                                         qreal offset, QWidget* widget )
01063 {
01064     class WeekFormatter : public Private::DateTextFormatter {
01065     public:
01066         virtual ~WeekFormatter() {}
01067 
01068         QString format( const QDateTime& dt ) {
01069             return QString::number( dt.date().weekNumber() );
01070         }
01071         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01072             Q_UNUSED(dt);
01073 
01074             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01075                            QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
01076         }
01077     };
01078     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
01079                     Private::HeaderWeek, new WeekFormatter ); // Custom parameters
01080 
01081     class MonthFormatter : public Private::DateTextFormatter {
01082     public:
01083         virtual ~MonthFormatter() {}
01084 
01085         QString format( const QDateTime& dt ) {
01086             return QDate::longMonthName( dt.date().month() );
01087         }
01088         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01089             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01090                            QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01091         }
01092     };
01093     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
01094                     Private::HeaderMonth, new MonthFormatter ); // Custom parameters
01095 }
01096 
01100 void DateTimeGrid::paintMonthScaleHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
01101                                         qreal offset, QWidget* widget )
01102 {
01103     class MonthFormatter : public Private::DateTextFormatter {
01104     public:
01105         virtual ~MonthFormatter() {}
01106 
01107         QString format( const QDateTime& dt ) {
01108             return QDate::shortMonthName( dt.date().month() );
01109         }
01110         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01111             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01112                            QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01113         }
01114     };
01115     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
01116                     Private::HeaderMonth, new MonthFormatter ); // Custom parameters
01117 
01118     class YearFormatter : public Private::DateTextFormatter {
01119     public:
01120         virtual ~YearFormatter() {}
01121 
01122         QString format( const QDateTime& dt ) {
01123             return QString::number( dt.date().year() );
01124         }
01125         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01126             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01127                            QSizeF( dayWidth * dt.date().daysInYear(), headerRect.height() / 2.0 ) ).toRect();
01128         }
01129     };
01130     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
01131                     Private::HeaderYear, new YearFormatter ); // Custom parameters
01132 }
01133 
01137 void DateTimeGrid::drawDayBackground(QPainter* painter, const QRectF& rect, const QDate& date)
01138 {
01139     Q_UNUSED(painter);
01140     Q_UNUSED(rect);
01141     Q_UNUSED(date);
01142 }
01143 
01147 void DateTimeGrid::drawDayForeground(QPainter* painter, const QRectF& rect, const QDate& date)
01148 {
01149     Q_UNUSED(painter);
01150     Q_UNUSED(rect);
01151     Q_UNUSED(date);
01152 }
01153 
01157 QRectF DateTimeGrid::computeRect(const QDateTime& from, const QDateTime& to, const QRectF& rect) const
01158 {
01159     qreal topLeft = d->dateTimeToChartX(from);
01160     qreal topRight = d->dateTimeToChartX(to);
01161 
01162     return QRectF(topLeft, rect.top(), topRight - topLeft, rect.height());
01163 }
01164 
01168 QPair<QDateTime, QDateTime> DateTimeGrid::dateTimeRange(const QRectF& rect) const
01169 {
01170     QDateTime start;
01171     QDateTime end;
01172 
01173     start = d->chartXtoDateTime(rect.left());
01174     end = d->chartXtoDateTime(rect.right());
01175 
01176     return qMakePair(start, end);
01177 }
01178 
01179 void DateTimeGrid::drawBackground(QPainter* paint, const QRectF& rect)
01180 {
01181     int offset = (int)dayWidth();
01182 
01183     // Figure out the date at the extreme left
01184     QDate date = d->chartXtoDateTime(rect.left()).date();
01185 
01186     // We need to paint from one end to the other
01187     int startx = rect.left();
01188     int endx = rect.right();
01189 
01190     // Save the painter state
01191     paint->save();
01192 
01193     // Paint the first date column
01194     while(1)
01195     {
01196         QDate nextDate = d->chartXtoDateTime(startx+1).date();
01197         if(date != nextDate)
01198         {
01199             QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
01200             dayRect = dayRect.adjusted(1, 0, 0, 0);
01201             drawDayBackground(paint, dayRect, date);
01202             break;
01203         }
01204 
01205         ++startx;
01206     }
01207 
01208     // Paint the remaining dates
01209     for(int i=startx; i<endx; i+=offset)
01210     {
01211         date = d->chartXtoDateTime(i+1).date();
01212 
01213         QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
01214         dayRect = dayRect.adjusted(1, 0, 0, 0);
01215         drawDayBackground(paint, dayRect, date);
01216     }
01217 
01218     // Restore the painter state
01219     paint->restore();
01220 }
01221 
01222 void DateTimeGrid::drawForeground(QPainter* paint, const QRectF& rect)
01223 {
01224     int offset = (int)dayWidth();
01225 
01226     // Figure out the date at the extreme left
01227     QDate date = d->chartXtoDateTime(rect.left()).date();
01228 
01229     // We need to paint from one end to the other
01230     int startx = rect.left();
01231     int endx = rect.right();
01232 
01233     // Save the painter state
01234     paint->save();
01235 
01236     // Paint the first date column
01237     while(1)
01238     {
01239         QDate nextDate = d->chartXtoDateTime(startx+1).date();
01240         if(date != nextDate)
01241         {
01242             QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
01243             dayRect = dayRect.adjusted(1, 0, 0, 0);
01244             drawDayForeground(paint, dayRect, date);
01245             break;
01246         }
01247 
01248         ++startx;
01249     }
01250 
01251     // Paint the remaining dates
01252     for(int i=startx; i<endx; i+=offset)
01253     {
01254         date = d->chartXtoDateTime(i+1).date();
01255 
01256         QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
01257         dayRect = dayRect.adjusted(1, 0, 0, 0);
01258         drawDayForeground(paint, dayRect, date);
01259     }
01260 
01261     // Restore the painter state
01262     paint->restore();
01263 }
01264 
01265 #undef d
01266 
01267 #ifndef KDAB_NO_UNIT_TESTS
01268 
01269 #include <QStandardItemModel>
01270 #include "unittest/test.h"
01271 
01272 namespace {
01273     std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
01274     {
01275 #ifdef QT_NO_STL
01276         os << dt.toString().toLatin1().constData();
01277 #else
01278         os << dt.toString().toStdString();
01279 #endif
01280         return os;
01281     }
01282 }
01283 
01284 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, DateTimeGrid, "test" ) {
01285     QStandardItemModel model( 3, 2 );
01286     DateTimeGrid grid;
01287     QDateTime dt = QDateTime::currentDateTime();
01288     grid.setModel( &model );
01289     QDateTime startdt = dt.addDays( -10 );
01290     grid.setStartDateTime( startdt );
01291 
01292     model.setData( model.index( 0, 0 ), dt,               StartTimeRole );
01293     model.setData( model.index( 0, 0 ), dt.addDays( 17 ), EndTimeRole );
01294 
01295     model.setData( model.index( 2, 0 ), dt.addDays( 18 ), StartTimeRole );
01296     model.setData( model.index( 2, 0 ), dt.addDays( 19 ), EndTimeRole );
01297 
01298     Span s = grid.mapToChart( model.index( 0, 0 ) );
01299     //qDebug() << "span="<<s;
01300 
01301     assertTrue( s.start()>0 );
01302     assertTrue( s.length()>0 );
01303 
01304     assertTrue( startdt == grid.mapToDateTime( grid.mapFromDateTime( startdt ) ) );
01305 
01306     grid.mapFromChart( s, model.index( 1, 0 ) );
01307 
01308     QDateTime s1 = model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime();
01309     QDateTime e1 = model.data( model.index( 0, 0 ), EndTimeRole ).toDateTime();
01310     QDateTime s2 = model.data( model.index( 1, 0 ), StartTimeRole ).toDateTime();
01311     QDateTime e2 = model.data( model.index( 1, 0 ), EndTimeRole ).toDateTime();
01312 
01313     assertTrue( s1.isValid() );
01314     assertTrue( e1.isValid() );
01315     assertTrue( s2.isValid() );
01316     assertTrue( e2.isValid() );
01317 
01318     assertEqual( s1, s2 );
01319     assertEqual( e1, e2 );
01320 
01321     assertTrue( grid.isSatisfiedConstraint( Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ) );
01322     assertFalse( grid.isSatisfiedConstraint( Constraint( model.index( 2, 0 ), model.index( 0, 0 ) ) ) );
01323 
01324     s = grid.mapToChart( model.index( 0, 0 ) );
01325     s.setEnd( s.end()+100000. );
01326     bool rc = grid.mapFromChart( s, model.index( 0, 0 ) );
01327     assertTrue( rc );
01328     assertEqual( s1, model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime() );
01329     Span newspan = grid.mapToChart( model.index( 0, 0 ) );
01330     assertEqual( newspan.start(), s.start() );
01331     assertEqual( newspan.length(), s.length() );
01332 
01333     {
01334         QDateTime startDateTime = QDateTime::currentDateTime();
01335         qreal dayWidth = 100;
01336         QDate currentDate = QDate::currentDate();
01337         QDateTime dt( QDate(currentDate.year(), 1, 1),  QTime( 0, 0, 0, 0 ) );
01338         assert( dt.isValid() );
01339         qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
01340         result += startDateTime.time().msecsTo(dt.time())/1000.;
01341         result *= dayWidth/( 24.*60.*60. );
01342 
01343         int days = static_cast<int>( result/dayWidth );
01344         qreal secs = result*( 24.*60.*60. )/dayWidth;
01345         QDateTime dt2 = startDateTime;
01346         QDateTime result2 = dt2.addDays( days ).addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) ).addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
01347 
01348         assertEqual( dt, result2 );
01349     }
01350 }
01351 
01352 #endif /* KDAB_NO_UNIT_TESTS */
01353 
01354 #include "moc_kdganttdatetimegrid.cpp"