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 <QDebug>
00035 #include <QList>
00036
00037 #include <cassert>
00038
00039 using namespace KDGantt;
00040
00041 QDebug operator<<( QDebug dbg, KDGantt::DateTimeScaleFormatter::Range range )
00042 {
00043 switch( range ) {
00044 case KDGantt::DateTimeScaleFormatter::Second: dbg << "KDGantt::DateTimeScaleFormatter::Second"; break;
00045 case KDGantt::DateTimeScaleFormatter::Minute: dbg << "KDGantt::DateTimeScaleFormatter::Minute"; break;
00046 case KDGantt::DateTimeScaleFormatter::Hour: dbg << "KDGantt::DateTimeScaleFormatter::Hour"; break;
00047 case KDGantt::DateTimeScaleFormatter::Day: dbg << "KDGantt::DateTimeScaleFormatter::Day"; break;
00048 case KDGantt::DateTimeScaleFormatter::Week: dbg << "KDGantt::DateTimeScaleFormatter::Week"; break;
00049 case KDGantt::DateTimeScaleFormatter::Month: dbg << "KDGantt::DateTimeScaleFormatter::Month"; break;
00050 case KDGantt::DateTimeScaleFormatter::Year: dbg << "KDGantt::DateTimeScaleFormatter::Year"; break;
00051 }
00052 return dbg;
00053 }
00054
00055
00063
00064
00065
00066 qreal DateTimeGrid::Private::dateTimeToChartX( const QDateTime& dt ) const
00067 {
00068 assert( startDateTime.isValid() );
00069 qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
00070 result += startDateTime.time().msecsTo(dt.time())/1000.;
00071 result *= dayWidth/( 24.*60.*60. );
00072
00073 return result;
00074 }
00075
00076 QDateTime DateTimeGrid::Private::chartXtoDateTime( qreal x ) const
00077 {
00078 assert( startDateTime.isValid() );
00079 int days = static_cast<int>( x/dayWidth );
00080 qreal secs = x*( 24.*60.*60. )/dayWidth;
00081 QDateTime dt = startDateTime;
00082 QDateTime result = dt.addDays( days )
00083 .addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) )
00084 .addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
00085 return result;
00086 }
00087
00088 #define d d_func()
00089
00117 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format,
00118 const QString& templ, Qt::Alignment alignment )
00119 : _d( new Private( range, format, templ, alignment ) )
00120 {
00121 }
00122
00123 DateTimeScaleFormatter::DateTimeScaleFormatter( Range range, const QString& format, Qt::Alignment alignment )
00124 : _d( new Private( range, format, QString::fromLatin1( "%1" ), alignment ) )
00125 {
00126 }
00127
00128 DateTimeScaleFormatter::DateTimeScaleFormatter( const DateTimeScaleFormatter& other )
00129 : _d( new Private( other.range(), other.format(), other.d->templ, other.alignment() ) )
00130 {
00131 }
00132
00133 DateTimeScaleFormatter::~DateTimeScaleFormatter()
00134 {
00135 delete _d;
00136 }
00137
00138 DateTimeScaleFormatter& DateTimeScaleFormatter::operator=( const DateTimeScaleFormatter& other )
00139 {
00140 delete _d;
00141 _d = new Private( other.range(), other.format(), other.d->templ, other.alignment() );
00142 return *this;
00143 }
00144
00147 QString DateTimeScaleFormatter::format() const
00148 {
00149 return d->format;
00150 }
00151
00154 QString DateTimeScaleFormatter::format( const QDateTime& datetime ) const
00155 {
00156 QString result = d->format;
00157
00158 const QString shortWeekNumber = QString::number( datetime.date().weekNumber() );
00159 const QString longWeekNumber = ( shortWeekNumber.length() == 1 ? QString::fromLatin1( "0" ) : QString() ) + shortWeekNumber;
00160 result.replace( QString::fromLatin1( "ww" ), longWeekNumber );
00161 result.replace( QString::fromLatin1( "w" ), shortWeekNumber );
00162 result = datetime.toLocalTime().toString( result );
00163 return result;
00164 }
00165
00166 QString DateTimeScaleFormatter::text( const QDateTime& datetime ) const
00167 {
00168 return d->templ.arg( format( datetime ) );
00169 }
00170
00173 DateTimeScaleFormatter::Range DateTimeScaleFormatter::range() const
00174 {
00175 return d->range;
00176 }
00177
00178 Qt::Alignment DateTimeScaleFormatter::alignment() const
00179 {
00180 return d->alignment;
00181 }
00182
00186 QDateTime DateTimeScaleFormatter::nextRangeBegin( const QDateTime& datetime ) const
00187 {
00188 QDateTime result = datetime;
00189 switch( d->range )
00190 {
00191 case Second:
00192 result = result.addSecs( 60 );
00193 break;
00194 case Minute:
00195
00196 result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00197 result = result.addSecs( 60 );
00198 break;
00199 case Hour:
00200
00201 result.setTime( QTime( result.time().hour(), 0 ) );
00202 result = result.addSecs( 60 * 60 );
00203 break;
00204 case Day:
00205
00206 result.setTime( QTime( 0, 0 ) );
00207 result = result.addDays( 1 );
00208 break;
00209 case Week:
00210
00211 result.setTime( QTime( 0, 0 ) );
00212
00213 {
00214 const int weekNumber = result.date().weekNumber();
00215 while( weekNumber == result.date().weekNumber() )
00216 result = result.addDays( 1 );
00217 }
00218 break;
00219 case Month:
00220
00221 result.setTime( QTime( 0, 0 ) );
00222
00223 result.setDate( QDate( result.date().year(), result.date().month(), 1 ).addMonths( 1 ) );
00224 break;
00225 case Year:
00226
00227 result.setTime( QTime( 0, 0 ) );
00228
00229 result.setDate( QDate( result.date().year(), 1, 1 ).addYears( 1 ) );
00230 break;
00231 }
00232
00233 assert( result != datetime );
00234
00235 return result;
00236 }
00237
00241 QDateTime DateTimeScaleFormatter::currentRangeBegin( const QDateTime& datetime ) const
00242 {
00243 QDateTime result = datetime;
00244 switch( d->range )
00245 {
00246 case Second:
00247 break;
00248 case Minute:
00249
00250 result.setTime( QTime( result.time().hour(), result.time().minute() ) );
00251 break;
00252 case Hour:
00253
00254 result.setTime( QTime( result.time().hour(), 0 ) );
00255 break;
00256 case Day:
00257
00258 result.setTime( QTime( 0, 0 ) );
00259 break;
00260 case Week:
00261
00262 result.setTime( QTime( 0, 0 ) );
00263
00264 {
00265 const int weekNumber = result.date().weekNumber();
00266 while( weekNumber == result.date().addDays( -1 ).weekNumber() )
00267 result = result.addDays( -1 );
00268 }
00269 break;
00270 case Month:
00271
00272 result.setTime( QTime( 0, 0 ) );
00273
00274 result.setDate( QDate( result.date().year(), result.date().month(), 1 ) );
00275 break;
00276 case Year:
00277
00278 result.setTime( QTime( 0, 0 ) );
00279
00280 result.setDate( QDate( result.date().year(), 1, 1 ) );
00281 break;
00282 }
00283 return result;
00284 }
00285
00286 DateTimeGrid::DateTimeGrid() : AbstractGrid( new Private )
00287 {
00288 }
00289
00290 DateTimeGrid::~DateTimeGrid()
00291 {
00292 }
00293
00298 QDateTime DateTimeGrid::startDateTime() const
00299 {
00300 return d->startDateTime;
00301 }
00302
00308 void DateTimeGrid::setStartDateTime( const QDateTime& dt )
00309 {
00310 d->startDateTime = dt;
00311 emit gridChanged();
00312 }
00313
00318 qreal DateTimeGrid::dayWidth() const
00319 {
00320 return d->dayWidth;
00321 }
00322
00325 qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
00326 {
00327 return d->dateTimeToChartX( dt );
00328 }
00329
00332 QDateTime DateTimeGrid::mapToDateTime( qreal x ) const
00333 {
00334 return d->chartXtoDateTime( x );
00335 }
00336
00341 void DateTimeGrid::setDayWidth( qreal w )
00342 {
00343 assert( w>0 );
00344 d->dayWidth = w;
00345 emit gridChanged();
00346 }
00347
00353 void DateTimeGrid::setScale( Scale s )
00354 {
00355 d->scale = s;
00356 emit gridChanged();
00357 }
00358
00365 DateTimeGrid::Scale DateTimeGrid::scale() const
00366 {
00367 return d->scale;
00368 }
00369
00377 void DateTimeGrid::setUserDefinedLowerScale( DateTimeScaleFormatter* lower )
00378 {
00379 delete d->lower;
00380 d->lower = lower;
00381 emit gridChanged();
00382 }
00383
00391 void DateTimeGrid::setUserDefinedUpperScale( DateTimeScaleFormatter* upper )
00392 {
00393 delete d->upper;
00394 d->upper = upper;
00395 emit gridChanged();
00396 }
00397
00400 DateTimeScaleFormatter* DateTimeGrid::userDefinedLowerScale() const
00401 {
00402 return d->lower;
00403 }
00404
00407 DateTimeScaleFormatter* DateTimeGrid::userDefinedUpperScale() const
00408 {
00409 return d->upper;
00410 }
00411
00417 void DateTimeGrid::setWeekStart( Qt::DayOfWeek ws )
00418 {
00419 d->weekStart = ws;
00420 emit gridChanged();
00421 }
00422
00424 Qt::DayOfWeek DateTimeGrid::weekStart() const
00425 {
00426 return d->weekStart;
00427 }
00428
00435 void DateTimeGrid::setFreeDays( const QSet<Qt::DayOfWeek>& fd )
00436 {
00437 d->freeDays = fd;
00438 emit gridChanged();
00439 }
00440
00442 QSet<Qt::DayOfWeek> DateTimeGrid::freeDays() const
00443 {
00444 return d->freeDays;
00445 }
00446
00449 void DateTimeGrid::setFreeDaysBrush(const QBrush brush)
00450 {
00451 d->freeDaysBrush = brush;
00452 }
00453
00457 QBrush DateTimeGrid::freeDaysBrush() const
00458 {
00459 return d->freeDaysBrush;
00460 }
00461
00463 bool DateTimeGrid::rowSeparators() const
00464 {
00465 return d->rowSeparators;
00466 }
00468 void DateTimeGrid::setRowSeparators( bool enable )
00469 {
00470 d->rowSeparators = enable;
00471 }
00472
00477 void DateTimeGrid::setNoInformationBrush( const QBrush& brush )
00478 {
00479 d->noInformationBrush = brush;
00480 emit gridChanged();
00481 }
00482
00485 QBrush DateTimeGrid::noInformationBrush() const
00486 {
00487 return d->noInformationBrush;
00488 }
00489
00494 qreal DateTimeGrid::mapToChart( const QVariant& value ) const
00495 {
00496 if ( ! qVariantCanConvert<QDateTime>( value ) ||
00497 ( value.type() == QVariant::String && qVariantValue<QString>(value).isEmpty() ) )
00498 {
00499 return -1.0;
00500 }
00501 return d->dateTimeToChartX( value.toDateTime() );
00502 }
00503
00508 QVariant DateTimeGrid::mapFromChart( qreal x ) const
00509 {
00510 return d->chartXtoDateTime( x );
00511 }
00512
00516 Span DateTimeGrid::mapToChart( const QModelIndex& idx ) const
00517 {
00518 assert( model() );
00519 if ( !idx.isValid() ) return Span();
00520 assert( idx.model()==model() );
00521 const QVariant sv = model()->data( idx, StartTimeRole );
00522 const QVariant ev = model()->data( idx, EndTimeRole );
00523 if( qVariantCanConvert<QDateTime>(sv) &&
00524 qVariantCanConvert<QDateTime>(ev) &&
00525 !(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) &&
00526 !(ev.type() == QVariant::String && qVariantValue<QString>(ev).isEmpty())
00527 ) {
00528 QDateTime st = sv.toDateTime();
00529 QDateTime et = ev.toDateTime();
00530 if ( et.isValid() && st.isValid() ) {
00531 qreal sx = d->dateTimeToChartX( st );
00532 qreal ex = d->dateTimeToChartX( et )-sx;
00533
00534 return Span( sx, ex);
00535 }
00536 }
00537
00538 if( qVariantCanConvert<QDateTime>(sv) && !(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) ) {
00539 QDateTime st = sv.toDateTime();
00540 if ( st.isValid() ) {
00541 qreal sx = d->dateTimeToChartX( st );
00542 return Span( sx, 0 );
00543 }
00544 }
00545 return Span();
00546 }
00547
00548 #if 0
00549 static void debug_print_idx( const QModelIndex& idx )
00550 {
00551 if ( !idx.isValid() ) {
00552 qDebug() << "[Invalid]";
00553 return;
00554 }
00555 QDateTime st = idx.data( StartTimeRole ).toDateTime();
00556 QDateTime et = idx.data( EndTimeRole ).toDateTime();
00557 qDebug() << idx << "["<<st<<et<<"]";
00558 }
00559 #endif
00560
00575 bool DateTimeGrid::mapFromChart( const Span& span, const QModelIndex& idx,
00576 const QList<Constraint>& constraints ) const
00577 {
00578 assert( model() );
00579 if ( !idx.isValid() ) return false;
00580 assert( idx.model()==model() );
00581
00582 QDateTime st = d->chartXtoDateTime(span.start());
00583 QDateTime et = d->chartXtoDateTime(span.start()+span.length());
00584
00585 Q_FOREACH( const Constraint& c, constraints ) {
00586 if ( c.type() != Constraint::TypeHard || !isSatisfiedConstraint( c )) continue;
00587 if ( c.startIndex() == idx ) {
00588 QDateTime tmpst = model()->data( c.endIndex(), StartTimeRole ).toDateTime();
00589
00590 if ( tmpst<et ) return false;
00591 } else if ( c.endIndex() == idx ) {
00592 QDateTime tmpet = model()->data( c.startIndex(), EndTimeRole ).toDateTime();
00593
00594 if ( tmpet>st ) return false;
00595 }
00596 }
00597 return model()->setData( idx, qVariantFromValue(st), StartTimeRole )
00598 && model()->setData( idx, qVariantFromValue(et), EndTimeRole );
00599 }
00600
00601 Qt::PenStyle DateTimeGrid::Private::gridLinePenStyle( QDateTime dt, Private::HeaderType headerType ) const
00602 {
00603 switch ( headerType ) {
00604 case Private::HeaderHour:
00605
00606 if ( dt.time().hour() == 0 )
00607 return Qt::SolidLine;
00608 return Qt::DashLine;
00609 case Private::HeaderDay:
00610
00611 if ( dt.date().dayOfWeek() == weekStart )
00612 return Qt::SolidLine;
00613 return Qt::DashLine;
00614 case Private::HeaderWeek:
00615
00616 if ( dt.date().day() == 1 )
00617 return Qt::SolidLine;
00618
00619 if ( dt.date().dayOfWeek() == weekStart )
00620 return Qt::DashLine;
00621 return Qt::NoPen;
00622 case Private::HeaderMonth:
00623
00624 if ( dt.date().dayOfYear() == 1 )
00625 return Qt::SolidLine;
00626
00627 if ( dt.date().day() == 1 )
00628 return Qt::DashLine;
00629 return Qt::NoPen;
00630 default:
00631
00632 break;
00633 }
00634
00635
00636 return Qt::NoPen;
00637 }
00638
00639 QDateTime DateTimeGrid::Private::adjustDateTimeForHeader( QDateTime dt, Private::HeaderType headerType ) const
00640 {
00641
00642 dt.setTime( QTime( 0, 0, 0, 0 ) );
00643
00644 switch ( headerType ) {
00645 case Private::HeaderWeek:
00646
00647 while ( dt.date().dayOfWeek() != weekStart )
00648 dt = dt.addDays( -1 );
00649 break;
00650 case Private::HeaderMonth:
00651
00652 dt = dt.addDays( 1 - dt.date().day() );
00653 break;
00654 case Private::HeaderYear:
00655
00656 dt = dt.addDays( 1 - dt.date().dayOfYear() );
00657 break;
00658 default:
00659
00660 break;
00661 }
00662
00663 return dt;
00664 }
00665
00666 void DateTimeGrid::Private::paintVerticalLines( QPainter* painter,
00667 const QRectF& sceneRect,
00668 const QRectF& exposedRect,
00669 QWidget* widget,
00670 Private::HeaderType headerType )
00671 {
00672 QDateTime dt = chartXtoDateTime( exposedRect.left() );
00673 dt = adjustDateTimeForHeader( dt, headerType );
00674
00675 int offsetSeconds = 0;
00676 int offsetDays = 0;
00677
00678 if ( headerType == Private::HeaderHour )
00679 offsetSeconds = 60*60;
00680 else
00681 offsetDays = 1;
00682
00683 for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
00684 dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), x = dateTimeToChartX( dt ) ) {
00685 if ( x >= exposedRect.left() ) {
00686 QPen pen = painter->pen();
00687 pen.setBrush( QApplication::palette().dark() );
00688 pen.setStyle( gridLinePenStyle( dt, headerType ) );
00689 painter->setPen( pen );
00690 if ( freeDays.contains( static_cast<Qt::DayOfWeek>( dt.date().dayOfWeek() ) ) ) {
00691 if(freeDaysBrush.style() == Qt::NoBrush)
00692 painter->setBrush( widget?widget->palette().midlight()
00693 :QApplication::palette().midlight() );
00694 else
00695 painter->setBrush(freeDaysBrush);
00696
00697 painter->fillRect( QRectF( x, exposedRect.top(), dayWidth, exposedRect.height() ), painter->brush() );
00698 }
00699 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00700 }
00701 }
00702 }
00703
00704 void DateTimeGrid::Private::paintVerticalUserDefinedLines( QPainter* painter,
00705 const QRectF& sceneRect,
00706 const QRectF& exposedRect,
00707 const DateTimeScaleFormatter* formatter,
00708 QWidget* widget )
00709 {
00710 Q_UNUSED( widget );
00711 QDateTime dt = chartXtoDateTime( exposedRect.left() );
00712 dt = formatter->currentRangeBegin( dt );
00713 QPen pen = painter->pen();
00714 pen.setBrush( QApplication::palette().dark() );
00715 pen.setStyle( Qt::DashLine );
00716 painter->setPen( pen );
00717 for ( qreal x = dateTimeToChartX( dt ); x < exposedRect.right();
00718 dt = formatter->nextRangeBegin( dt ),x=dateTimeToChartX( dt ) ) {
00719 if ( x >= exposedRect.left() ) {
00720
00721 painter->drawLine( QPointF( x, sceneRect.top() ), QPointF( x, sceneRect.bottom() ) );
00722 }
00723 }
00724 }
00725
00726 DateTimeGrid::Private::HeaderType DateTimeGrid::Private::headerTypeForScale( DateTimeGrid::Scale scale )
00727 {
00728 switch ( scale ) {
00729 case ScaleHour:
00730 return Private::HeaderHour;
00731 case ScaleDay:
00732 return Private::HeaderDay;
00733 case ScaleWeek:
00734 return Private::HeaderWeek;
00735 case ScaleMonth:
00736 return Private::HeaderMonth;
00737 default:
00738
00739 assert( false );
00740 break;
00741 }
00742 }
00743
00744 void DateTimeGrid::paintGrid( QPainter* painter,
00745 const QRectF& sceneRect,
00746 const QRectF& exposedRect,
00747 AbstractRowController* rowController,
00748 QWidget* widget )
00749 {
00750
00751 switch( scale() ) {
00752 case ScaleHour:
00753 case ScaleDay:
00754 case ScaleWeek:
00755 case ScaleMonth:
00756 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, d->headerTypeForScale( scale() ) );
00757 break;
00758 case ScaleAuto: {
00759 const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00760 const qreal dayw = dayWidth();
00761 if ( dayw > 24*60*60*tabw ) {
00762
00763 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->minute_lower, widget );
00764 } else if ( dayw > 24*60*tabw ) {
00765 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderHour );
00766 } else if ( dayw > 24*tabw ) {
00767 d->paintVerticalLines( painter, sceneRect, exposedRect, widget, Private::HeaderDay );
00768 } else if ( dayw > tabw ) {
00769 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->week_lower, widget );
00770 } else if ( 4*dayw > tabw ) {
00771 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->month_lower, widget );
00772 } else {
00773 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, &d->year_lower, widget );
00774 }
00775 break;
00776 }
00777 case ScaleUserDefined:
00778 d->paintVerticalUserDefinedLines( painter, sceneRect, exposedRect, d->lower, widget );
00779 break;
00780 }
00781 if ( rowController ) {
00782
00783 QPen pen = painter->pen();
00784 pen.setBrush( QApplication::palette().dark() );
00785 pen.setStyle( Qt::DashLine );
00786 painter->setPen( pen );
00787 QModelIndex idx = rowController->indexAt( qRound( exposedRect.top() ) );
00788 if ( rowController->indexAbove( idx ).isValid() ) idx = rowController->indexAbove( idx );
00789 qreal y = 0;
00790 while ( y < exposedRect.bottom() && idx.isValid() ) {
00791 const Span s = rowController->rowGeometry( idx );
00792 y = s.start()+s.length();
00793 if ( d->rowSeparators ) {
00794 painter->drawLine( QPointF( sceneRect.left(), y ),
00795 QPointF( sceneRect.right(), y ) );
00796 }
00797 if ( !idx.data( ItemTypeRole ).isValid() && d->noInformationBrush.style() != Qt::NoBrush ) {
00798 painter->fillRect( QRectF( exposedRect.left(), s.start(), exposedRect.width(), s.length() ), d->noInformationBrush );
00799 }
00800
00801
00802 idx = rowController->indexBelow( idx );
00803 }
00804 }
00805 }
00806
00807 int DateTimeGrid::Private::tabHeight( const QString& txt, QWidget* widget ) const
00808 {
00809 QStyleOptionHeader opt;
00810 if ( widget ) opt.initFrom( widget );
00811 opt.text = txt;
00812 QStyle* style;
00813 if ( widget ) style = widget->style();
00814 else style = QApplication::style();
00815 QSize s = style->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), widget);
00816 return s.height();
00817 }
00818
00819 void DateTimeGrid::Private::getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper)
00820 {
00821 const qreal tabw = QApplication::fontMetrics().width( QLatin1String( "XXXXX" ) );
00822 const qreal dayw = dayWidth;
00823 if ( dayw > 24*60*60*tabw ) {
00824 *lower = &minute_lower;
00825 *upper = &minute_upper;
00826 } else if ( dayw > 24*60*tabw ) {
00827 *lower = &hour_lower;
00828 *upper = &hour_upper;
00829 } else if ( dayw > 24*tabw ) {
00830 *lower = &day_lower;
00831 *upper = &day_upper;
00832 } else if ( dayw > tabw ) {
00833 *lower = &week_lower;
00834 *upper = &week_upper;
00835 } else if ( 4*dayw > tabw ) {
00836 *lower = &month_lower;
00837 *upper = &month_upper;
00838 } else {
00839 *lower = &year_lower;
00840 *upper = &year_upper;
00841 }
00842 }
00843
00844
00845 void DateTimeGrid::paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
00846 qreal offset, QWidget* widget )
00847 {
00848 painter->save();
00849 QPainterPath clipPath;
00850 clipPath.addRect( headerRect );
00851 painter->setClipPath( clipPath, Qt::IntersectClip );
00852 switch( scale() )
00853 {
00854 case ScaleHour:
00855 paintHourScaleHeader( painter, headerRect, exposedRect, offset, widget );
00856 break;
00857 case ScaleDay:
00858 paintDayScaleHeader( painter, headerRect, exposedRect, offset, widget );
00859 break;
00860 case ScaleWeek:
00861 paintWeekScaleHeader( painter, headerRect, exposedRect, offset, widget );
00862 break;
00863 case ScaleMonth:
00864 paintMonthScaleHeader( painter, headerRect, exposedRect, offset, widget );
00865 break;
00866 case ScaleAuto:
00867 {
00868 DateTimeScaleFormatter *lower, *upper;
00869 d->getAutomaticFormatters( &lower, &upper );
00870 const qreal lowerHeight = d->tabHeight( lower->text( startDateTime() ) );
00871 const qreal upperHeight = d->tabHeight( upper->text( startDateTime() ) );
00872 const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00873
00874 const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00875 const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 );
00876
00877 paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, lower, widget );
00878 paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, upper, widget );
00879 break;
00880 }
00881 case ScaleUserDefined:
00882 {
00883 const qreal lowerHeight = d->tabHeight( d->lower->text( startDateTime() ) );
00884 const qreal upperHeight = d->tabHeight( d->upper->text( startDateTime() ) );
00885 const qreal upperRatio = upperHeight/( lowerHeight+upperHeight );
00886
00887 const QRectF upperHeaderRect( headerRect.x(), headerRect.top(), headerRect.width()-1, headerRect.height() * upperRatio );
00888 const QRectF lowerHeaderRect( headerRect.x(), upperHeaderRect.bottom()+1, headerRect.width()-1, headerRect.height()-upperHeaderRect.height()-1 );
00889
00890 paintUserDefinedHeader( painter, lowerHeaderRect, exposedRect, offset, d->lower, widget );
00891 paintUserDefinedHeader( painter, upperHeaderRect, exposedRect, offset, d->upper, widget );
00892 }
00893 break;
00894 }
00895 painter->restore();
00896 }
00897
00898 void DateTimeGrid::paintUserDefinedHeader( QPainter* painter,
00899 const QRectF& headerRect, const QRectF& exposedRect,
00900 qreal offset, const DateTimeScaleFormatter* formatter,
00901 QWidget* widget )
00902 {
00903 const QStyle* const style = widget ? widget->style() : QApplication::style();
00904
00905 QDateTime dt = formatter->currentRangeBegin( d->chartXtoDateTime( offset + exposedRect.left() ));
00906 qreal x = d->dateTimeToChartX( dt );
00907
00908 while( x < exposedRect.right() + offset ) {
00909 const QDateTime next = formatter->nextRangeBegin( dt );
00910 const qreal nextx = d->dateTimeToChartX( next );
00911
00912 QStyleOptionHeader opt;
00913 if ( widget ) opt.init( widget );
00914 opt.rect = QRectF( x - offset+1, headerRect.top(), qMax( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect();
00915 opt.textAlignment = formatter->alignment();
00916 opt.text = formatter->text( dt );
00917 style->drawControl( QStyle::CE_Header, &opt, painter, widget );
00918
00919 dt = next;
00920 x = nextx;
00921 }
00922 }
00923
00924 void DateTimeGrid::Private::paintHeader( QPainter* painter,
00925 const QRectF& headerRect, const QRectF& exposedRect,
00926 qreal offset, QWidget* widget,
00927 Private::HeaderType headerType,
00928 DateTextFormatter *formatter )
00929 {
00930 QStyle* style = widget?widget->style():QApplication::style();
00931
00932 const qreal left = exposedRect.left() + offset;
00933 const qreal right = exposedRect.right() + offset;
00934
00935
00936 QDateTime dt = chartXtoDateTime( left );
00937 dt = adjustDateTimeForHeader( dt, headerType );
00938
00939 int offsetSeconds = 0;
00940 int offsetDays = 0;
00941 int offsetMonths = 0;
00942
00943 switch ( headerType ) {
00944 case Private::HeaderHour:
00945 offsetSeconds = 60*60;
00946 break;
00947 case Private::HeaderDay:
00948 offsetDays = 1;
00949 break;
00950 case Private::HeaderWeek:
00951 offsetDays = 7;
00952 break;
00953 case Private::HeaderMonth:
00954 offsetMonths = 1;
00955 break;
00956 case Private::HeaderYear:
00957 offsetMonths = 12;
00958 break;
00959 default:
00960
00961 assert( false );
00962 break;
00963 }
00964
00965 for ( qreal x = dateTimeToChartX( dt ); x < right;
00966 dt = dt.addSecs( offsetSeconds ), dt = dt.addDays( offsetDays ), dt = dt.addMonths( offsetMonths ),
00967 x = dateTimeToChartX( dt ) ) {
00968 QStyleOptionHeader opt;
00969 if ( widget ) opt.init( widget );
00970 opt.rect = formatter->textRect( x, offset, dayWidth, headerRect, dt );
00971 opt.text = formatter->format( dt );
00972 opt.textAlignment = Qt::AlignCenter;
00973 style->drawControl(QStyle::CE_Header, &opt, painter, widget);
00974 }
00975 }
00976
00980 void DateTimeGrid::paintHourScaleHeader( QPainter* painter,
00981 const QRectF& headerRect, const QRectF& exposedRect,
00982 qreal offset, QWidget* widget )
00983 {
00984 class HourFormatter : public Private::DateTextFormatter {
00985 public:
00986 virtual ~HourFormatter() {}
00987
00988 QString format( const QDateTime& dt ) {
00989 return dt.time().toString( QString::fromAscii( "hh" ) );
00990 }
00991 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
00992 Q_UNUSED(dt);
00993
00994 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
00995 QSizeF( dayWidth / 24.0, headerRect.height() / 2.0 ) ).toAlignedRect();
00996 }
00997 };
00998 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
00999 Private::HeaderHour, new HourFormatter );
01000
01001 class DayFormatter : public Private::DateTextFormatter {
01002 public:
01003 virtual ~DayFormatter() {}
01004 QString format( const QDateTime& dt ) {
01005 return dt.date().toString();
01006 }
01007 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01008 Q_UNUSED(dt);
01009
01010 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01011 QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toRect();
01012 }
01013 };
01014 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01015 Private::HeaderDay, new DayFormatter );
01016 }
01017
01021 void DateTimeGrid::paintDayScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01022 qreal offset, QWidget* widget )
01023 {
01024 class DayFormatter : public Private::DateTextFormatter {
01025 public:
01026 virtual ~DayFormatter() {}
01027
01028 QString format( const QDateTime& dt ) {
01029 return dt.toString( QString::fromAscii( "ddd" ) ).left( 1 );
01030 }
01031 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01032 Q_UNUSED(dt);
01033
01034 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset + 1.0, headerRect.height() / 2.0 ),
01035 QSizeF( dayWidth, headerRect.height() / 2.0 ) ).toAlignedRect();
01036 }
01037 };
01038 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01039 Private::HeaderDay, new DayFormatter );
01040
01041 class WeekFormatter : public Private::DateTextFormatter {
01042 public:
01043 virtual ~WeekFormatter() {}
01044 QString format( const QDateTime& dt ) {
01045 return QString::number( dt.date().weekNumber() );
01046 }
01047 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01048 Q_UNUSED(dt);
01049
01050 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01051 QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
01052 }
01053 };
01054 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01055 Private::HeaderWeek, new WeekFormatter );
01056 }
01057
01061 void DateTimeGrid::paintWeekScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01062 qreal offset, QWidget* widget )
01063 {
01064 class WeekFormatter : public Private::DateTextFormatter {
01065 public:
01066 virtual ~WeekFormatter() {}
01067
01068 QString format( const QDateTime& dt ) {
01069 return QString::number( dt.date().weekNumber() );
01070 }
01071 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01072 Q_UNUSED(dt);
01073
01074 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01075 QSizeF( dayWidth * 7, headerRect.height() / 2.0 ) ).toRect();
01076 }
01077 };
01078 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01079 Private::HeaderWeek, new WeekFormatter );
01080
01081 class MonthFormatter : public Private::DateTextFormatter {
01082 public:
01083 virtual ~MonthFormatter() {}
01084
01085 QString format( const QDateTime& dt ) {
01086 return QDate::longMonthName( dt.date().month() );
01087 }
01088 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01089 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01090 QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01091 }
01092 };
01093 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01094 Private::HeaderMonth, new MonthFormatter );
01095 }
01096
01100 void DateTimeGrid::paintMonthScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
01101 qreal offset, QWidget* widget )
01102 {
01103 class MonthFormatter : public Private::DateTextFormatter {
01104 public:
01105 virtual ~MonthFormatter() {}
01106
01107 QString format( const QDateTime& dt ) {
01108 return QDate::shortMonthName( dt.date().month() );
01109 }
01110 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01111 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, headerRect.height() / 2.0 ),
01112 QSizeF( dayWidth * dt.date().daysInMonth(), headerRect.height() / 2.0 ) ).toRect();
01113 }
01114 };
01115 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01116 Private::HeaderMonth, new MonthFormatter );
01117
01118 class YearFormatter : public Private::DateTextFormatter {
01119 public:
01120 virtual ~YearFormatter() {}
01121
01122 QString format( const QDateTime& dt ) {
01123 return QString::number( dt.date().year() );
01124 }
01125 QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) {
01126 return QRectF( QPointF( x, headerRect.top() ) + QPointF( -offset, 0.0 ),
01127 QSizeF( dayWidth * dt.date().daysInYear(), headerRect.height() / 2.0 ) ).toRect();
01128 }
01129 };
01130 d->paintHeader( painter, headerRect, exposedRect, offset, widget,
01131 Private::HeaderYear, new YearFormatter );
01132 }
01133
01137 void DateTimeGrid::drawDayBackground(QPainter* painter, const QRectF& rect, const QDate& date)
01138 {
01139 Q_UNUSED(painter);
01140 Q_UNUSED(rect);
01141 Q_UNUSED(date);
01142 }
01143
01147 void DateTimeGrid::drawDayForeground(QPainter* painter, const QRectF& rect, const QDate& date)
01148 {
01149 Q_UNUSED(painter);
01150 Q_UNUSED(rect);
01151 Q_UNUSED(date);
01152 }
01153
01157 QRectF DateTimeGrid::computeRect(const QDateTime& from, const QDateTime& to, const QRectF& rect) const
01158 {
01159 qreal topLeft = d->dateTimeToChartX(from);
01160 qreal topRight = d->dateTimeToChartX(to);
01161
01162 return QRectF(topLeft, rect.top(), topRight - topLeft, rect.height());
01163 }
01164
01168 QPair<QDateTime, QDateTime> DateTimeGrid::dateTimeRange(const QRectF& rect) const
01169 {
01170 QDateTime start;
01171 QDateTime end;
01172
01173 start = d->chartXtoDateTime(rect.left());
01174 end = d->chartXtoDateTime(rect.right());
01175
01176 return qMakePair(start, end);
01177 }
01178
01179 void DateTimeGrid::drawBackground(QPainter* paint, const QRectF& rect)
01180 {
01181 int offset = (int)dayWidth();
01182
01183
01184 QDate date = d->chartXtoDateTime(rect.left()).date();
01185
01186
01187 int startx = rect.left();
01188 int endx = rect.right();
01189
01190
01191 paint->save();
01192
01193
01194 while(1)
01195 {
01196 QDate nextDate = d->chartXtoDateTime(startx+1).date();
01197 if(date != nextDate)
01198 {
01199 QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
01200 dayRect = dayRect.adjusted(1, 0, 0, 0);
01201 drawDayBackground(paint, dayRect, date);
01202 break;
01203 }
01204
01205 ++startx;
01206 }
01207
01208
01209 for(int i=startx; i<endx; i+=offset)
01210 {
01211 date = d->chartXtoDateTime(i+1).date();
01212
01213 QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
01214 dayRect = dayRect.adjusted(1, 0, 0, 0);
01215 drawDayBackground(paint, dayRect, date);
01216 }
01217
01218
01219 paint->restore();
01220 }
01221
01222 void DateTimeGrid::drawForeground(QPainter* paint, const QRectF& rect)
01223 {
01224 int offset = (int)dayWidth();
01225
01226
01227 QDate date = d->chartXtoDateTime(rect.left()).date();
01228
01229
01230 int startx = rect.left();
01231 int endx = rect.right();
01232
01233
01234 paint->save();
01235
01236
01237 while(1)
01238 {
01239 QDate nextDate = d->chartXtoDateTime(startx+1).date();
01240 if(date != nextDate)
01241 {
01242 QRectF dayRect(startx-dayWidth(), rect.top(), dayWidth(), rect.height());
01243 dayRect = dayRect.adjusted(1, 0, 0, 0);
01244 drawDayForeground(paint, dayRect, date);
01245 break;
01246 }
01247
01248 ++startx;
01249 }
01250
01251
01252 for(int i=startx; i<endx; i+=offset)
01253 {
01254 date = d->chartXtoDateTime(i+1).date();
01255
01256 QRectF dayRect(i, rect.top(), dayWidth(), rect.height());
01257 dayRect = dayRect.adjusted(1, 0, 0, 0);
01258 drawDayForeground(paint, dayRect, date);
01259 }
01260
01261
01262 paint->restore();
01263 }
01264
01265 #undef d
01266
01267 #ifndef KDAB_NO_UNIT_TESTS
01268
01269 #include <QStandardItemModel>
01270 #include "unittest/test.h"
01271
01272 namespace {
01273 std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
01274 {
01275 #ifdef QT_NO_STL
01276 os << dt.toString().toLatin1().constData();
01277 #else
01278 os << dt.toString().toStdString();
01279 #endif
01280 return os;
01281 }
01282 }
01283
01284 KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, DateTimeGrid, "test" ) {
01285 QStandardItemModel model( 3, 2 );
01286 DateTimeGrid grid;
01287 QDateTime dt = QDateTime::currentDateTime();
01288 grid.setModel( &model );
01289 QDateTime startdt = dt.addDays( -10 );
01290 grid.setStartDateTime( startdt );
01291
01292 model.setData( model.index( 0, 0 ), dt, StartTimeRole );
01293 model.setData( model.index( 0, 0 ), dt.addDays( 17 ), EndTimeRole );
01294
01295 model.setData( model.index( 2, 0 ), dt.addDays( 18 ), StartTimeRole );
01296 model.setData( model.index( 2, 0 ), dt.addDays( 19 ), EndTimeRole );
01297
01298 Span s = grid.mapToChart( model.index( 0, 0 ) );
01299
01300
01301 assertTrue( s.start()>0 );
01302 assertTrue( s.length()>0 );
01303
01304 assertTrue( startdt == grid.mapToDateTime( grid.mapFromDateTime( startdt ) ) );
01305
01306 grid.mapFromChart( s, model.index( 1, 0 ) );
01307
01308 QDateTime s1 = model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime();
01309 QDateTime e1 = model.data( model.index( 0, 0 ), EndTimeRole ).toDateTime();
01310 QDateTime s2 = model.data( model.index( 1, 0 ), StartTimeRole ).toDateTime();
01311 QDateTime e2 = model.data( model.index( 1, 0 ), EndTimeRole ).toDateTime();
01312
01313 assertTrue( s1.isValid() );
01314 assertTrue( e1.isValid() );
01315 assertTrue( s2.isValid() );
01316 assertTrue( e2.isValid() );
01317
01318 assertEqual( s1, s2 );
01319 assertEqual( e1, e2 );
01320
01321 assertTrue( grid.isSatisfiedConstraint( Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ) );
01322 assertFalse( grid.isSatisfiedConstraint( Constraint( model.index( 2, 0 ), model.index( 0, 0 ) ) ) );
01323
01324 s = grid.mapToChart( model.index( 0, 0 ) );
01325 s.setEnd( s.end()+100000. );
01326 bool rc = grid.mapFromChart( s, model.index( 0, 0 ) );
01327 assertTrue( rc );
01328 assertEqual( s1, model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime() );
01329 Span newspan = grid.mapToChart( model.index( 0, 0 ) );
01330 assertEqual( newspan.start(), s.start() );
01331 assertEqual( newspan.length(), s.length() );
01332
01333 {
01334 QDateTime startDateTime = QDateTime::currentDateTime();
01335 qreal dayWidth = 100;
01336 QDate currentDate = QDate::currentDate();
01337 QDateTime dt( QDate(currentDate.year(), 1, 1), QTime( 0, 0, 0, 0 ) );
01338 assert( dt.isValid() );
01339 qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
01340 result += startDateTime.time().msecsTo(dt.time())/1000.;
01341 result *= dayWidth/( 24.*60.*60. );
01342
01343 int days = static_cast<int>( result/dayWidth );
01344 qreal secs = result*( 24.*60.*60. )/dayWidth;
01345 QDateTime dt2 = startDateTime;
01346 QDateTime result2 = dt2.addDays( days ).addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) ).addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
01347
01348 assertEqual( dt, result2 );
01349 }
01350 }
01351
01352 #endif
01353
01354 #include "moc_kdganttdatetimegrid.cpp"