00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00065
00066
00067 qreal DateTimeGrid::Private::dateTimeToChartX( const QDateTime& dt ) const
00068 {
00069 assert( startDateTime.isValid() );
00070 qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
00071 result += startDateTime.time().msecsTo(dt.time())/1000.;
00072 result *= dayWidth/( 24.*60.*60. );
00073
00074 return result;
00075 }
00076
00077 QDateTime DateTimeGrid::Private::chartXtoDateTime( qreal x ) const
00078 {
00079 assert( startDateTime.isValid() );
00080 int days = static_cast<int>( x/dayWidth );
00081 qreal secs = x*( 24.*60.*60. )/dayWidth;
00082 QDateTime dt = startDateTime;
00083 QDateTime result = dt.addDays( days )
00084 .addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) )
00085 .addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
00086 return result;
00087 }
00088
00089 #define d d_func()
00090
00118 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format,
00119 const QString& templ, Qt::Alignment alignment )
00120 : _d( new Private( range, format, templ, alignment ) )
00121 {
00122 }
00123
00124 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format, Qt::Alignment alignment )
00125 : _d( new Private( range, format, QString::fromLatin1( "%1" ), alignment ) )
00126 {
00127 }
00128
00129 DateTimeScaleFormatter::DateTimeScaleFormatter( const DateTimeScaleFormatter& other )
00130 : _d( new Private( other.range(), other.format(), other.d->templ, other.alignment() ) )
00131 {
00132 }
00133
00134 DateTimeScaleFormatter::~DateTimeScaleFormatter()
00135 {
00136 delete _d;
00137 }
00138
00139 DateTimeScaleFormatter& DateTimeScaleFormatter::operator=( const DateTimeScaleFormatter& other )
00140 {
00141 delete _d;
00142 _d = new Private( other.range(), other.format(), other.d->templ, other.alignment() );
00143 return *this;
00144 }
00145
00148 QString DateTimeScaleFormatter::format() const
00149 {
00150 return d->format;
00151 }
00152
00155 QString DateTimeScaleFormatter::format( const QDateTime& datetime ) const
00156 {
00157 QString result = d->format;
00158
00159 const QString shortWeekNumber = QString::number( datetime.date().weekNumber()) + 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
00198 result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00199 result = result.addSecs( 60 );
00200 break;
00201 case Hour:
00202
00203 result.setTime( QTime( result.time().hour(), 0 ) );
00204 result = result.addSecs( 60 * 60 );
00205 break;
00206 case Day:
00207
00208 result.setTime( QTime( 0, 0 ) );
00209 result = result.addDays( 1 );
00210 break;
00211 case Week:
00212
00213 result.setTime( QTime( 0, 0 ) );
00214
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
00223 result.setTime( QTime( 0, 0 ) );
00224
00225 result.setDate( QDate( result.date().year(), result.date().month(), 1 ).addMonths( 1 ) );
00226 break;
00227 case Year:
00228
00229 result.setTime( QTime( 0, 0 ) );
00230
00231 result.setDate( QDate( result.date().year(), 1, 1 ).addYears( 1 ) );
00232 break;
00233 }
00234
00235 assert( result != datetime );
00236
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;
00250 case Minute:
00251
00252 result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00253 break;
00254 case Hour:
00255
00256 result.setTime( QTime( result.time().hour(), 0 ) );
00257 break;
00258 case Day:
00259
00260 result.setTime( QTime( 0, 0 ) );
00261 break;
00262 case Week:
00263
00264 result.setTime( QTime( 0, 0 ) );
00265
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
00274 result.setTime( QTime( 0, 0 ) );
00275
00276 result.setDate( QDate( result.date().year(), result.date().month(), 1 ) );
00277 break;
00278 case Year:
00279
00280 result.setTime( QTime( 0, 0 ) );
00281
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
00536 return Span( sx, ex);
00537 }
00538 }
00539
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
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
00592 if ( tmpst<et ) return false;
00593 } else if ( c.endIndex() == idx ) {
00594 QDateTime tmpet = model()->data( c.startIndex(), EndTimeRole ).toDateTime();
00595
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
00609 if ( dt.time().hour() == 0 )
00610 return Qt::SolidLine;
00611 return Qt::DashLine;
00612 case Private::HeaderDay:
00613
00614 if ( dt.date().dayOfWeek() == weekStart )
00615 return Qt::SolidLine;
00616 return Qt::DashLine;
00617 case Private::HeaderWeek:
00618
00619 if ( dt.date().day() == 1 )
00620 return Qt::SolidLine;
00621
00622 if ( dt.date().dayOfWeek() == weekStart )
00623 return Qt::DashLine;
00624 return Qt::NoPen;
00625 case Private::HeaderMonth:
00626
00627 if ( dt.date().dayOfYear() == 1 )
00628 return Qt::SolidLine;
00629
00630 if ( dt.date().day() == 1 )
00631 return Qt::DashLine;
00632 return Qt::NoPen;
00633 default:
00634
00635 break;
00636 }
00637
00638
00639 return Qt::NoPen;
00640 }
00641
00642 QDateTime DateTimeGrid::Private::adjustDateTimeForHeader( QDateTime dt, Private::HeaderType headerType ) const
00643 {
00644
00645 dt.setTime( QTime( 0, 0, 0, 0 ) );
00646
00647 switch ( headerType ) {
00648 case Private::HeaderWeek:
00649
00650 while ( dt.date().dayOfWeek() != weekStart )
00651 dt = dt.addDays( -1 );
00652 break;
00653 case Private::HeaderMonth:
00654
00655 dt = dt.addDays( 1 - dt.date().day() );
00656 break;
00657 case Private::HeaderYear:
00658
00659 dt = dt.addDays( 1 - dt.date().dayOfYear() );
00660 break;
00661 default:
00662
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
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
00689
00690
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
00736
00737
00738
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
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
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
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
00820
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
00955 QDateTime dt = chartXtoDateTime( left );
00956 dt = adjustDateTimeForHeader( dt, headerType );
00957
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
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,
01018 Private::HeaderHour, new HourFormatter );
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,
01034 Private::HeaderDay, new DayFormatter );
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,
01058 Private::HeaderDay, new DayFormatter );
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,
01074 Private::HeaderWeek, new WeekFormatter );
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,
01098 Private::HeaderWeek, new WeekFormatter );
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,
01113 Private::HeaderMonth, new MonthFormatter );
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,
01135 Private::HeaderMonth, new MonthFormatter );
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,
01150 Private::HeaderYear, new YearFormatter );
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
01203 QDate date = d->chartXtoDateTime(rect.left()).date();
01204
01205
01206 int startx = rect.left();
01207 int endx = rect.right();
01208
01209
01210 paint->save();
01211
01212
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
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
01238 paint->restore();
01239 }
01240
01241 void DateTimeGrid::drawForeground(QPainter* paint, const QRectF& rect)
01242 {
01243 int offset = (int)dayWidth();
01244
01245
01246 QDate date = d->chartXtoDateTime(rect.left()).date();
01247
01248
01249 int startx = rect.left();
01250 int endx = rect.right();
01251
01252
01253 paint->save();
01254
01255
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
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
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
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
01372
01373 #include "moc_kdganttdatetimegrid.cpp"