File indexing completed on 2024-05-12 04:20:40

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KGantt library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "kganttdatetimegrid.h"
0010 #include "kganttdatetimegrid_p.h"
0011 
0012 #include "kganttabstractrowcontroller.h"
0013 
0014 #include <QApplication>
0015 #include <QLocale>
0016 #include <QPainter>
0017 #include <QPainterPath>
0018 #include <QStyle>
0019 #include <QStyleOptionHeader>
0020 #include <QWidget>
0021 #include <QString>
0022 #include <QDebug>
0023 #include <QList>
0024 #include <QPainterPath>
0025 
0026 #include <cassert>
0027 
0028 using namespace KGantt;
0029 
0030 QDebug operator<<( QDebug dbg, KGantt::DateTimeScaleFormatter::Range range )
0031 {
0032     switch ( range ) {
0033     case KGantt::DateTimeScaleFormatter::Second: dbg << "KGantt::DateTimeScaleFormatter::Second"; break;
0034     case KGantt::DateTimeScaleFormatter::Minute: dbg << "KGantt::DateTimeScaleFormatter::Minute"; break;
0035     case KGantt::DateTimeScaleFormatter::Hour:   dbg << "KGantt::DateTimeScaleFormatter::Hour"; break;
0036     case KGantt::DateTimeScaleFormatter::Day:    dbg << "KGantt::DateTimeScaleFormatter::Day"; break;
0037     case KGantt::DateTimeScaleFormatter::Week:   dbg << "KGantt::DateTimeScaleFormatter::Week"; break;
0038     case KGantt::DateTimeScaleFormatter::Month:  dbg << "KGantt::DateTimeScaleFormatter::Month"; break;
0039     case KGantt::DateTimeScaleFormatter::Year:   dbg << "KGantt::DateTimeScaleFormatter::Year"; break;
0040     }
0041     return dbg;
0042 }
0043 
0044 
0045 
0046 
0047 qreal DateTimeGrid::Private::dateTimeToChartX( const QDateTime& dt ) const
0048 {
0049     assert( startDateTime.isValid() );
0050     qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
0051     result += startDateTime.time().msecsTo(dt.time())/1000.;
0052     result *= dayWidth/( 24.*60.*60. );
0053 
0054     return result;
0055 }
0056 
0057 QDateTime DateTimeGrid::Private::chartXtoDateTime( qreal x ) const
0058 {
0059     assert( startDateTime.isValid() );
0060     int days = static_cast<int>( x/dayWidth );
0061     qreal secs = x*( 24.*60.*60. )/dayWidth;
0062     QDateTime dt = startDateTime;
0063     QDateTime result = dt.addDays( days )
0064                        .addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) )
0065                        .addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
0066     return result;
0067 }
0068 
0069 #define d d_func()
0070 
0071 
0072 
0073 
0074 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format,
0075                                                 const QString& templ, Qt::Alignment alignment )
0076     : _d( new Private( range, format, templ, alignment ) )
0077 {
0078 }
0079 
0080 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format, Qt::Alignment alignment )
0081     : _d( new Private( range, format, QString::fromLatin1( "%1" ), alignment ) )
0082 {
0083 }
0084 
0085 DateTimeScaleFormatter::DateTimeScaleFormatter( const DateTimeScaleFormatter& other )
0086     : _d( new Private( other.range(), other.format(), other.d->templ, other.alignment() ) )
0087 {
0088 }
0089 
0090 DateTimeScaleFormatter::~DateTimeScaleFormatter()
0091 {
0092     delete _d;
0093 }
0094 
0095 DateTimeScaleFormatter& DateTimeScaleFormatter::operator=( const DateTimeScaleFormatter& other )
0096 {
0097     if ( this == &other )
0098         return *this;
0099 
0100     delete _d;
0101     _d = new Private( other.range(), other.format(), other.d->templ, other.alignment() );
0102     return *this;
0103 }
0104 
0105 
0106 QString DateTimeScaleFormatter::format() const
0107 {
0108     return d->format;
0109 }
0110 
0111 
0112 QString DateTimeScaleFormatter::format( const QDateTime& datetime ) const
0113 {
0114     QString result = d->format;
0115     // additional feature: Weeknumber
0116     const QString shortWeekNumber = QString::number( datetime.date().weekNumber()) + QLatin1String("/")
0117                                                                                      + QString::number( datetime.date().year());
0118     const QString longWeekNumber = ( shortWeekNumber.length() == 1 ? QString::fromLatin1( "0" ) : QString() ) + shortWeekNumber;
0119     result.replace( QString::fromLatin1( "ww" ), longWeekNumber );
0120     result.replace( QString::fromLatin1( "w" ), shortWeekNumber );
0121     result = QLocale().toString(datetime.toLocalTime(), result);
0122     return result;
0123 }
0124 
0125 QString DateTimeScaleFormatter::text( const QDateTime& datetime ) const
0126 {
0127     return d->templ.arg( format( datetime ) );
0128 }
0129 
0130 
0131 DateTimeScaleFormatter::Range DateTimeScaleFormatter::range() const
0132 {
0133     return d->range;
0134 }
0135 
0136 Qt::Alignment DateTimeScaleFormatter::alignment() const
0137 {
0138     return d->alignment;
0139 }
0140 
0141 
0142 QDateTime DateTimeScaleFormatter::nextRangeBegin( const QDateTime& datetime ) const
0143 {
0144     QDateTime result = datetime;
0145     switch ( d->range )
0146     {
0147     case Second:
0148         result = result.addSecs( 60 );
0149         break;
0150     case Minute:
0151         // set it to the begin of the next minute
0152         result.setTime( QTime( result.time().hour(), result.time().minute() ) );
0153         result = result.addSecs( 60 );
0154         break;
0155     case Hour:
0156         // set it to the begin of the next hour
0157         result.setTime( QTime( result.time().hour(), 0 ) );
0158         result = result.addSecs( 60 * 60 );
0159         break;
0160     case Day:
0161         // set it to midnight the next day
0162         result.setTime( QTime( 0, 0 ) );
0163         result = result.addDays( 1 );
0164         break;
0165     case Week:
0166         // set it to midnight
0167         result.setTime( QTime( 0, 0 ) );
0168         // iterate day-wise, until weekNumber changes
0169         {
0170             const int weekNumber = result.date().weekNumber();
0171             while ( weekNumber == result.date().weekNumber() )
0172                 result = result.addDays( 1 );
0173         }
0174         break;
0175     case Month:
0176         // set it to midnight
0177         result.setTime( QTime( 0, 0 ) );
0178         // set it to the first of the next month
0179         result.setDate( QDate( result.date().year(), result.date().month(), 1 ).addMonths( 1 ) );
0180         break;
0181     case Year:
0182         // set it to midnight
0183         result.setTime( QTime( 0, 0 ) );
0184         // set it to the first of the next year
0185         result.setDate( QDate( result.date().year(), 1, 1 ).addYears( 1 ) );
0186         break;
0187     }
0188     //result = result.toLocalTime();
0189     assert( result != datetime );
0190     //qDebug() << "DateTimeScaleFormatter::nextRangeBegin("<<datetime<<")="<<d->range<<result;
0191     return result;
0192 }
0193 
0194 
0195 QDateTime DateTimeScaleFormatter::currentRangeBegin( const QDateTime& datetime ) const
0196 {
0197     QDateTime result = datetime;
0198     switch ( d->range )
0199     {
0200     case Second:
0201         break; // nothing
0202     case Minute:
0203         // set it to the begin of the current minute
0204         result.setTime( QTime( result.time().hour(), result.time().minute() ) );
0205         break;
0206     case Hour:
0207         // set it to the begin of the current hour
0208         result.setTime( QTime( result.time().hour(), 0 ) );
0209         break;
0210     case Day:
0211         // set it to midnight the current day
0212         result.setTime( QTime( 0, 0 ) );
0213         break;
0214     case Week:
0215         // set it to midnight
0216         result.setTime( QTime( 0, 0 ) );
0217         // iterate day-wise, as long weekNumber is the same
0218         {
0219             const int weekNumber = result.date().weekNumber();
0220             while ( weekNumber == result.date().addDays( -1 ).weekNumber() )
0221                 result = result.addDays( -1 );
0222         }
0223         break;
0224     case Month:
0225         // set it to midnight
0226         result.setTime( QTime( 0, 0 ) );
0227         // set it to the first of the current month
0228         result.setDate( QDate( result.date().year(), result.date().month(), 1 ) );
0229         break;
0230     case Year:
0231         // set it to midnight
0232         result.setTime( QTime( 0, 0 ) );
0233         // set it to the first of the current year
0234         result.setDate( QDate( result.date().year(), 1, 1 ) );
0235         break;
0236     }
0237     return result;
0238 }
0239 
0240 DateTimeGrid::DateTimeGrid() : AbstractGrid( new Private )
0241 {
0242 }
0243 
0244 DateTimeGrid::~DateTimeGrid()
0245 {
0246 }
0247 
0248 
0249 QDateTime DateTimeGrid::startDateTime() const
0250 {
0251     return d->startDateTime;
0252 }
0253 
0254 
0255 void DateTimeGrid::setStartDateTime( const QDateTime& dt )
0256 {
0257     d->startDateTime = dt;
0258     Q_EMIT gridChanged();
0259 }
0260 
0261 
0262 qreal DateTimeGrid::dayWidth() const
0263 {
0264     return d->dayWidth;
0265 }
0266 
0267 
0268 qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
0269 {
0270     return d->dateTimeToChartX( dt );
0271 }
0272 
0273 
0274 QDateTime DateTimeGrid::mapToDateTime( qreal x ) const
0275 {
0276     return d->chartXtoDateTime( x );
0277 }
0278 
0279 
0280 void DateTimeGrid::setDayWidth( qreal w )
0281 {
0282     assert( w>0 );
0283     d->dayWidth = w;
0284     Q_EMIT gridChanged();
0285 }
0286 
0287 
0288 void DateTimeGrid::setScale( Scale s )
0289 {
0290     d->scale = s;
0291     Q_EMIT gridChanged();
0292 }
0293 
0294 
0295 DateTimeGrid::Scale DateTimeGrid::scale() const
0296 {
0297     return d->scale;
0298 }
0299 
0300 
0301 void DateTimeGrid::setUserDefinedLowerScale( DateTimeScaleFormatter* lower )
0302 {
0303     delete d->lower;
0304     d->lower = lower;
0305     Q_EMIT gridChanged();
0306 }
0307 
0308 
0309 void DateTimeGrid::setUserDefinedUpperScale( DateTimeScaleFormatter* upper )
0310 {
0311     delete d->upper;
0312     d->upper = upper;
0313     Q_EMIT gridChanged();
0314 }
0315 
0316 
0317 DateTimeScaleFormatter* DateTimeGrid::userDefinedLowerScale() const
0318 {
0319     return d->lower;
0320 }
0321 
0322 
0323 DateTimeScaleFormatter* DateTimeGrid::userDefinedUpperScale() const
0324 {
0325     return d->upper;
0326 }
0327 
0328 
0329 void DateTimeGrid::setWeekStart( Qt::DayOfWeek ws )
0330 {
0331     d->weekStart = ws;
0332     Q_EMIT gridChanged();
0333 }
0334 
0335 
0336 Qt::DayOfWeek DateTimeGrid::weekStart() const
0337 {
0338     return d->weekStart;
0339 }
0340 
0341 
0342 void DateTimeGrid::setFreeDays( const QSet<Qt::DayOfWeek>& fd )
0343 {
0344     d->freeDays = fd;
0345     Q_EMIT gridChanged();
0346 }
0347 
0348 
0349 QSet<Qt::DayOfWeek> DateTimeGrid::freeDays() const
0350 {
0351     return d->freeDays;
0352 }
0353 
0354 
0355 void DateTimeGrid::setFreeDaysBrush(const QBrush brush)
0356 {
0357     d->freeDaysBrush = brush;
0358 }
0359 
0360 
0361 QBrush DateTimeGrid::freeDaysBrush() const
0362 {
0363     return d->freeDaysBrush;
0364 }
0365 
0366 
0367 bool DateTimeGrid::rowSeparators() const
0368 {
0369     return d->rowSeparators;
0370 }
0371 
0372 void DateTimeGrid::setRowSeparators( bool enable )
0373 {
0374     d->rowSeparators = enable;
0375 }
0376 
0377 
0378 void DateTimeGrid::setNoInformationBrush( const QBrush& brush )
0379 {
0380     d->noInformationBrush = brush;
0381     Q_EMIT gridChanged();
0382 }
0383 
0384 
0385 QBrush DateTimeGrid::noInformationBrush() const
0386 {
0387     return d->noInformationBrush;
0388 }
0389 
0390 
0391 qreal DateTimeGrid::mapToChart( const QVariant& value ) const
0392 {
0393     if ( ! value.canConvert( QVariant::DateTime ) ||
0394          ( value.type() == QVariant::String && value.toString().isEmpty() ) )
0395     {
0396         return -1.0;
0397     }
0398     return d->dateTimeToChartX( value.toDateTime() );
0399 }
0400 
0401 
0402 QVariant DateTimeGrid::mapFromChart( qreal x ) const
0403 {
0404     return d->chartXtoDateTime( x );
0405 }
0406 
0407 
0408 Span DateTimeGrid::mapToChart( const QModelIndex& idx ) const
0409 {
0410     assert( model() );
0411     if ( !idx.isValid() ) return Span();
0412     assert( idx.model()==model() );
0413     const QVariant sv = model()->data( idx, StartTimeRole );
0414     const QVariant ev = model()->data( idx, EndTimeRole );
0415     if ( sv.canConvert( QVariant::DateTime ) &&
0416         ev.canConvert( QVariant::DateTime ) &&
0417     !(sv.type() == QVariant::String && sv.toString().isEmpty()) &&
0418     !(ev.type() == QVariant::String && ev.toString().isEmpty())
0419     ) {
0420       QDateTime st = sv.toDateTime();
0421       QDateTime et = ev.toDateTime();
0422       if ( et.isValid() && st.isValid() ) {
0423         qreal sx = d->dateTimeToChartX( st );
0424         qreal ex = d->dateTimeToChartX( et )-sx;
0425         //qDebug() << "DateTimeGrid::mapToChart("<<st<<et<<") => "<< Span( sx, ex );
0426         return Span( sx, ex);
0427       }
0428     }
0429     // Special case for Events with only a start date
0430     if ( sv.canConvert( QVariant::DateTime ) && !(sv.type() == QVariant::String && sv.toString().isEmpty()) ) {
0431       QDateTime st = sv.toDateTime();
0432       if ( st.isValid() ) {
0433         qreal sx = d->dateTimeToChartX( st );
0434         return Span( sx, 0 );
0435       }
0436     }
0437     return Span();
0438 }
0439 
0440 #if 0
0441 static void debug_print_idx( const QModelIndex& idx )
0442 {
0443     if ( !idx.isValid() ) {
0444         qDebug() << "[Invalid]";
0445         return;
0446     }
0447     QDateTime st = idx.data( StartTimeRole ).toDateTime();
0448     QDateTime et = idx.data( EndTimeRole ).toDateTime();
0449     qDebug() << idx << "["<<st<<et<<"]";
0450 }
0451 #endif
0452 
0453 
0454 bool DateTimeGrid::mapFromChart( const Span& span, const QModelIndex& idx,
0455     const QList<Constraint>& constraints ) const
0456 {
0457     assert( model() );
0458     if ( !idx.isValid() ) return false;
0459     assert( idx.model()==model() );
0460 
0461     QDateTime st = d->chartXtoDateTime(span.start());
0462     QDateTime et = d->chartXtoDateTime(span.start()+span.length());
0463     //qDebug() << "DateTimeGrid::mapFromChart("<<span<<") => "<< st << et;
0464     for ( const Constraint& c : constraints ) {
0465         if ( c.type() != Constraint::TypeHard || !isSatisfiedConstraint( c )) continue;
0466         if ( c.startIndex() == idx ) {
0467             QDateTime tmpst = model()->data( c.endIndex(), StartTimeRole ).toDateTime();
0468             //qDebug() << tmpst << "<" << et <<"?";
0469             if ( tmpst<et ) return false;
0470         } else if ( c.endIndex() == idx ) {
0471             QDateTime tmpet = model()->data( c.startIndex(), EndTimeRole ).toDateTime();
0472             //qDebug() << tmpet << ">" << st <<"?";
0473             if ( tmpet>st ) return false;
0474         }
0475     }
0476 
0477     return model()->setData( idx, QVariant::fromValue(st), StartTimeRole )
0478         && model()->setData( idx, QVariant::fromValue(et), EndTimeRole );
0479 }
0480 
0481 Qt::PenStyle DateTimeGrid::Private::gridLinePenStyle( QDateTime dt, Private::HeaderType headerType ) const
0482 {
0483     switch ( headerType ) {
0484         case Private::HeaderHour:
0485             // Midnight
0486             if ( dt.time().hour() == 0 )
0487                 return Qt::SolidLine;
0488             return Qt::DashLine;
0489         case Private::HeaderDay:
0490             // First day of the week
0491             if ( dt.date().dayOfWeek() == weekStart )
0492                 return Qt::SolidLine;
0493             return Qt::DashLine;
0494         case Private::HeaderWeek:
0495             // First day of the month
0496             if ( dt.date().day() == 1 )
0497                 return Qt::SolidLine;
0498             // First day of the week
0499             if ( dt.date().dayOfWeek() == weekStart )
0500                 return Qt::DashLine;
0501             return Qt::NoPen;
0502         case Private::HeaderMonth:
0503             // First day of the year
0504             if ( dt.date().dayOfYear() == 1 )
0505                 return Qt::SolidLine;
0506             // First day of the month
0507             if ( dt.date().day() == 1 )
0508                 return Qt::DashLine;
0509             return Qt::NoPen;
0510         default:
0511             // Nothing to do here
0512             break;
0513    }
0514 
0515     // Default
0516     return Qt::NoPen;
0517 }
0518 
0519 QDateTime DateTimeGrid::Private::adjustDateTimeForHeader( QDateTime dt, Private::HeaderType headerType ) const
0520 {
0521     // In any case, set time to 00:00:00:00
0522     dt.setTime( QTime( 0, 0, 0, 0 ) );
0523 
0524     switch ( headerType ) {
0525         case Private::HeaderWeek:
0526             // Set day to beginning of the week
0527             while ( dt.date().dayOfWeek() != weekStart )
0528                 dt = dt.addDays( -1 );
0529             break;
0530         case Private::HeaderMonth:
0531             // Set day to beginning of the month
0532             dt = dt.addDays( 1 - dt.date().day() );
0533             break;
0534         case Private::HeaderYear:
0535             // Set day to first day of the year
0536             dt = dt.addDays( 1 - dt.date().dayOfYear() );
0537             break;
0538         default:
0539             // In any other case, we don't need to adjust the date time
0540             break;
0541     }
0542 
0543     return dt;
0544 }
0545 
0546 void DateTimeGrid::Private::paintVerticalLines( QPainter* painter,
0547                                                 const QRectF& sceneRect,
0548                                                 const QRectF& exposedRect,
0549                                                 QWidget* widget,
0550                                                 Private::HeaderType headerType )
0551 {
0552         QDateTime dt = chartXtoDateTime( exposedRect.left() );
0553         dt = adjustDateTimeForHeader( dt, headerType );
0554 
0555         int offsetSeconds = 0;
0556         int offsetDays = 0;
0557         // Determine the time step per grid line
0558         if ( headerType == Private::HeaderHour )
0559             offsetSeconds = 60*60;
0560         else
0561             offsetDays = 1;
0562 
0563         for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
0564               dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), x = dateTimeToChartX( dt ) ) {
0565                   //TODO not the best solution as it might be one paint too much, but i don't know what
0566                   //causes the test to fail yet, i think it might be a rounding error
0567             //if ( x >= exposedRect.left() ) {
0568                 QPen pen = painter->pen();
0569                 pen.setBrush( QApplication::palette().dark() );
0570                 pen.setStyle( gridLinePenStyle( dt, headerType ) );
0571                 painter->setPen( pen );
0572                 if ( freeDays.contains( static_cast<Qt::DayOfWeek>( dt.date().dayOfWeek() ) ) ) {
0573                     if (freeDaysBrush.style() == Qt::NoBrush)
0574                         painter->setBrush( widget?widget->palette().midlight()
0575                                            :QApplication::palette().midlight() );
0576                     else
0577                         painter->setBrush(freeDaysBrush);
0578 
0579                     painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() );
0580                 }
0581                 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
0582             //}
0583         }
0584 }
0585 
0586 void DateTimeGrid::Private::paintVerticalUserDefinedLines( QPainter* painter,
0587                                                            const QRectF& sceneRect,
0588                                                            const QRectF& exposedRect,
0589                                                            QWidget* widget )
0590 {
0591     Q_UNUSED( widget );
0592     DateTimeScaleFormatter *lowerFormatter, *upperFormatter;
0593     switch ( scale ) {
0594         case ScaleUserDefined:
0595             lowerFormatter = lower;
0596             upperFormatter = upper;
0597             break;
0598         default:
0599             getAutomaticFormatters( &lowerFormatter, &upperFormatter );
0600             break;
0601     }
0602     QPen pen = painter->pen();
0603     pen.setBrush( QApplication::palette().dark() );
0604 
0605     // Do freeDays, we need to iterate over all dates
0606     QDateTime dtLeft = chartXtoDateTime( exposedRect.left() );
0607     if ( ! freeDays.isEmpty() ) {
0608         QDate lastDate = chartXtoDateTime( exposedRect.right() ).date();
0609         for (QDateTime date(dtLeft.date(), QTime()); date.date() <= lastDate; date = date.addDays(1)) {
0610             if ( freeDays.contains( static_cast<Qt::DayOfWeek>( date.date().dayOfWeek() ) ) ) {
0611                 if (freeDaysBrush.style() == Qt::NoBrush) {
0612                     painter->setBrush( widget?widget->palette().midlight():QApplication::palette().midlight() );
0613                 } else {
0614                     painter->setBrush(freeDaysBrush);
0615                 }
0616                 qreal x = dateTimeToChartX( date );
0617                 painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() );
0618             }
0619         }
0620     }
0621     QDateTime dt = upperFormatter->currentRangeBegin( dtLeft );
0622     // Get all upper scale gridline x values to avoid mixing with lower scale gridlines
0623     QList<qreal> upperXList;
0624     for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right(); dt = upperFormatter->nextRangeBegin( dt ), x=dateTimeToChartX( dt ) ) {
0625         upperXList.append(x);
0626     }
0627     dt = lowerFormatter->currentRangeBegin( dtLeft );
0628     for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
0629         dt = lowerFormatter->nextRangeBegin( dt ),x=dateTimeToChartX( dt ) )
0630     {
0631         if (!upperXList.contains(x)) {
0632             pen.setStyle( Qt::DashLine );
0633             painter->setPen( pen );
0634             painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
0635         }
0636     }
0637     dt = upperFormatter->currentRangeBegin( dtLeft );
0638     for ( qreal x : upperXList )
0639     {
0640         pen.setStyle( Qt::SolidLine );
0641         painter->setPen( pen );
0642         painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
0643     }
0644 }
0645 
0646 DateTimeGrid::Private::HeaderType DateTimeGrid::Private::headerTypeForScale( DateTimeGrid::Scale scale )
0647 {
0648     switch ( scale ) {
0649         case ScaleHour:
0650             return Private::HeaderHour;
0651         case ScaleDay:
0652             return Private::HeaderDay;
0653         case ScaleWeek:
0654             return Private::HeaderWeek;
0655         case ScaleMonth:
0656             return Private::HeaderMonth;
0657         default:
0658             // There are no specific header types for any other scale!
0659             assert( false );
0660             break;
0661     }
0662         return Private::HeaderDay;
0663 }
0664 
0665 void DateTimeGrid::paintGrid( QPainter* painter,
0666                               const QRectF& sceneRect,
0667                               const QRectF& exposedRect,
0668                               AbstractRowController* rowController,
0669                               QWidget* widget )
0670 {
0671     switch ( scale() ) {
0672     case ScaleHour:
0673     case ScaleDay:
0674     case ScaleWeek:
0675     case ScaleMonth:
0676         d->paintVerticalLines( painter, sceneRect, exposedRect, widget, d->headerTypeForScale( scale() ) );
0677         break;
0678     case ScaleAuto:
0679     case ScaleUserDefined:
0680         d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, widget );
0681         break;
0682     }
0683     if ( rowController ) {
0684         // First draw the rows
0685         QPen pen = painter->pen();
0686         pen.setBrush( QApplication::palette().dark() );
0687         pen.setStyle( Qt::DashLine );
0688         painter->setPen( pen );
0689         QModelIndex idx = rowController->indexAt( qRound( exposedRect.top() ) );
0690         if ( rowController->indexAbove( idx ).isValid() ) idx = rowController->indexAbove( idx );
0691         qreal y = 0;
0692         while ( y < exposedRect.bottom() && idx.isValid() ) {
0693             const Span s = rowController->rowGeometry( idx );
0694             y = s.start()+s.length();
0695             if ( d->rowSeparators ) {
0696                 painter->drawLine( QPointF( sceneRect.left(), y ),
0697                                    QPointF( sceneRect.right(), y ) );
0698             }
0699             if ( !idx.data( ItemTypeRole ).isValid() && d->noInformationBrush.style() != Qt::NoBrush ) {
0700                 painter->fillRect( QRectF( exposedRect.left(), s.start(), exposedRect.width(), s.length() ), d->noInformationBrush );
0701             }
0702             // Is alternating background better?
0703             //if ( idx.row()%2 ) painter->fillRect( QRectF( exposedRect.x(), s.start(), exposedRect.width(), s.length() ), QApplication::palette().alternateBase() );
0704             idx =  rowController->indexBelow( idx );
0705         }
0706     }
0707 }
0708 
0709 int DateTimeGrid::Private::tabHeight( const QString& txt, QWidget* widget ) const
0710 {
0711     QStyleOptionHeader opt;
0712     if ( widget ) opt.initFrom( widget );
0713     else opt.palette = QApplication::palette();
0714     opt.text = txt;
0715     QStyle* style;
0716     if ( widget ) style = widget->style();
0717     else style = QApplication::style();
0718     QSize s = style->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), widget);
0719     return s.height();
0720 }
0721 
0722 void DateTimeGrid::Private::getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper)
0723 {
0724     const qreal tabw = QApplication::fontMetrics().boundingRect( QLatin1String( "XXXXX" ) ).width();
0725     const qreal dayw = dayWidth;
0726     if ( dayw > 24*60*60*tabw ) {
0727         *lower = &minute_lower;
0728         *upper = &minute_upper;
0729     } else if ( dayw > 24*60*tabw ) {
0730         *lower = &hour_lower;
0731         *upper = &hour_upper;
0732     } else if ( dayw > 24*tabw ) {
0733         *lower = &day_lower;
0734         *upper = &day_upper;
0735     } else if ( dayw > tabw ) {
0736         *lower = &week_lower;
0737         *upper = &week_upper;
0738     } else if ( 4*dayw > tabw ) {
0739         *lower = &month_lower;
0740         *upper = &month_upper;
0741     } else {
0742         *lower = &year_lower;
0743         *upper = &year_upper;
0744     }
0745 }
0746 
0747 void DateTimeGrid::Private::getFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper)
0748 {
0749     switch ( scale ) {
0750         case DateTimeGrid::ScaleHour:
0751             *lower = &hour_lower;
0752             *upper = &hour_upper;
0753             break;
0754         case DateTimeGrid::ScaleDay:
0755             *lower = &day_lower;
0756             *upper = &day_upper;
0757             break;
0758         case DateTimeGrid::ScaleWeek:
0759             *lower = &week_lower;
0760             *upper = &week_upper;
0761             break;
0762         case DateTimeGrid::ScaleMonth:
0763             *lower = &month_lower;
0764             *upper = &month_upper;
0765             break;
0766         case DateTimeGrid::ScaleUserDefined:
0767             *lower = this->lower;
0768             *upper = this->upper;
0769             break;
0770         default: /*ScaleAuto:*/
0771             getAutomaticFormatters( lower, upper );
0772             break;
0773     }
0774 }
0775 
0776 DateTimeGrid::HeaderType DateTimeGrid::sectionHandleAtPos(int x, int y, const QRect &headerRect) const
0777 {
0778     QDateTime dt1 = d->chartXtoDateTime( x );
0779     QDateTime dt2 = d->chartXtoDateTime( x + 5 );
0780 
0781     DateTimeScaleFormatter *lower, *upper;
0782     const_cast<Private*>(d)->getFormatters( &lower, &upper );
0783 
0784     const qreal lowerHeight = d->tabHeight( lower->text( dt1 ) );
0785     const qreal upperHeight = d->tabHeight( upper->text( dt1 ) );
0786     const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
0787 
0788     const QRectF upperHeaderRect( x, headerRect.top(), 5, headerRect.height() * upperRatio );
0789     const QRectF lowerHeaderRect( x, upperHeaderRect.bottom()+1, 5,  headerRect.height()-upperHeaderRect.height()-1 );
0790     if ( upperHeaderRect.contains( QPoint( x , y ) ) ) {
0791         return upper->currentRangeBegin(dt2) == upper->nextRangeBegin(dt1) ? UpperHeader : NoHeader;
0792     }
0793     if (lowerHeaderRect.contains( QPoint( x , y ) ) ) {
0794         return lower->currentRangeBegin(dt2)==lower->nextRangeBegin(dt1) ? LowerHeader : NoHeader;
0795     }
0796     return NoHeader;
0797 }
0798 
0799 void DateTimeGrid::paintHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
0800                                 qreal offset, QWidget* widget )
0801 {
0802     painter->save();
0803     QPainterPath clipPath;
0804     clipPath.addRect( headerRect );
0805     painter->setClipPath( clipPath, Qt::IntersectClip );
0806     switch ( scale() )
0807     {
0808     case ScaleHour:
0809         paintHourScaleHeader( painter, headerRect, exposedRect, offset, widget );
0810         break;
0811     case ScaleDay:
0812         paintDayScaleHeader( painter, headerRect, exposedRect, offset, widget );
0813         break;
0814     case ScaleWeek:
0815         paintWeekScaleHeader( painter, headerRect, exposedRect, offset, widget );
0816         break;
0817     case ScaleMonth:
0818         paintMonthScaleHeader( painter, headerRect, exposedRect, offset, widget );
0819         break;
0820     case ScaleAuto:
0821         {
0822             DateTimeScaleFormatter *lower, *upper;
0823             d->getAutomaticFormatters( &lower, &upper );
0824             const qreal lowerHeight = d->tabHeight( lower->text( startDateTime() ) );
0825             const qreal upperHeight = d->tabHeight( upper->text( startDateTime() ) );
0826             const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
0827 
0828             const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
0829             const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1,  headerRect.height()-upperHeaderRect.height()-1 );
0830 
0831             paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, lower, widget );
0832             paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, upper, widget );
0833             break;
0834         }
0835     case ScaleUserDefined:
0836         {
0837             const qreal lowerHeight = d->tabHeight( d->lower->text( startDateTime() ) );
0838             const qreal upperHeight = d->tabHeight( d->upper->text( startDateTime() ) );
0839             const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
0840 
0841             const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
0842             const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1,  headerRect.height()-upperHeaderRect.height()-1 );
0843 
0844             paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, d->lower, widget );
0845             paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, d->upper, widget );
0846         }
0847         break;
0848     }
0849     painter->restore();
0850 }
0851 
0852 void DateTimeGrid::paintUserDefinedHeader( QPainter* painter,
0853                                            const QRectF& headerRect, const QRectF& exposedRect,
0854                                            qreal offset, const DateTimeScaleFormatter* formatter,
0855                                            QWidget* widget )
0856 {
0857     const QStyle* const style = widget ? widget->style() : QApplication::style();
0858 
0859     QDateTime dt = formatter->currentRangeBegin( d->chartXtoDateTime( offset + exposedRect.left() ));
0860     qreal x = d->dateTimeToChartX( dt );
0861 
0862     while ( x < exposedRect.right() + offset ) {
0863         const QDateTime next = formatter->nextRangeBegin( dt );
0864         const qreal nextx = d->dateTimeToChartX( next );
0865 
0866         QStyleOptionHeader opt;
0867         if ( widget ) opt.initFrom( widget );
0868         else opt.palette = QApplication::palette();
0869         opt.rect = QRectF( x - offset+1, headerRect.top(), qMax<qreal>( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect();
0870         opt.textAlignment = formatter->alignment();
0871         opt.text = formatter->text( dt );
0872         style->drawControl( QStyle::CE_Header, &opt, painter, widget );
0873 
0874         dt = next;
0875         x = nextx;
0876     }
0877 }
0878 
0879 void DateTimeGrid::Private::paintHeader( QPainter* painter,
0880                                          const QRectF& headerRect, const QRectF& exposedRect,
0881                                          qreal offset, QWidget* widget,
0882                                          Private::HeaderType headerType,
0883                                          DateTextFormatter *formatter )
0884 {
0885     QStyle* style = widget?widget->style():QApplication::style();
0886 
0887     const qreal left = exposedRect.left() + offset;
0888     const qreal right = exposedRect.right() + offset;
0889 
0890     // Paint a section for each hour
0891     QDateTime dt = chartXtoDateTime( left );
0892     dt = adjustDateTimeForHeader( dt, headerType );
0893     // Determine the time step per grid line
0894     int offsetSeconds = 0;
0895     int offsetDays = 0;
0896     int offsetMonths = 0;
0897 
0898     switch ( headerType ) {
0899         case Private::HeaderHour:
0900             offsetSeconds = 60*60;
0901             break;
0902         case Private::HeaderDay:
0903             offsetDays = 1;
0904             break;
0905         case Private::HeaderWeek:
0906             offsetDays = 7;
0907             break;
0908         case Private::HeaderMonth:
0909             offsetMonths = 1;
0910             break;
0911         case Private::HeaderYear:
0912             offsetMonths = 12;
0913             break;
0914         default:
0915             // Other scales cannot be painted with this method!
0916             assert( false );
0917             break;
0918     }
0919 
0920     for ( qreal x = dateTimeToChartX( dt ); x < right;
0921           dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), dt = dt.addMonths( offsetMonths ),
0922           x = dateTimeToChartX( dt ) ) {
0923         QStyleOptionHeader opt;
0924         if ( widget ) opt.initFrom( widget );
0925         else opt.palette = QApplication::palette();
0926         opt.rect = formatter->textRect( x, offset, dayWidth, headerRect, dt );
0927         opt.text = formatter->format( dt );
0928         opt.textAlignment = Qt::AlignCenter;
0929         style->drawControl(QStyle::CE_Header, &opt, painter, widget);
0930     }
0931     delete formatter;
0932     formatter = nullptr;
0933 }
0934 
0935 
0936 void DateTimeGrid::paintHourScaleHeader( QPainter* painter,
0937                                          const QRectF& headerRect, const QRectF& exposedRect,
0938                                          qreal offset, QWidget* widget )
0939 {
0940     class HourFormatter : public Private::DateTextFormatter {
0941     public:
0942         ~HourFormatter() override {}
0943 
0944         QString format( const QDateTime& dt ) override {
0945             return QLocale().toString(dt.time(), QString::fromLatin1( "hh" ) );
0946         }
0947         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
0948             Q_UNUSED(dt);
0949 
0950             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
0951                            QSizeF( dayWidth / 24.0, headerRect.height() / 2.0 ) ).toAlignedRect();
0952         }
0953     };
0954     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
0955                     Private::HeaderHour, new HourFormatter ); // Custom parameters
0956 
0957     class DayFormatter : public Private::DateTextFormatter {
0958     public:
0959         ~DayFormatter() override {}
0960         QString format( const QDateTime& dt ) override {
0961             return QLocale().toString(dt.date());
0962         }
0963         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
0964             Q_UNUSED(dt);
0965 
0966             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
0967                            QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toRect();
0968         }
0969     };
0970     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
0971                     Private::HeaderDay, new DayFormatter ); // Custom parameters
0972 }
0973 
0974 
0975 void DateTimeGrid::paintDayScaleHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
0976                                 qreal offset, QWidget* widget )
0977 {
0978     class DayFormatter : public Private::DateTextFormatter {
0979     public:
0980         ~DayFormatter() override {}
0981 
0982         QString format( const QDateTime& dt ) override {
0983             return QLocale().toString(dt, QStringLiteral("ddd")).left(1);
0984         }
0985         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
0986             Q_UNUSED(dt);
0987 
0988             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
0989                            QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toAlignedRect();
0990         }
0991     };
0992     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
0993                     Private::HeaderDay, new DayFormatter ); // Custom parameters
0994 
0995     class WeekFormatter : public Private::DateTextFormatter {
0996     public:
0997         ~WeekFormatter() override {}
0998         QString format( const QDateTime& dt ) override {
0999             return QString::number(dt.date().weekNumber()) + QLatin1String("/") + QString::number(dt.date().year());
1000         }
1001         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
1002             Q_UNUSED(dt);
1003 
1004             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
1005                            QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
1006         }
1007     };
1008     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
1009                     Private::HeaderWeek, new WeekFormatter ); // Custom parameters
1010 }
1011 
1012 
1013 void DateTimeGrid::paintWeekScaleHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
1014                                         qreal offset, QWidget* widget )
1015 {
1016     class WeekFormatter : public Private::DateTextFormatter {
1017     public:
1018         ~WeekFormatter() override {}
1019 
1020         QString format( const QDateTime& dt ) override {
1021             return QString::number( dt.date().weekNumber() );
1022         }
1023         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
1024             Q_UNUSED(dt);
1025 
1026             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
1027                            QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
1028         }
1029     };
1030     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
1031                     Private::HeaderWeek, new WeekFormatter ); // Custom parameters
1032 
1033     class MonthFormatter : public Private::DateTextFormatter {
1034     public:
1035         ~MonthFormatter() override {}
1036 
1037         QString format( const QDateTime& dt ) override {
1038             return QLocale().monthName(dt.date().month(), QLocale::LongFormat) + QLatin1String("/") + QString::number(dt.date().year());
1039         }
1040         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
1041             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
1042                            QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
1043         }
1044     };
1045     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
1046                     Private::HeaderMonth, new MonthFormatter ); // Custom parameters
1047 }
1048 
1049 
1050 void DateTimeGrid::paintMonthScaleHeader( QPainter* painter,  const QRectF& headerRect, const QRectF& exposedRect,
1051                                         qreal offset, QWidget* widget )
1052 {
1053     class MonthFormatter : public Private::DateTextFormatter {
1054     public:
1055         ~MonthFormatter() override {}
1056 
1057         QString format( const QDateTime& dt ) override {
1058             return QLocale().monthName(dt.date().month(), QLocale::ShortFormat) + QLatin1String("/") + QString::number(dt.date().year());
1059         }
1060         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
1061             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
1062                            QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
1063         }
1064     };
1065     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
1066                     Private::HeaderMonth, new MonthFormatter ); // Custom parameters
1067 
1068     class YearFormatter : public Private::DateTextFormatter {
1069     public:
1070         ~YearFormatter() override {}
1071 
1072         QString format( const QDateTime& dt ) override {
1073             return QString::number( dt.date().year() );
1074         }
1075         QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) override {
1076             return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
1077                            QSizeF( dayWidth * dt.date().daysInYear(), headerRect.height() / 2.0 ) ).toRect();
1078         }
1079     };
1080     d->paintHeader( painter, headerRect, exposedRect, offset, widget, // General parameters
1081                     Private::HeaderYear, new YearFormatter ); // Custom parameters
1082 }
1083 
1084 
1085 void DateTimeGrid::drawDayBackground(QPainter* painter, const QRectF& rect, const QDate& date)
1086 {
1087     Q_UNUSED(date);
1088     if (d->timeLine->options() & DateTimeTimeLine::Background) {
1089         d->drawTimeLine(painter, rect);
1090     }
1091 }
1092 
1093 
1094 void DateTimeGrid::drawDayForeground(QPainter* painter, const QRectF& rect, const QDate& date)
1095 {
1096     Q_UNUSED(date);
1097     if (d->timeLine->options() & DateTimeTimeLine::Foreground) {
1098         d->drawTimeLine(painter, rect);
1099     }
1100 }
1101 
1102 
1103 QRectF DateTimeGrid::computeRect(const QDateTime& from, const QDateTime& to, const QRectF& rect) const
1104 {
1105     qreal topLeft = d->dateTimeToChartX(from);
1106     qreal topRight = d->dateTimeToChartX(to);
1107 
1108     return QRectF(topLeft, rect.top(), topRight - topLeft, rect.height());
1109 }
1110 
1111 
1112 QPair<QDateTime, QDateTime> DateTimeGrid::dateTimeRange(const QRectF& rect) const
1113 {
1114     QDateTime start;
1115     QDateTime end;
1116 
1117     start = d->chartXtoDateTime(rect.left());
1118     end = d->chartXtoDateTime(rect.right());
1119 
1120     return qMakePair(start, end);
1121 }
1122 
1123 void DateTimeGrid::drawBackground(QPainter* paint, const QRectF& rect)
1124 {
1125     int offset = (int)dayWidth();
1126 
1127     assert( offset>0 );
1128 
1129     // Figure out the date at the extreme left
1130     QDate date = d->chartXtoDateTime(rect.left()).date();
1131 
1132     // We need to paint from one end to the other
1133     int startx = rect.left();
1134     int endx = rect.right();
1135 
1136     // Save the painter state
1137     paint->save();
1138 
1139     // Paint the first date column
1140     while (1)
1141     {
1142         QDate nextDate = d->chartXtoDateTime(startx+1).date();
1143         if (date != nextDate)
1144         {
1145             QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
1146             dayRect = dayRect.adjusted(1, 0, 0, 0);
1147             drawDayBackground(paint, dayRect, date);
1148             break;
1149         }
1150 
1151         ++startx;
1152     }
1153 
1154     // Paint the remaining dates
1155     for (int i=startx; i<endx; i+=offset)
1156     {
1157         date = d->chartXtoDateTime(i+1).date();
1158 
1159         QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
1160         dayRect = dayRect.adjusted(1, 0, 0, 0);
1161         drawDayBackground(paint, dayRect, date);
1162     }
1163 
1164     // Restore the painter state
1165     paint->restore();
1166 }
1167 
1168 void DateTimeGrid::drawForeground(QPainter* paint, const QRectF& rect)
1169 {
1170     int offset = (int)dayWidth();
1171 
1172     // Figure out the date at the extreme left
1173     QDate date = d->chartXtoDateTime(rect.left()).date();
1174 
1175     // We need to paint from one end to the other
1176     int startx = rect.left();
1177     int endx = rect.right();
1178 
1179     // Save the painter state
1180     paint->save();
1181 
1182     // Paint the first date column
1183     while (1)
1184     {
1185         QDate nextDate = d->chartXtoDateTime(startx+1).date();
1186         if (date != nextDate)
1187         {
1188             QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
1189             dayRect = dayRect.adjusted(1, 0, 0, 0);
1190             drawDayForeground(paint, dayRect, date);
1191             break;
1192         }
1193 
1194         ++startx;
1195     }
1196 
1197     // Paint the remaining dates
1198     for (int i=startx; i<endx; i+=offset)
1199     {
1200         date = d->chartXtoDateTime(i+1).date();
1201 
1202         QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
1203         dayRect = dayRect.adjusted(1, 0, 0, 0);
1204         drawDayForeground(paint, dayRect, date);
1205     }
1206 
1207     // Restore the painter state
1208     paint->restore();
1209 }
1210 
1211 
1212 DateTimeTimeLine *DateTimeGrid::timeLine() const
1213 {
1214     return d->timeLine;
1215 }
1216 
1217 void DateTimeGrid::Private::drawTimeLine(QPainter* painter, const QRectF& rect)
1218 {
1219     qreal x = dateTimeToChartX(timeLine->dateTime());
1220     if (rect.contains(x, rect.top())) {
1221         painter->save();
1222         painter->setPen(timeLine->pen());
1223         painter->drawLine(x, rect.top(), x, rect.bottom());
1224         painter->restore();
1225     }
1226 }
1227 
1228 #undef d
1229 
1230 #ifndef KDAB_NO_UNIT_TESTS
1231 
1232 #include <QStandardItemModel>
1233 #include "unittest/test.h"
1234 
1235 static std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
1236 {
1237 #ifdef QT_NO_STL
1238     os << dt.toString().toLatin1().constData();
1239 #else
1240     os << dt.toString().toStdString();
1241 #endif
1242     return os;
1243 }
1244 
1245 KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, DateTimeGrid, "test" ) {
1246     QStandardItemModel model( 3, 2 );
1247     DateTimeGrid grid;
1248     QDateTime dt = QDateTime::currentDateTime();
1249     grid.setModel( &model );
1250     QDateTime startdt = dt.addDays( -10 );
1251     grid.setStartDateTime( startdt );
1252 
1253     model.setData( model.index( 0, 0 ), dt,               StartTimeRole );
1254     model.setData( model.index( 0, 0 ), dt.addDays( 17 ), EndTimeRole );
1255 
1256     model.setData( model.index( 2, 0 ), dt.addDays( 18 ), StartTimeRole );
1257     model.setData( model.index( 2, 0 ), dt.addDays( 19 ), EndTimeRole );
1258 
1259     Span s = grid.mapToChart( model.index( 0, 0 ) );
1260     //qDebug() << "span="<<s;
1261 
1262     assertTrue( s.start()>0 );
1263     assertTrue( s.length()>0 );
1264 
1265     assertTrue( startdt == grid.mapToDateTime( grid.mapFromDateTime( startdt ) ) );
1266 
1267     grid.mapFromChart( s, model.index( 1, 0 ) );
1268 
1269     QDateTime s1 = model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime();
1270     QDateTime e1 = model.data( model.index( 0, 0 ), EndTimeRole ).toDateTime();
1271     QDateTime s2 = model.data( model.index( 1, 0 ), StartTimeRole ).toDateTime();
1272     QDateTime e2 = model.data( model.index( 1, 0 ), EndTimeRole ).toDateTime();
1273 
1274     assertTrue( s1.isValid() );
1275     assertTrue( e1.isValid() );
1276     assertTrue( s2.isValid() );
1277     assertTrue( e2.isValid() );
1278 
1279     assertEqual( s1, s2 );
1280     assertEqual( e1, e2 );
1281 
1282     assertTrue( grid.isSatisfiedConstraint( Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ) );
1283     assertFalse( grid.isSatisfiedConstraint( Constraint( model.index( 2, 0 ), model.index( 0, 0 ) ) ) );
1284 
1285     s = grid.mapToChart( model.index( 0, 0 ) );
1286     s.setEnd( s.end()+100000. );
1287     bool rc = grid.mapFromChart( s, model.index( 0, 0 ) );
1288     assertTrue( rc );
1289     assertEqual( s1, model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime() );
1290     Span newspan = grid.mapToChart( model.index( 0, 0 ) );
1291     assertEqual( newspan.start(), s.start() );
1292     assertEqual( newspan.length(), s.length() );
1293 
1294     {
1295         QDateTime startDateTime = QDateTime::currentDateTime();
1296         qreal dayWidth = 100;
1297         QDate currentDate = QDate::currentDate();
1298         QDateTime dt( QDate(currentDate.year(), 1, 1),  QTime( 0, 0, 0, 0 ) );
1299         assert( dt.isValid() );
1300         qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
1301         result += startDateTime.time().msecsTo(dt.time())/1000.;
1302         result *= dayWidth/( 24.*60.*60. );
1303 
1304         int days = static_cast<int>( result/dayWidth );
1305         qreal secs = result*( 24.*60.*60. )/dayWidth;
1306         QDateTime dt2 = startDateTime;
1307         QDateTime result2 = dt2.addDays( days ).addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) ).addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
1308 
1309         assertEqual( dt, result2 );
1310     }
1311 }
1312 
1313 #endif /* KDAB_NO_UNIT_TESTS */
1314 
1315 #include "moc_kganttdatetimegrid.cpp"