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"