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