1
/****************************************************************************
2
** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
4
** This file is part of the KD Gantt library.
6
** This file may be used under the terms of the GNU General Public
7
** License versions 2.0 or 3.0 as published by the Free Software
8
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
9
** included in the packaging of this file. Alternatively you may (at
10
** your option) use any later version of the GNU General Public
11
** License if such license has been publicly approved by
12
** Klarälvdalens Datakonsult AB (or its successors, if any).
14
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
15
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
16
** A PARTICULAR PURPOSE. Klarälvdalens Datakonsult AB reserves all rights
17
** not expressly granted herein.
19
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22
**********************************************************************/
23
#include "kdganttdatetimegrid.h"
24
#include "kdganttdatetimegrid_p.h"
26
#include "kdganttabstractrowcontroller.h"
28
#include <QApplication>
32
#include <QStyleOptionHeader>
38
using namespace KDGantt;
40
/*!\class KDGantt::DateTimeGrid
43
* This implementation of AbstractGrid works with QDateTime
44
* and shows days and week numbers in the header
46
* \todo Extend to work with hours, minutes,... as units too.
49
// TODO: I think maybe this class should be responsible
50
// for unit-transformation of the scene...
52
qreal DateTimeGrid::Private::dateTimeToChartX( const QDateTime& dt ) const
54
assert( startDateTime.isValid() );
55
qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
56
result += startDateTime.time().msecsTo(dt.time())/1000.;
57
result *= dayWidth/( 24.*60.*60. );
62
QDateTime DateTimeGrid::Private::chartXtoDateTime( qreal x ) const
64
assert( startDateTime.isValid() );
65
int days = static_cast<int>( x/dayWidth );
66
qreal secs = x*( 24.*60.*60. )/dayWidth;
67
QDateTime dt = startDateTime;
68
QDateTime result = dt.addDays( days )
69
.addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) )
70
.addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
76
DateTimeGrid::DateTimeGrid() : AbstractGrid( new Private )
80
DateTimeGrid::~DateTimeGrid()
84
/*! \returns The QDateTime used as start date for the grid.
86
* The default is three days before the current date.
88
QDateTime DateTimeGrid::startDateTime() const
90
return d->startDateTime;
93
/*! \param dt The start date of the grid. It is used as the beginning of the
94
* horizontal scrollbar in the view.
96
* Emits gridChanged() after the start date has changed.
98
void DateTimeGrid::setStartDateTime( const QDateTime& dt )
100
d->startDateTime = dt;
104
/*! \returns The width in pixels for each day in the grid.
106
* The default is 100 pixels.
108
qreal DateTimeGrid::dayWidth() const
113
/*! \param w The width in pixels for each day in the grid.
114
* Day width is limited to minimum 1.0.
116
* The signal gridChanged() is emitted after the day width is changed.
118
void DateTimeGrid::setDayWidth( qreal w )
120
qDebug()<<"DateTimeGrid::setDayWidth"<<w;
121
d->dayWidth = qMax( w, qreal(0.1) );
125
/*! \param s The scale to be used to paint the grid.
127
* The signal gridChanged() is emitted after the scale has changed.
130
void DateTimeGrid::setScale( Scale s )
136
/*! \returns The scale used to paint the grid.
138
* The default is ScaleAuto, which means the day scale will be used
139
* as long as the day width is less or equal to 500.
142
DateTimeGrid::Scale DateTimeGrid::scale() const
147
/*! \returns The format used to paint the hours.
149
* The default is "hh".
151
QString DateTimeGrid::hourFormat() const
153
return d->hourFormat;
156
/*! Set the format used to paint the hours.
158
* If @p format is empty, it is not set.
160
void DateTimeGrid::setHourFormat( const QString &format )
162
if ( ! format.isEmpty() ) {
163
d->hourFormat = format;
167
/*! \param factor The zoom factor
170
void DateTimeGrid::zoomIn( qreal factor )
172
setDayWidth( d->dayWidth * factor );
175
/*! \param factor The zoom factor
178
void DateTimeGrid::zoomOut( qreal factor )
180
setDayWidth( d->dayWidth * factor );
183
/*! \param ws The start day of the week.
185
* A solid line is drawn on the grid to mark the beginning of a new week.
186
* Emits gridChanged() after the start day has changed.
188
void DateTimeGrid::setWeekStart( Qt::DayOfWeek ws )
194
/*! \returns The start day of the week */
195
Qt::DayOfWeek DateTimeGrid::weekStart() const
200
/*! \param fd A set of days to mark as free in the grid.
202
* Free days are filled with the alternate base brush of the
203
* palette used by the view.
204
* The signal gridChanged() is emitted after the free days are changed.
206
void DateTimeGrid::setFreeDays( const QSet<Qt::DayOfWeek>& fd )
212
/*! \returns true if row separators are used. */
213
bool DateTimeGrid::rowSeparators() const
215
return d->rowSeparators;
217
/*! \param enable Whether to use row separators or not. */
218
void DateTimeGrid::setRowSeparators( bool enable )
220
d->rowSeparators = enable;
223
/*! \returns The days marked as free in the grid. */
224
QSet<Qt::DayOfWeek> DateTimeGrid::freeDays() const
230
* \param value The datetime to get the x value for.
231
* \returns The x value corresponding to \a value or -1.0 if \a value is not a datetime variant.
233
qreal DateTimeGrid::mapToChart( const QVariant& value ) const
235
if ( ! qVariantCanConvert<QDateTime>( value ) ||
236
( value.type() == QVariant::String && qVariantValue<QString>(value).isEmpty() ) )
240
return d->dateTimeToChartX( value.toDateTime() );
244
* \param x The x value get the datetime for.
245
* \returns The datetime corresponding to \a x or an invalid datetime if x cannot be mapped.
247
QVariant DateTimeGrid::mapFromChart( qreal x ) const
249
return d->chartXtoDateTime( x );
252
/*! \param idx The index to get the Span for.
253
* \returns The start and end pixels, in a Span, of the specified index.
255
Span DateTimeGrid::mapToChart( const QModelIndex& idx ) const
258
if ( !idx.isValid() ) return Span();
259
assert( idx.model()==model() );
260
const QVariant sv = model()->data( idx, StartTimeRole );
261
const QVariant ev = model()->data( idx, EndTimeRole );
262
if( qVariantCanConvert<QDateTime>(sv) &&
263
qVariantCanConvert<QDateTime>(ev) &&
264
!(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) &&
265
!(ev.type() == QVariant::String && qVariantValue<QString>(ev).isEmpty())
267
QDateTime st = sv.toDateTime();
268
QDateTime et = ev.toDateTime();
269
if ( et.isValid() && st.isValid() ) {
270
qreal sx = d->dateTimeToChartX( st );
271
qreal ex = d->dateTimeToChartX( et )-sx;
272
//qDebug() << "DateTimeGrid::mapToChart("<<st<<et<<") => "<< Span( sx, ex );
273
return Span( sx, ex);
276
// Special case for Events with only a start date
277
if( qVariantCanConvert<QDateTime>(sv) && !(sv.type() == QVariant::String && qVariantValue<QString>(sv).isEmpty()) ) {
278
QDateTime st = sv.toDateTime();
279
if ( st.isValid() ) {
280
qreal sx = d->dateTimeToChartX( st );
281
return Span( sx, 0 );
288
static void debug_print_idx( const QModelIndex& idx )
290
if ( !idx.isValid() ) {
291
qDebug() << "[Invalid]";
294
QDateTime st = idx.data( StartTimeRole ).toDateTime();
295
QDateTime et = idx.data( StartTimeRole ).toDateTime();
296
qDebug() << idx << "["<<st<<et<<"]";
300
/*! Maps the supplied Span to QDateTimes, and puts them as start time and
301
* end time for the supplied index.
303
* \param span The span used to map from.
304
* \param idx The index used for setting the start time and end time in the model.
305
* \param constraints A list of hard constraints to match against the start time and
306
* end time mapped from the span.
308
* \returns true if the start time and time was successfully added to the model, or false
310
* Also returns false if any of the constraints isn't satisfied. That is, if the start time of
311
* the constrained index is before the end time of the dependency index, or the end time of the
312
* constrained index is before the start time of the dependency index.
314
bool DateTimeGrid::mapFromChart( const Span& span, const QModelIndex& idx,
315
const QList<Constraint>& constraints ) const
318
if ( !idx.isValid() ) return false;
319
assert( idx.model()==model() );
321
QDateTime st = d->chartXtoDateTime(span.start());
322
QDateTime et = d->chartXtoDateTime(span.start()+span.length());
323
//qDebug() << "DateTimeGrid::mapFromChart("<<span<<") => "<< st << et;
324
Q_FOREACH( const Constraint& c, constraints ) {
325
if ( c.type() != Constraint::TypeHard || !isSatisfiedConstraint( c )) continue;
326
if ( c.startIndex() == idx ) {
327
QDateTime tmpst = model()->data( c.endIndex(), StartTimeRole ).toDateTime();
328
//qDebug() << tmpst << "<" << et <<"?";
329
if ( tmpst<et ) return false;
330
} else if ( c.endIndex() == idx ) {
331
QDateTime tmpet = model()->data( c.startIndex(), EndTimeRole ).toDateTime();
332
//qDebug() << tmpet << ">" << st <<"?";
333
if ( tmpet>st ) return false;
336
return model()->setData( idx, qVariantFromValue(st), StartTimeRole )
337
&& model()->setData( idx, qVariantFromValue(et), EndTimeRole );
340
DateTimeGrid::Scale DateTimeGrid::autoScale() const
342
Scale scale = ScaleDay;
343
if ( dayWidth() > 450) {
345
} else if (dayWidth() * 30 < 20) {
347
} else if (dayWidth() * 7 < 20) {
349
} else if (dayWidth() < 12) {
355
void DateTimeGrid::paintGrid( QPainter* painter,
356
const QRectF& sceneRect,
357
const QRectF& exposedRect,
358
AbstractRowController* rowController,
361
//qDebug()<<"paintGrid()"<<scale()<<dayWidth();
363
paintRowGrid(painter,sceneRect,exposedRect,rowController,widget);
367
paintHourGrid(painter,sceneRect,exposedRect,rowController,widget);
370
paintDayGrid(painter,sceneRect,exposedRect,rowController,widget);
373
paintWeekGrid(painter,sceneRect,exposedRect,rowController,widget);
376
paintMonthGrid(painter,sceneRect,exposedRect,rowController,widget);
379
paintYearGrid(painter,sceneRect,exposedRect,rowController,widget);
382
switch(autoScale()) {
384
paintHourGrid(painter,sceneRect,exposedRect,rowController,widget);
387
paintDayGrid(painter,sceneRect,exposedRect,rowController,widget);
390
paintWeekGrid(painter,sceneRect,exposedRect,rowController,widget);
393
paintMonthGrid(painter,sceneRect,exposedRect,rowController,widget);
396
paintYearGrid(painter,sceneRect,exposedRect,rowController,widget);
398
case ScaleAuto: // for completeness and remove warning
405
void DateTimeGrid::paintHourGrid( QPainter* painter,
406
const QRectF& /*sceneRect*/,
407
const QRectF& exposedRect,
408
AbstractRowController* /*rowController*/,
409
QWidget* /*widget*/ )
411
//qDebug()<<"paintHourGrid()"<<scale()<<dayWidth();
412
QDateTime dt = d->chartXtoDateTime( exposedRect.left() );
413
dt.setTime( QTime( dt.time().hour(), 0, 0, 0 ) );
414
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right(); dt = dt.addSecs( 60*60 ),x=d->dateTimeToChartX( dt ) ) {
415
QPen pen = painter->pen();
416
pen.setBrush( QApplication::palette().dark() );
417
if ( dt.time() == QTime( 23, 0, 0 ) ) {
418
pen.setStyle( Qt::SolidLine );
420
pen.setStyle( Qt::DashLine );
422
painter->setPen( pen );
423
x += ( dayWidth() / 24.0 ) - 1;
424
painter->drawLine( QPointF( x, exposedRect.top() ), QPointF( x, exposedRect.bottom() ) );
428
void DateTimeGrid::paintDayGrid( QPainter* painter,
429
const QRectF& /*sceneRect*/,
430
const QRectF& exposedRect,
431
AbstractRowController* /*rowController*/,
434
//qDebug()<<"paintDayGrid()"<<scale()<<dayWidth();
435
QDateTime dt = d->chartXtoDateTime( exposedRect.left() );
436
dt.setTime( QTime( 0, 0, 0, 0 ) );
437
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right(); dt = dt.addDays( 1 ),x=d->dateTimeToChartX( dt ) ) {
438
QPen pen = painter->pen();
439
pen.setBrush( QApplication::palette().dark() );
440
if ( dt.date().addDays( 1 ).dayOfWeek() == d->weekStart ) {
441
pen.setStyle( Qt::SolidLine );
443
pen.setStyle( Qt::DashLine );
445
painter->setPen( pen );
446
paintFreeDay( painter, x, exposedRect, dt.date(), widget );
448
painter->drawLine( QPointF( x, exposedRect.top() ), QPointF( x, exposedRect.bottom() ) );
452
void DateTimeGrid::paintWeekGrid( QPainter* painter,
453
const QRectF& /*sceneRect*/,
454
const QRectF& exposedRect,
455
AbstractRowController* /*rowController*/,
458
//qDebug()<<"paintWeekGrid()"<<scale()<<dayWidth();
459
QDateTime dt = d->chartXtoDateTime( exposedRect.left() );
460
dt.setTime( QTime( 0, 0, 0, 0 ) );
461
while ( dt.date().dayOfWeek() != d->weekStart ) dt = dt.addDays( -1 );
462
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right(); dt = dt.addDays( 1 ),x=d->dateTimeToChartX( dt ) ) {
463
QPen pen = painter->pen();
464
pen.setBrush( QApplication::palette().dark() );
465
if ( dt.date().addDays( 1 ).day() == 1 ) {
466
pen.setStyle( Qt::SolidLine );
467
} else if ( dt.date().addDays( 1 ).dayOfWeek() == d->weekStart ) {
468
pen.setStyle( Qt::DashLine );
470
pen.setStyle( Qt::NoPen );
472
painter->setPen( pen );
473
paintFreeDay( painter, x, exposedRect, dt.date(), widget );
474
if ( pen.style() != Qt::NoPen ) {
475
//qDebug()<<"paintWeekGrid()"<<dt;
477
painter->drawLine( QPointF( x, exposedRect.top() ), QPointF( x, exposedRect.bottom() ) );
482
void DateTimeGrid::paintMonthGrid( QPainter* painter,
483
const QRectF& /*sceneRect*/,
484
const QRectF& exposedRect,
485
AbstractRowController* /*rowController*/,
488
//qDebug()<<"paintMonthGrid()"<<scale()<<dayWidth();
490
// Paint a dashed line between each month and a solid line between the years.
491
QDateTime dt = d->chartXtoDateTime( exposedRect.left() );
492
dt.setTime( QTime( 0, 0, 0, 0 ) );
493
dt = dt.addDays( 1 - dt.date().day() );
494
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right(); dt = dt.addDays( 1 ),x=d->dateTimeToChartX( dt ) ) {
495
QPen pen = painter->pen();
496
pen.setBrush( QApplication::palette().dark() );
497
if ( dt.date().addMonths( 1 ).month() == 1 && dt.date().addDays( 1 ).day() == 1 ) {
498
pen.setStyle( Qt::SolidLine );
499
} else if ( dt.date().addDays( 1 ).day() == 1 ) {
500
pen.setStyle( Qt::DashLine );
502
pen.setStyle( Qt::NoPen );
504
painter->setPen( pen );
505
paintFreeDay( painter, x, exposedRect, dt.date(), widget );
506
if ( pen.style() != Qt::NoPen ) {
507
//qDebug()<<"paintMonthGrid()"<<dt;
509
painter->drawLine( QPointF( x, exposedRect.top() ), QPointF( x, exposedRect.bottom() ) );
514
void DateTimeGrid::paintYearGrid( QPainter* painter,
515
const QRectF& sceneRect,
516
const QRectF& exposedRect,
517
AbstractRowController* rowController,
520
//qDebug()<<"paintYearGrid()"<<scale()<<dayWidth();
523
Q_UNUSED(rowController)
525
// Paint a dashed line between each quarter and a solid line between the years.
526
QDateTime dt = d->chartXtoDateTime( exposedRect.left() );
527
dt.setTime( QTime( 0, 0, 0, 0 ) );
528
dt = dt.addDays( 1 - dt.date().day() );
529
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right(); dt = dt.addDays( 1 ),x=d->dateTimeToChartX( dt ) ) {
530
QPen pen = painter->pen();
531
pen.setBrush( QApplication::palette().dark() );
532
if ( dt.date().addMonths( 1 ).month() == 1 && dt.date().addDays( 1 ).day() == 1 ) {
533
// Solid line at day 1 of each year.
534
pen.setStyle( Qt::SolidLine );
535
} else if ( dt.date().addMonths( 1 ).month() % 3 == 1 && dt.date().addDays( 1 ).day() == 1 ) {
536
// Dashed line between the quarters
537
pen.setStyle( Qt::DashLine );
539
pen.setStyle( Qt::NoPen );
541
painter->setPen( pen );
542
paintFreeDay( painter, x, exposedRect, dt.date(), widget );
543
if ( pen.style() != Qt::NoPen ) {
544
//qDebug()<<"paintYearGrid()"<<dt;
546
painter->drawLine( QPointF( x, exposedRect.top() ), QPointF( x, exposedRect.bottom() ) );
551
void DateTimeGrid::paintFreeDay( QPainter* painter, qreal x, const QRectF& exposedRect, const QDate &dt, QWidget* widget )
553
if ( d->freeDays.contains( static_cast<Qt::DayOfWeek>( dt.dayOfWeek() ) ) ) {
554
//FIXME We now use same color for alternating rows and free days
555
painter->setBrush( widget ? widget->palette().alternateBase() : QApplication::palette().alternateBase() );
556
painter->fillRect( QRectF( x, exposedRect.top(), dayWidth(), exposedRect.height() ), painter->brush() );
560
void DateTimeGrid::paintRowGrid( QPainter* painter,
561
const QRectF& /*sceneRect*/,
562
const QRectF& exposedRect,
563
AbstractRowController* rowController,
564
QWidget* /*widget*/ )
566
if ( rowController && rowSeparators() ) {
567
// First draw the rows
568
QPen pen = painter->pen();
569
pen.setBrush( QApplication::palette().dark() );
570
pen.setStyle( Qt::DashLine );
571
painter->setPen( pen );
572
QModelIndex idx = rowController->indexAt( qRound( exposedRect.top() ) );
574
while ( y < exposedRect.bottom() && idx.isValid() ) {
575
const Span s = rowController->rowGeometry( idx );
576
y = s.start()+s.length();
577
//painter->drawLine( QPointF( sceneRect.left(), y ), QPointF( sceneRect.right(), y ) );
578
// Is alternating background better?
579
if ( idx.row()%2 ) painter->fillRect( QRectF( exposedRect.x(), s.start(), exposedRect.width(), s.length() ), QApplication::palette().alternateBase() );
580
idx = rowController->indexBelow( idx );
585
void DateTimeGrid::render( QPainter* painter, const QRectF &target, const QRectF& headerRect, const QRectF& exposedRect, QWidget *widget, Qt::AspectRatioMode aspectRatioMode )
589
qreal xratio = target.width() / exposedRect.width();
590
qreal yratio = target.height() / exposedRect.height();
591
//qDebug()<<"QGraphicsScene::render()"<<xratio<<yratio;
592
// Scale according to the aspect ratio mode.
593
switch (aspectRatioMode) {
594
case Qt::KeepAspectRatio:
595
xratio = yratio = qMin(xratio, yratio);
597
case Qt::KeepAspectRatioByExpanding:
598
xratio = yratio = qMax(xratio, yratio);
600
case Qt::IgnoreAspectRatio:
604
//qDebug()<<"DateTimeGrid::render()"<<"target="<<target<<"exposedRect="<<exposedRect<<"xr="<<xratio<<"yr="<<yratio;
606
painter->setClipRect( target );
607
QTransform painterTransform;
608
painterTransform *= QTransform()
609
.translate(target.left(), target.top())
610
.scale(xratio, yratio)
611
.translate(-exposedRect.left(), -exposedRect.top());
612
painter->setWorldTransform(painterTransform, true);
614
paintHeader( painter, headerRect, exposedRect, 0.0, widget );
618
void DateTimeGrid::paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
619
qreal offset, QWidget* widget )
622
case ScaleHour: paintHourScaleHeader(painter,headerRect,exposedRect,offset,widget); break;
623
case ScaleDay: paintDayScaleHeader(painter,headerRect,exposedRect,offset,widget); break;
624
case ScaleWeek: paintWeekScaleHeader(painter,headerRect,exposedRect,offset,widget); break;
626
case ScaleYear: paintMonthScaleHeader(painter,scale(),headerRect,exposedRect,offset,widget); break;
628
Scale autoResult = autoScale();
631
paintHourScaleHeader(painter,headerRect,exposedRect,offset,widget);
634
paintDayScaleHeader(painter,headerRect,exposedRect,offset,widget);
637
paintWeekScaleHeader(painter,headerRect,exposedRect,offset,widget);
641
paintMonthScaleHeader(painter,autoResult,headerRect,exposedRect,offset,widget);
643
case ScaleAuto: // For completeness and remove warning
650
/*! Paints the hour scale header.
653
void DateTimeGrid::paintHourScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
654
qreal offset, QWidget* widget )
656
QStyle* style = widget?widget->style():QApplication::style();
658
// Paint a section for each hour
659
QDateTime dt = d->chartXtoDateTime( offset+exposedRect.left() );
660
dt.setTime( QTime( dt.time().hour(), 0, 0, 0 ) );
661
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right()+offset;
662
dt = dt.addSecs( 60*60 /*1 hour*/ ),x=d->dateTimeToChartX( dt ) ) {
663
QStyleOptionHeader opt;
665
opt.rect = QRectF( x-offset, headerRect.top()+headerRect.height()/2., dayWidth()/24., headerRect.height()/2. ).toRect();
666
opt.text = dt.time().toString( d->hourFormat );
667
opt.textAlignment = Qt::AlignCenter;
668
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
669
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
670
QStyleOptionHeader subopt = opt;
671
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
672
if ( subopt.rect.isValid() ) {
673
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
677
dt = d->chartXtoDateTime( offset+exposedRect.left() );
678
dt.setTime( QTime( 0, 0, 0, 0 ) );
679
// Paint a section for each day
680
for ( qreal x2 = d->dateTimeToChartX( dt ); x2 < exposedRect.right()+offset;
681
dt = dt.addDays( 1 ),x2=d->dateTimeToChartX( dt ) ) {
682
QStyleOptionHeader opt;
684
opt.rect = QRectF( x2-offset, headerRect.top(), dayWidth(), headerRect.height()/2. ).toRect();
685
opt.text = QDate::longDayName( dt.date().dayOfWeek() );
686
opt.textAlignment = Qt::AlignCenter;
687
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
688
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
689
QStyleOptionHeader subopt = opt;
690
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
691
if ( subopt.rect.isValid() ) {
692
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
697
/*! Paints the day scale header.
700
void DateTimeGrid::paintDayScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
701
qreal offset, QWidget* widget )
703
// For starters, support only the regular tab-per-day look
704
QStyle* style = widget?widget->style():QApplication::style();
706
// Paint a section for each day
707
QDateTime dt = d->chartXtoDateTime( offset+exposedRect.left() );
708
dt.setTime( QTime( 0, 0, 0, 0 ) );
709
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right()+offset;
710
dt = dt.addDays( 1 ),x=d->dateTimeToChartX( dt ) ) {
711
QStyleOptionHeader opt;
713
opt.rect = QRectF( x-offset, headerRect.top()+headerRect.height()/2., dayWidth(), headerRect.height()/2. ).toRect();
714
opt.text = dt.toString( QString::fromAscii( "ddd" ) ).left( 1 );
715
opt.textAlignment = Qt::AlignCenter;
716
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
717
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
718
QStyleOptionHeader subopt = opt;
719
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
720
if ( subopt.rect.isValid() ) {
721
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
725
dt = d->chartXtoDateTime( offset+exposedRect.left() );
726
dt.setTime( QTime( 0, 0, 0, 0 ) );
727
// Go backwards until start of week
728
while ( dt.date().dayOfWeek() != d->weekStart ) dt = dt.addDays( -1 );
729
// Paint a section for each week
730
for ( qreal x2 = d->dateTimeToChartX( dt ); x2 < exposedRect.right()+offset;
731
dt = dt.addDays( 7 ),x2=d->dateTimeToChartX( dt ) ) {
732
QStyleOptionHeader opt;
734
opt.rect = QRectF( x2-offset, headerRect.top(), dayWidth()*7., headerRect.height()/2. ).toRect();
735
opt.text = QString::number( dt.date().weekNumber() );
736
opt.textAlignment = Qt::AlignCenter;
737
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
738
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
739
QStyleOptionHeader subopt = opt;
740
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
741
if ( subopt.rect.isValid() ) {
742
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
747
/*! Paints the week scale header.
750
void DateTimeGrid::paintWeekScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
751
qreal offset, QWidget* widget )
753
QStyle* style = widget?widget->style():QApplication::style();
755
// Paint a section for each week
756
QDateTime sdt = d->chartXtoDateTime( offset+exposedRect.left() );
757
sdt.setTime( QTime( 0, 0, 0, 0 ) );
758
// Go backwards until start of week
759
while ( sdt.date().dayOfWeek() != d->weekStart ) sdt = sdt.addDays( -1 );
761
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right()+offset;
762
dt = dt.addDays( 7 ),x=d->dateTimeToChartX( dt ) ) {
763
QStyleOptionHeader opt;
765
opt.rect = QRectF( x-offset, headerRect.top()+headerRect.height()/2., dayWidth()*7, headerRect.height()/2. ).toRect();
766
opt.text = QString::number( dt.date().weekNumber() );
767
opt.textAlignment = Qt::AlignCenter;
768
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
769
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
770
QStyleOptionHeader subopt = opt;
771
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
772
if ( subopt.rect.isValid() ) {
773
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
777
// Paint a section for each month
779
for ( qreal x2 = d->dateTimeToChartX( dt ); x2 < exposedRect.right()+offset; x2=d->dateTimeToChartX( dt ) ) {
780
//qDebug()<<"paintWeekScaleHeader()"<<dt;
781
QDate next = dt.date().addMonths( 1 );
782
next = next.addDays( 1 - next.day() );
784
QStyleOptionHeader opt;
786
opt.rect = QRectF( x2-offset, headerRect.top(), dayWidth()*dt.date().daysTo( next ), headerRect.height()/2. ).toRect();
787
opt.text = QDate::longMonthName( dt.date().month() );
788
opt.textAlignment = Qt::AlignCenter;
789
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
790
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
791
QStyleOptionHeader subopt = opt;
792
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
793
if ( subopt.rect.isValid() ) {
794
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
801
/*! Paints the month scale header.
804
void DateTimeGrid::paintMonthScaleHeader( QPainter* painter, Scale scale,
805
const QRectF& headerRect, const QRectF& exposedRect,
806
qreal offset, QWidget* widget )
808
QStyle* style = widget?widget->style():QApplication::style();
810
// Paint a section for each month
811
QDateTime sdt = d->chartXtoDateTime( offset+exposedRect.left() );
812
sdt.setTime( QTime( 0, 0, 0, 0 ) );
813
sdt = sdt.addDays( 1 - sdt.date().day() );
815
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right()+offset;
816
dt = dt.addMonths( 1 ),x=d->dateTimeToChartX( dt ) ) {
817
QStyleOptionHeader opt;
819
opt.rect = QRectF( x-offset, headerRect.top()+headerRect.height()/2., dayWidth()*dt.date().daysInMonth(), headerRect.height()/2. ).toRect();
820
QString monthName = QDate::shortMonthName( dt.date().month() );
821
if (scale == ScaleYear)
822
opt.text = monthName.left(1);
824
opt.text = monthName;
825
opt.textAlignment = Qt::AlignCenter;
826
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
827
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
828
QStyleOptionHeader subopt = opt;
829
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
830
if ( subopt.rect.isValid() ) {
831
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
835
// Paint a section for each year
837
for ( qreal x2 = d->dateTimeToChartX( dt ); x2 < exposedRect.right()+offset; x2=d->dateTimeToChartX( dt ) ) {
838
//qDebug()<<"paintMonthScaleHeader()"<<dt;
839
QDate next = dt.date().addYears( 1 );
840
next = next.addMonths( 1 - next.month() );
842
QStyleOptionHeader opt;
844
opt.rect = QRectF( x2-offset, headerRect.top(), dayWidth()*dt.date().daysTo( next ), headerRect.height()/2. ).toRect();
845
opt.text = QString::number( dt.date().year() );
846
opt.textAlignment = Qt::AlignCenter;
847
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
848
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
849
QStyleOptionHeader subopt = opt;
850
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
851
if ( subopt.rect.isValid() ) {
852
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
860
/*! Paints the year scale header.
863
void DateTimeGrid::paintYearScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect,
864
qreal offset, QWidget* widget )
867
// FIXME: Improve this with e.g. single letter months
868
paintMonthScaleHeader( painter, headerRect, exposedRect, offset, widget );
870
QStyle* style = widget?widget->style():QApplication::style();
872
// Paint a section for each month
873
QDateTime sdt = d->chartXtoDateTime( offset+exposedRect.left() );
874
sdt.setTime( QTime( 0, 0, 0, 0 ) );
875
sdt = sdt.addDays( 1 - sdt.date().day() );
877
for ( qreal x = d->dateTimeToChartX( dt ); x < exposedRect.right()+offset;
878
dt = dt.addMonths( 1 ),x=d->dateTimeToChartX( dt ) ) {
879
QStyleOptionHeader opt;
881
opt.rect = QRectF( x-offset, headerRect.top()+headerRect.height()/2., dayWidth()*dt.date().daysInMonth(), headerRect.height()/2. ).toRect();
882
opt.text = QDate::shortMonthName( dt.date().month() );
883
opt.textAlignment = Qt::AlignCenter;
884
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
885
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
886
QStyleOptionHeader subopt = opt;
887
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
888
if ( subopt.rect.isValid() ) {
889
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
893
// Paint a section for each year
895
for ( qreal x2 = d->dateTimeToChartX( dt ); x2 < exposedRect.right()+offset; x2=d->dateTimeToChartX( dt ) ) {
896
//qDebug()<<"paintMonthScaleHeader()"<<dt;
897
QDate next = dt.date().addYears( 1 );
898
next = next.addMonths( 1 - next.month() );
900
QStyleOptionHeader opt;
902
opt.rect = QRectF( x2-offset, headerRect.top(), dayWidth()*dt.date().daysTo( next ), headerRect.height()/2. ).toRect();
903
opt.text = QString::number( dt.date().year() );
904
opt.textAlignment = Qt::AlignCenter;
905
// NOTE:CE_Header does not honor clipRegion(), so we do the CE_Header logic here
906
style->drawControl( QStyle::CE_HeaderSection, &opt, painter, 0 ); //NOTE: using widget will loose background when printing
907
QStyleOptionHeader subopt = opt;
908
subopt.rect = style->subElementRect( QStyle::SE_HeaderLabel, &opt, widget );
909
if ( subopt.rect.isValid() ) {
910
style->drawControl( QStyle::CE_HeaderLabel, &subopt, painter, widget );
922
#ifndef KDAB_NO_UNIT_TESTS
924
#include <QStandardItemModel>
925
#include "unittest/test.h"
928
std::ostream& operator<<( std::ostream& os, const QDateTime& dt )
930
os << dt.toString().toStdString();
935
KDAB_SCOPED_UNITTEST_SIMPLE( KDGantt, DateTimeGrid, "test" ) {
936
QStandardItemModel model( 3, 2 );
938
QDateTime dt = QDateTime::currentDateTime();
939
grid.setModel( &model );
940
grid.setStartDateTime( dt.addDays( -10 ) );
942
model.setData( model.index( 0, 0 ), dt, StartTimeRole );
943
model.setData( model.index( 0, 0 ), dt.addDays( 17 ), EndTimeRole );
945
model.setData( model.index( 2, 0 ), dt.addDays( 18 ), StartTimeRole );
946
model.setData( model.index( 2, 0 ), dt.addDays( 19 ), EndTimeRole );
948
Span s = grid.mapToChart( model.index( 0, 0 ) );
949
//qDebug() << "span="<<s;
951
assertTrue( s.start()>0 );
952
assertTrue( s.length()>0 );
954
grid.mapFromChart( s, model.index( 1, 0 ) );
956
QDateTime s1 = model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime();
957
QDateTime e1 = model.data( model.index( 0, 0 ), EndTimeRole ).toDateTime();
958
QDateTime s2 = model.data( model.index( 1, 0 ), StartTimeRole ).toDateTime();
959
QDateTime e2 = model.data( model.index( 1, 0 ), EndTimeRole ).toDateTime();
961
assertTrue( s1.isValid() );
962
assertTrue( e1.isValid() );
963
assertTrue( s2.isValid() );
964
assertTrue( e2.isValid() );
966
assertEqual( s1, s2 );
967
assertEqual( e1, e2 );
969
assertTrue( grid.isSatisfiedConstraint( Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ) );
970
assertFalse( grid.isSatisfiedConstraint( Constraint( model.index( 2, 0 ), model.index( 0, 0 ) ) ) );
972
s = grid.mapToChart( model.index( 0, 0 ) );
973
s.setEnd( s.end()+100000. );
974
bool rc = grid.mapFromChart( s, model.index( 0, 0 ) );
976
assertEqual( s1, model.data( model.index( 0, 0 ), StartTimeRole ).toDateTime() );
977
Span newspan = grid.mapToChart( model.index( 0, 0 ) );
978
assertEqual( newspan.start(), s.start() );
979
assertEqual( newspan.length(), s.length() );
982
QDateTime startDateTime = QDateTime::currentDateTime();
983
qreal dayWidth = 100;
984
QDate currentDate = QDate::currentDate();
985
QDateTime dt( QDate(currentDate.year(), 1, 1), QTime( 0, 0, 0, 0 ) );
986
assert( dt.isValid() );
987
qreal result = startDateTime.date().daysTo(dt.date())*24.*60.*60.;
988
result += startDateTime.time().msecsTo(dt.time())/1000.;
989
result *= dayWidth/( 24.*60.*60. );
991
int days = static_cast<int>( result/dayWidth );
992
qreal secs = result*( 24.*60.*60. )/dayWidth;
993
QDateTime dt2 = startDateTime;
994
QDateTime result2 = dt2.addDays( days ).addSecs( static_cast<int>(secs-(days*24.*60.*60.) ) ).addMSecs( qRound( ( secs-static_cast<int>( secs ) )*1000. ) );
996
assertEqual( dt, result2 );
1000
#endif /* KDAB_NO_UNIT_TESTS */
1002
#include "moc_kdganttdatetimegrid.cpp"