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 if ( x >= exposedRect.left() ) {
00689 QPen pen = painter->pen();
00690 pen.setBrush( QApplication::palette().dark() );
00691 pen.setStyle( gridLinePenStyle( dt, headerType ) );
00692 painter->setPen( pen );
00693 if ( freeDays.contains( static_cast<Qt::DayOfWeek>( dt.date().dayOfWeek() ) ) ) {
00694 if(freeDaysBrush.style() == Qt::NoBrush)
00695 painter->setBrush( widget?widget->palette().midlight()
00696 :QApplication::palette().midlight() );
00697 else
00698 painter->setBrush(freeDaysBrush);
00699
00700 painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() );
00701 }
00702 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00703 }
00704 }
00705 }
00706
00707 void DateTimeGrid::Private::paintVerticalUserDefinedLines( QPainter* painter,
00708 const QRectF& sceneRect,
00709 const QRectF& exposedRect,
00710 const DateTimeScaleFormatter* formatter,
00711 QWidget* widget )
00712 {
00713 Q_UNUSED( widget );
00714 QDateTime dt = chartXtoDateTime( exposedRect.left() );
00715 dt = formatter->currentRangeBegin( dt );
00716 QPen pen = painter->pen();
00717 pen.setBrush( QApplication::palette().dark() );
00718 pen.setStyle( Qt::DashLine );
00719 painter->setPen( pen );
00720 for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
00721 dt = formatter->nextRangeBegin( dt ),x=dateTimeToChartX( dt ) ) {
00722 if ( x >= exposedRect.left() ) {
00723
00724 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00725 }
00726 }
00727 }
00728
00729 DateTimeGrid::Private::HeaderType DateTimeGrid::Private::headerTypeForScale( DateTimeGrid::Scale scale )
00730 {
00731 switch ( scale ) {
00732 case ScaleHour:
00733 return Private::HeaderHour;
00734 case ScaleDay:
00735 return Private::HeaderDay;
00736 case ScaleWeek:
00737 return Private::HeaderWeek;
00738 case ScaleMonth:
00739 return Private::HeaderMonth;
00740 default:
00741
00742 assert( false );
00743 break;
00744 }
00745 }
00746
00747 void DateTimeGrid::paintGrid( QPainter* painter,
00748 const QRectF& sceneRect,
00749 const QRectF& exposedRect,
00750 AbstractRowController* rowController,
00751 QWidget* widget )
00752 {
00753
00754 switch( scale() ) {
00755 case ScaleHour:
00756 case ScaleDay:
00757 case ScaleWeek:
00758 case ScaleMonth:
00759 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, d->headerTypeForScale( scale() ) );
00760 break;
00761 case ScaleAuto: {
00762 const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00763 const qreal dayw = dayWidth();
00764 if ( dayw > 24*60*60*tabw ) {
00765
00766 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->minute_lower, widget );
00767 } else if ( dayw > 24*60*tabw ) {
00768 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderHour );
00769 } else if ( dayw > 24*tabw ) {
00770 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderDay );
00771 } else if ( dayw > tabw ) {
00772 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->week_lower, widget );
00773 } else if ( 4*dayw > tabw ) {
00774 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->month_lower, widget );
00775 } else {
00776 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->year_lower, widget );
00777 }
00778 break;
00779 }
00780 case ScaleUserDefined:
00781 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, d->lower, widget );
00782 break;
00783 }
00784 if ( rowController ) {
00785
00786 QPen pen = painter->pen();
00787 pen.setBrush( QApplication::palette().dark() );
00788 pen.setStyle( Qt::DashLine );
00789 painter->setPen( pen );
00790 QModelIndex idx = rowController->indexAt( qRound( exposedRect.top() ) );
00791 if ( rowController->indexAbove( idx ).isValid() ) idx = rowController->indexAbove( idx );
00792 qreal y = 0;
00793 while ( y < exposedRect.bottom() && idx.isValid() ) {
00794 const Span s = rowController->rowGeometry( idx );
00795 y = s.start()+s.length();
00796 if ( d->rowSeparators ) {
00797 painter->drawLine( QPointF( sceneRect.left(), y ),
00798 QPointF( sceneRect.right(), y ) );
00799 }
00800 if ( !idx.data( ItemTypeRole ).isValid() && d->noInformationBrush.style() != Qt::NoBrush ) {
00801 painter->fillRect( QRectF( exposedRect.left(), s.start(), exposedRect.width(), s.length() ), d->noInformationBrush );
00802 }
00803
00804
00805 idx = rowController->indexBelow( idx );
00806 }
00807 }
00808 }
00809
00810 int DateTimeGrid::Private::tabHeight( const QString& txt, QWidget* widget ) const
00811 {
00812 QStyleOptionHeader opt;
00813 if ( widget ) opt.initFrom( widget );
00814 opt.text = txt;
00815 QStyle* style;
00816 if ( widget ) style = widget->style();
00817 else style = QApplication::style();
00818 QSize s = style->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), widget);
00819 return s.height();
00820 }
00821
00822 void DateTimeGrid::Private::getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper)
00823 {
00824 const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00825 const qreal dayw = dayWidth;
00826 if ( dayw > 24*60*60*tabw ) {
00827 *lower = &minute_lower;
00828 *upper = &minute_upper;
00829 } else if ( dayw > 24*60*tabw ) {
00830 *lower = &hour_lower;
00831 *upper = &hour_upper;
00832 } else if ( dayw > 24*tabw ) {
00833 *lower = &day_lower;
00834 *upper = &day_upper;
00835 } else if ( dayw > tabw ) {
00836 *lower = &week_lower;
00837 *upper = &week_upper;
00838 } else if ( 4*dayw > tabw ) {
00839 *lower = &month_lower;
00840 *upper = &month_upper;
00841 } else {
00842 *lower = &year_lower;
00843 *upper = &year_upper;
00844 }
00845 }
00846
00847
00848 void DateTimeGrid::paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
00849 qreal offset, QWidget* widget )
00850 {
00851 painter->save();
00852 QPainterPath clipPath;
00853 clipPath.addRect( headerRect );
00854 painter->setClipPath( clipPath, Qt::IntersectClip );
00855 switch( scale() )
00856 {
00857 case ScaleHour:
00858 paintHourScaleHeader( painter, headerRect, exposedRect, offset, widget );
00859 break;
00860 case ScaleDay:
00861 paintDayScaleHeader( painter, headerRect, exposedRect, offset, widget );
00862 break;
00863 case ScaleWeek:
00864 paintWeekScaleHeader( painter, headerRect, exposedRect, offset, widget );
00865 break;
00866 case ScaleMonth:
00867 paintMonthScaleHeader( painter, headerRect, exposedRect, offset, widget );
00868 break;
00869 case ScaleAuto:
00870 {
00871 DateTimeScaleFormatter *lower, *upper;
00872 d->getAutomaticFormatters( &lower, &upper );
00873 const qreal lowerHeight = d->tabHeight( lower->text( startDateTime() ) );
00874 const qreal upperHeight = d->tabHeight( upper->text( startDateTime() ) );
00875 const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00876
00877 const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00878 const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 );
00879
00880 paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, lower, widget );
00881 paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, upper, widget );
00882 break;
00883 }
00884 case ScaleUserDefined:
00885 {
00886 const qreal lowerHeight = d->tabHeight( d->lower->text( startDateTime() ) );
00887 const qreal upperHeight = d->tabHeight( d->upper->text( startDateTime() ) );
00888 const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00889
00890 const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00891 const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 );
00892
00893 paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, d->lower, widget );
00894 paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, d->upper, widget );
00895 }
00896 break;
00897 }
00898 painter->restore();
00899 }
00900
00901 void DateTimeGrid::paintUserDefinedHeader( QPainter* painter,
00902 const QRectF& headerRect, const QRectF& exposedRect,
00903 qreal offset, const DateTimeScaleFormatter* formatter,
00904 QWidget* widget )
00905 {
00906 const QStyle* const style = widget ? widget->style() : QApplication::style();
00907
00908 QDateTime dt = formatter->currentRangeBegin( d->chartXtoDateTime( offset + exposedRect.left() ));
00909 qreal x = d->dateTimeToChartX( dt );
00910
00911 while( x < exposedRect.right() + offset ) {
00912 const QDateTime next = formatter->nextRangeBegin( dt );
00913 const qreal nextx = d->dateTimeToChartX( next );
00914
00915 QStyleOptionHeader opt;
00916 if ( widget ) opt.init( widget );
00917 opt.rect = QRectF( x - offset+1, headerRect.top(), qMax<qreal>( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect();
00918 opt.textAlignment = formatter->alignment();
00919 opt.text = formatter->text( dt );
00920 style->drawControl( QStyle::CE_Header, &opt, painter, widget );
00921
00922 dt = next;
00923 x = nextx;
00924 }
00925 }
00926
00927 void DateTimeGrid::Private::paintHeader( QPainter* painter,
00928 const QRectF& headerRect, const QRectF& exposedRect,
00929 qreal offset, QWidget* widget,
00930 Private::HeaderType headerType,
00931 DateTextFormatter *formatter )
00932 {
00933 QStyle* style = widget?widget->style():QApplication::style();
00934
00935 const qreal left = exposedRect.left() + offset;
00936 const qreal right = exposedRect.right() + offset;
00937
00938
00939 QDateTime dt = chartXtoDateTime( left );
00940 dt = adjustDateTimeForHeader( dt, headerType );
00941
00942 int offsetSeconds = 0;
00943 int offsetDays = 0;
00944 int offsetMonths = 0;
00945
00946 switch ( headerType ) {
00947 case Private::HeaderHour:
00948 offsetSeconds = 60*60;
00949 break;
00950 case Private::HeaderDay:
00951 offsetDays = 1;
00952 break;
00953 case Private::HeaderWeek:
00954 offsetDays = 7;
00955 break;
00956 case Private::HeaderMonth:
00957 offsetMonths = 1;
00958 break;
00959 case Private::HeaderYear:
00960 offsetMonths = 12;
00961 break;
00962 default:
00963
00964 assert( false );
00965 break;
00966 }
00967
00968 for ( qreal x = dateTimeToChartX( dt ); x < right;
00969 dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), dt = dt.addMonths( offsetMonths ),
00970 x = dateTimeToChartX( dt ) ) {
00971 QStyleOptionHeader opt;
00972 if ( widget ) opt.init( widget );
00973 opt.rect = formatter->textRect( x, offset, dayWidth, headerRect, dt );
00974 opt.text = formatter->format( dt );
00975 opt.textAlignment = Qt::AlignCenter;
00976 style->drawControl(QStyle::CE_Header, &opt, painter, widget);
00977 }
00978 }
00979
00983 void DateTimeGrid::paintHourScaleHeader( QPainter* painter,
00984 const QRectF& headerRect, const QRectF& exposedRect,
00985 qreal offset, QWidget* widget )
00986 {
00987 class HourFormatter : public Private::DateTextFormatter {
00988 public:
00989 virtual ~HourFormatter() {}
00990
00991 QString format( const QDateTime& dt ) {
00992 return dt.time().toString( QString::fromAscii( "hh" ) );
00993 }
00994 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
00995 Q_UNUSED(dt);
00996
00997 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
00998 QSizeF( dayWidth / 24.0, headerRect.height() / 2.0 ) ).toAlignedRect();
00999 }
01000 };
01001 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01002 Private::HeaderHour, new HourFormatter );
01003
01004 class DayFormatter : public Private::DateTextFormatter {
01005 public:
01006 virtual ~DayFormatter() {}
01007 QString format( const QDateTime& dt ) {
01008 return dt.date().toString();
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, 0.0 ),
01014 QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toRect();
01015 }
01016 };
01017 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01018 Private::HeaderDay, new DayFormatter );
01019 }
01020
01024 void DateTimeGrid::paintDayScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01025 qreal offset, QWidget* widget )
01026 {
01027 class DayFormatter : public Private::DateTextFormatter {
01028 public:
01029 virtual ~DayFormatter() {}
01030
01031 QString format( const QDateTime& dt ) {
01032 return dt.toString( QString::fromAscii( "ddd" ) ).left( 1 );
01033 }
01034 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01035 Q_UNUSED(dt);
01036
01037 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
01038 QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toAlignedRect();
01039 }
01040 };
01041 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01042 Private::HeaderDay, new DayFormatter );
01043
01044 class WeekFormatter : public Private::DateTextFormatter {
01045 public:
01046 virtual ~WeekFormatter() {}
01047 QString format( const QDateTime& dt ) {
01048 return QString::number(dt.date().weekNumber()) + QLatin1String("/") + QString::number(dt.date().year());
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, 0.0 ),
01054 QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
01055 }
01056 };
01057 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01058 Private::HeaderWeek, new WeekFormatter );
01059 }
01060
01064 void DateTimeGrid::paintWeekScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01065 qreal offset, QWidget* widget )
01066 {
01067 class WeekFormatter : public Private::DateTextFormatter {
01068 public:
01069 virtual ~WeekFormatter() {}
01070
01071 QString format( const QDateTime& dt ) {
01072 return QString::number( dt.date().weekNumber() );
01073 }
01074 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01075 Q_UNUSED(dt);
01076
01077 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01078 QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
01079 }
01080 };
01081 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01082 Private::HeaderWeek, new WeekFormatter );
01083
01084 class MonthFormatter : public Private::DateTextFormatter {
01085 public:
01086 virtual ~MonthFormatter() {}
01087
01088 QString format( const QDateTime& dt ) {
01089 return QLocale().monthName(dt.date().month(), QLocale::LongFormat) + QLatin1String("/") + QString::number(dt.date().year());
01090 }
01091 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01092 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01093 QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01094 }
01095 };
01096 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01097 Private::HeaderMonth, new MonthFormatter );
01098 }
01099
01103 void DateTimeGrid::paintMonthScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01104 qreal offset, QWidget* widget )
01105 {
01106 class MonthFormatter : public Private::DateTextFormatter {
01107 public:
01108 virtual ~MonthFormatter() {}
01109
01110 QString format( const QDateTime& dt ) {
01111 return QLocale().monthName(dt.date().month(), QLocale::ShortFormat) + QLatin1String("/") + QString::number(dt.date().year());
01112 }
01113 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01114 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01115 QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01116 }
01117 };
01118 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01119 Private::HeaderMonth, new MonthFormatter );
01120
01121 class YearFormatter : public Private::DateTextFormatter {
01122 public:
01123 virtual ~YearFormatter() {}
01124
01125 QString format( const QDateTime& dt ) {
01126 return QString::number( dt.date().year() );
01127 }
01128 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01129 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01130 QSizeF( dayWidth * dt.date().daysInYear(), headerRect.height() / 2.0 ) ).toRect();
01131 }
01132 };
01133 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01134 Private::HeaderYear, new YearFormatter );
01135 }
01136
01140 void DateTimeGrid::drawDayBackground(QPainter* painter, const QRectF& rect, const QDate& date)
01141 {
01142 Q_UNUSED(painter);
01143 Q_UNUSED(rect);
01144 Q_UNUSED(date);
01145 }
01146
01150 void DateTimeGrid::drawDayForeground(QPainter* painter, const QRectF& rect, const QDate& date)
01151 {
01152 Q_UNUSED(painter);
01153 Q_UNUSED(rect);
01154 Q_UNUSED(date);
01155 }
01156
01160 QRectF DateTimeGrid::computeRect(const QDateTime& from, const QDateTime& to, const QRectF& rect) const
01161 {
01162 qreal topLeft = d->dateTimeToChartX(from);
01163 qreal topRight = d->dateTimeToChartX(to);
01164
01165 return QRectF(topLeft, rect.top(), topRight - topLeft, rect.height());
01166 }
01167
01171 QPair<QDateTime, QDateTime> DateTimeGrid::dateTimeRange(const QRectF& rect) const
01172 {
01173 QDateTime start;
01174 QDateTime end;
01175
01176 start = d->chartXtoDateTime(rect.left());
01177 end = d->chartXtoDateTime(rect.right());
01178
01179 return qMakePair(start, end);
01180 }
01181
01182 void DateTimeGrid::drawBackground(QPainter* paint, const QRectF& rect)
01183 {
01184 int offset = (int)dayWidth();
01185
01186
01187 QDate date = d->chartXtoDateTime(rect.left()).date();
01188
01189
01190 int startx = rect.left();
01191 int endx = rect.right();
01192
01193
01194 paint->save();
01195
01196
01197 while(1)
01198 {
01199 QDate nextDate = d->chartXtoDateTime(startx+1).date();
01200 if(date != nextDate)
01201 {
01202 QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
01203 dayRect = dayRect.adjusted(1, 0, 0, 0);
01204 drawDayBackground(paint, dayRect, date);
01205 break;
01206 }
01207
01208 ++startx;
01209 }
01210
01211
01212 for(int i=startx; i<endx; i+=offset)
01213 {
01214 date = d->chartXtoDateTime(i+1).date();
01215
01216 QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
01217 dayRect = dayRect.adjusted(1, 0, 0, 0);
01218 drawDayBackground(paint, dayRect, date);
01219 }
01220
01221
01222 paint->restore();
01223 }
01224
01225 void DateTimeGrid::drawForeground(QPainter* paint, const QRectF& rect)
01226 {
01227 int offset = (int)dayWidth();
01228
01229
01230 QDate date = d->chartXtoDateTime(rect.left()).date();
01231
01232
01233 int startx = rect.left();
01234 int endx = rect.right();
01235
01236
01237 paint->save();
01238
01239
01240 while(1)
01241 {
01242 QDate nextDate = d->chartXtoDateTime(startx+1).date();
01243 if(date != nextDate)
01244 {
01245 QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
01246 dayRect = dayRect.adjusted(1, 0, 0, 0);
01247 drawDayForeground(paint, dayRect, date);
01248 break;
01249 }
01250
01251 ++startx;
01252 }
01253
01254
01255 for(int i=startx; i<endx; i+=offset)
01256 {
01257 date = d->chartXtoDateTime(i+1).date();
01258
01259 QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
01260 dayRect = dayRect.adjusted(1, 0, 0, 0);
01261 drawDayForeground(paint, dayRect, date);
01262 }
01263
01264
01265 paint->restore();
01266 }
01267
01268 #undef d
01269
01270 #ifndef KDAB_NO_UNIT_TESTS
01271
01272 #include <QStandardItemModel>
01273 #include "unittest/test.h"
01274
01275 namespace {
01276 std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
01277 {
01278 #ifdef QT_NO_STL
01279 os << dt.toString().toLatin1().constData();
01280 #else
01281 os << dt.toString().toStdString();
01282 #endif
01283 return os;
01284 }
01285 }
01286
01287 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, DateTimeGrid, "test" ) {
01288 QStandardItemModel model( 3, 2 );
01289 DateTimeGrid grid;
01290 QDateTime dt = QDateTime::currentDateTime();
01291 grid.setModel( &model );
01292 QDateTime startdt = dt.addDays( -10 );
01293 grid.setStartDateTime( startdt );
01294
01295 model.setData( model.index( 0, 0 ), dt, StartTimeRole );
01296 model.setData( model.index( 0, 0 ), dt.addDays( 17 ), EndTimeRole );
01297
01298 model.setData( model.index( 2, 0 ), dt.addDays( 18 ), StartTimeRole );
01299 model.setData( model.index( 2, 0 ), dt.addDays( 19 ), EndTimeRole );
01300
01301 Span s = grid.mapToChart( model.index( 0, 0 ) );
01302
01303
01304 assertTrue( s.start()>0 );
01305 assertTrue( s.length()>0 );
01306
01307 assertTrue( startdt == grid.mapToDateTime( grid.mapFromDateTime( startdt ) ) );
01308
01309 grid.mapFromChart( s, model.index( 1, 0 ) );
01310
01311 QDateTime s1 = model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime();
01312 QDateTime e1 = model.data( model.index( 0, 0 ), EndTimeRole ).toDateTime();
01313 QDateTime s2 = model.data( model.index( 1, 0 ), StartTimeRole ).toDateTime();
01314 QDateTime e2 = model.data( model.index( 1, 0 ), EndTimeRole ).toDateTime();
01315
01316 assertTrue( s1.isValid() );
01317 assertTrue( e1.isValid() );
01318 assertTrue( s2.isValid() );
01319 assertTrue( e2.isValid() );
01320
01321 assertEqual( s1, s2 );
01322 assertEqual( e1, e2 );
01323
01324 assertTrue( grid.isSatisfiedConstraint( Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ) );
01325 assertFalse( grid.isSatisfiedConstraint( Constraint( model.index( 2, 0 ), model.index( 0, 0 ) ) ) );
01326
01327 s = grid.mapToChart( model.index( 0, 0 ) );
01328 s.setEnd( s.end()+100000. );
01329 bool rc = grid.mapFromChart( s, model.index( 0, 0 ) );
01330 assertTrue( rc );
01331 assertEqual( s1, model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime() );
01332 Span newspan = grid.mapToChart( model.index( 0, 0 ) );
01333 assertEqual( newspan.start(), s.start() );
01334 assertEqual( newspan.length(), s.length() );
01335
01336 {
01337 QDateTime startDateTime = QDateTime::currentDateTime();
01338 qreal dayWidth = 100;
01339 QDate currentDate = QDate::currentDate();
01340 QDateTime dt( QDate(currentDate.year(), 1, 1), QTime( 0, 0, 0, 0 ) );
01341 assert( dt.isValid() );
01342 qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
01343 result += startDateTime.time().msecsTo(dt.time())/1000.;
01344 result *= dayWidth/( 24.*60.*60. );
01345
01346 int days = static_cast<int>( result/dayWidth );
01347 qreal secs = result*( 24.*60.*60. )/dayWidth;
01348 QDateTime dt2 = startDateTime;
01349 QDateTime result2 = dt2.addDays( days ).addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) ).addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
01350
01351 assertEqual( dt, result2 );
01352 }
01353 }
01354
01355 #endif
01356
01357 #include "moc_kdganttdatetimegrid.cpp"