summaryrefslogtreecommitdiffabout
authorzautrix <zautrix>2005-03-18 15:12:58 (UTC)
committer zautrix <zautrix>2005-03-18 15:12:58 (UTC)
commitf9f521c487143641b2cf077d04fe1c475001bce2 (patch) (side-by-side diff)
tree4ffac6d14ceb3e5d77f7634f2b2a49fd91a9e77a
parent66bc0202d4e2306f7029362fe09d0c7ab0e7cc36 (diff)
downloadkdepimpi-f9f521c487143641b2cf077d04fe1c475001bce2.zip
kdepimpi-f9f521c487143641b2cf077d04fe1c475001bce2.tar.gz
kdepimpi-f9f521c487143641b2cf077d04fe1c475001bce2.tar.bz2
rec fix
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--korganizer/komonthview.cpp15
-rw-r--r--libkcal/recurrence.cpp26
2 files changed, 10 insertions, 31 deletions
diff --git a/korganizer/komonthview.cpp b/korganizer/komonthview.cpp
index cb69832..004ff50 100644
--- a/korganizer/komonthview.cpp
+++ b/korganizer/komonthview.cpp
@@ -1,1707 +1,1698 @@
/*
This file is part of KOrganizer.
Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <qpopupmenu.h>
#include <qfont.h>
#include <qfontmetrics.h>
#include <qkeycode.h>
#include <qhbox.h>
#include <qvbox.h>
#include <qwidgetstack.h>
#include <qpushbutton.h>
#include <qtooltip.h>
#include <qpainter.h>
#include <qtimer.h>
#include <qwhatsthis.h>
#ifndef DESKTOP_VERSION
#include <qpe/qpeapplication.h>
#else
#include <qapplication.h>
#endif
#include <kdebug.h>
#include <klocale.h>
#include <kglobal.h>
#include <kconfig.h>
#include <kiconloader.h>
#include <kcalendarsystem.h>
#ifndef KORG_NOPRINTER
#include "calprinter.h"
#endif
#include "koprefs.h"
#ifndef KORG_NOPLUGINS
#include "kocore.h"
#endif
#include "koglobals.h"
#include <libkcal/kincidenceformatter.h>
#include "komonthview.h"
#define PIXMAP_SIZE 5
#ifdef DESKTOP_VERSION
QToolTipGroup *MonthViewCell::mToolTipGroup = 0;
#endif
class KNOWhatsThis :public QWhatsThis
{
public:
KNOWhatsThis( KNoScrollListBox* sbox ) : QWhatsThis( sbox ), _wid( sbox) { };
//~KNOWhatsThis( ) {qDebug("~KNOWhatsThis( ) "); };
protected:
virtual QString text( const QPoint& p)
{
return _wid->getWhatsThisText(p) ;
};
private:
KNoScrollListBox* _wid;
};
KNoScrollListBox::KNoScrollListBox(QWidget *parent,const char *name)
: QListBox(parent, name, WRepaintNoErase)
{
#ifndef DESKTOP_VERSION
QPEApplication::setStylusOperation( viewport(), QPEApplication::RightOnHold );
#endif
mWT = new KNOWhatsThis(this);
}
KNoScrollListBox::~KNoScrollListBox()
{
}
QString KNoScrollListBox::getWhatsThisText(QPoint p)
{
QListBoxItem* item = itemAt ( p );
if ( ! item ) {
return i18n("Click in the cell\nto add an event!");
}
return KIncidenceFormatter::instance()->getFormattedText(((MonthViewItem*) item)->incidence(),
KOPrefs::instance()->mWTshowDetails,
KOPrefs::instance()->mWTshowCreated,
KOPrefs::instance()->mWTshowChanged);
}
void KNoScrollListBox::keyPressEvent(QKeyEvent *e)
{
switch(e->key()) {
case Key_Right:
if ( e->state() == Qt::ControlButton|| e->state() == Qt::ShiftButton )
{
e->ignore();
return;
}
scrollBy(10,0);
break;
case Key_Left:
if (e->state() == Qt::ControlButton|| e->state() == Qt::ShiftButton )
{
e->ignore();
return;
}
scrollBy(-10,0);
break;
case Key_Up:
if( e->state() == Qt::ControlButton|| e->state() == Qt::ShiftButton) {
e->ignore();
break;
}
setCurrentItem((currentItem()+count()-1)%count());
if(!itemVisible(currentItem())) {
if((unsigned int) currentItem() == (count()-1)) {
setTopItem(currentItem()-numItemsVisible()+1);
} else {
setTopItem(topItem()-1);
}
}
break;
case Key_Down:
if(e->state() == Qt::ControlButton|| e->state() == Qt::ShiftButton) {
e->ignore();
break;
}
setCurrentItem((currentItem()+1)%count());
if(!itemVisible(currentItem())) {
if(currentItem() == 0) {
setTopItem(0);
} else {
setTopItem(topItem()+1);
}
}
break;
case Key_I:
QTimer::singleShot( 11, this, SLOT ( oneDown() ) );
e->ignore();
break;
case Key_Shift:
emit shiftDown();
break;
default:
e->ignore();
break;
}
}
void KNoScrollListBox::oneDown()
{
setCurrentItem((currentItem()+1)%count());
if(!itemVisible(currentItem())) {
if(currentItem() == 0) {
setTopItem(0);
} else {
setTopItem(topItem()+1);
}
}
}
void KNoScrollListBox::keyReleaseEvent(QKeyEvent *e)
{
switch(e->key()) {
case Key_Shift:
emit shiftUp();
break;
default:
break;
}
}
void KNoScrollListBox::mousePressEvent(QMouseEvent *e)
{
QListBox::mousePressEvent(e);
if(e->button() == RightButton) {
emit rightClick();
}
}
MonthViewItem::MonthViewItem( Incidence *incidence, QDate qd, const QString & s)
: QListBoxItem()
{
setText( s );
mMultiday = 0;
mIncidence = incidence;
mDate = qd;
mRecur = false;
mAlarm = false;
mReply = false;
mInfo = false;
}
void MonthViewItem::paint(QPainter *p)
{
#if QT_VERSION >= 0x030000
bool sel = isSelected();
#else
bool sel = selected();
#endif
if (KOPrefs::instance()->mMonthViewUsesCategoryColor)
{
p->setBackgroundColor( palette().color( QPalette::Normal, \
sel ? QColorGroup::Highlight : QColorGroup::Background ) );
p->eraseRect( 0, 0, listBox()->maxItemWidth(), height( listBox() ) );
}
int x = 1;
//int y = 3;//(height() - mRecurPixmap.height()) /2;
int size = PIXMAP_SIZE;
if ( QApplication::desktop()->width() < 300 )
size = 3;
int heihei = height( listBox () );
int y = (heihei - size -1 ) /2;
if ( KOPrefs::instance()->mMonthShowIcons ) {
if ( mInfo ) {
p->fillRect ( x, y,size,size, Qt::darkGreen );
x += size + 1;
}
if ( mRecur ) {
p->fillRect ( x, y,size,size, Qt::blue );
x += size + 1;
}
if ( mAlarm ) {
p->fillRect ( x, y,size,size, Qt::red );
x += size + 1;
}
if ( mReply ) {
p->fillRect ( x, y,size,size, Qt::yellow );
x += size + 1;
}
}
if ( mMultiday ) {
int yyy = y+(size/2);
int sizeM = size+2;
p->setBrush( QBrush::SolidPattern );
p->drawLine ( x+1, yyy, x +sizeM +sizeM/2-1, yyy ) ;
if ( mMultiday == 2 || mMultiday == 3 ) {
QPointArray pa ( 3 );
pa.setPoint (0, x, yyy );
pa.setPoint (1, x+sizeM/2, yyy+sizeM/2 );
pa.setPoint (2, x+sizeM/2, yyy-sizeM/2 );
p->drawPolygon( pa );
}
if ( mMultiday == 2 || mMultiday == 1 ) {
QPointArray pa ( 3 );
pa.setPoint (0, x+sizeM +sizeM/2, yyy );
pa.setPoint (1, x+sizeM, yyy+sizeM/2 );
pa.setPoint (2, x+sizeM, yyy-sizeM/2 );
p->drawPolygon( pa );
}
if ( mMultiday == 1 ) {
// p->fillRect ( x, yyy-sizeM/2+1, sizeM/2, size, QBrush ( QBrush::SolidPattern ) );
p->drawLine ( x+1, yyy-sizeM/2, x+1, yyy+sizeM/2 );
}
if ( mMultiday == 3 ) {
// p->fillRect ( x+sizeM, yyy-sizeM/2+1, sizeM/2, size, QBrush ( QBrush::SolidPattern ) );
p->drawLine ( x+sizeM +sizeM/2-1, yyy-sizeM/2, x+sizeM +sizeM/2-1, yyy+sizeM/2 );
}
x += sizeM/2 + 1;
x += sizeM + 1;
}
if ( mIncidence->type() == "Todo" ){
Todo* td = ( Todo* ) mIncidence;
if ( td->isCompleted() ) {
int half = size/2;
p->drawLine ( x, heihei/2, x +half , heihei/2 +half ) ;
p->drawLine ( x +half , heihei/2 +half , x +half+half +2 , heihei/2 -2 ) ;
x += half+half + 4;
} else {
int val = td->percentComplete()/20;
p->fillRect ( x+1, y-1, val ,size+2,Qt::black );
p->drawRect ( x, y-1,7,size+2);
x += size + 3;
}
}
QFontMetrics fm = p->fontMetrics();
int yPos;
int pmheight = size;
if( pmheight < fm.height() )
yPos = fm.ascent() + fm.leading()/2;
else
yPos = pmheight/2 - fm.height()/2 + fm.ascent();
p->setPen( palette().color( QPalette::Normal, sel ? \
QColorGroup::HighlightedText : QColorGroup::Foreground ) );
p->drawText( x, yPos, text() );
if ( mIncidence->cancelled() ) {
int wid = fm.width( text() );
p->drawLine( x, heihei/2 ,x+wid, heihei/2 );
}
}
int MonthViewItem::height(const QListBox *lb) const
{
if ( lb )
return lb->fontMetrics().lineSpacing()+1;
return 10;
}
int MonthViewItem::width(const QListBox *lb) const
{
int size = PIXMAP_SIZE;
if ( QApplication::desktop()->width() < 300 )
size = 3;
int x = 1;
if ( KOPrefs::instance()->mMonthShowIcons ) {
if ( mInfo ) {
x += size + 1;
}
if( mRecur ) {
x += size+1;
}
if( mAlarm ) {
x += size+1;
}
if( mReply ) {
x += size+1;
}
}
if( mMultiday ) {
x += size+1+2+size/2;
}
return( x + lb->fontMetrics().width( text() ) + 1 );
}
MonthViewCell::MonthViewCell( KOMonthView *parent,QWidget* par )
: QWidget( par ),
mMonthView( parent )
{
QVBoxLayout *topLayout = new QVBoxLayout( this );
// mLabel = new QLabel( this );QPushButton
mLabel = new QPushButton( this );
//mLabel->setFrameStyle( QFrame::Panel | QFrame::Plain );
//mLabel->setLineWidth( 1 );
//mLabel->setAlignment( AlignCenter );
mLabel->setFlat( true );
mLabel->setFocusPolicy(NoFocus);
mItemList = new KNoScrollListBox( this );
mItemList->setMinimumSize( 10, 10 );
mItemList->setFrameStyle( QFrame::Panel | QFrame::Plain );
mItemList->setLineWidth( 1 );
topLayout->addWidget( mItemList );
mLabel->raise();
// QColor( 0,0,255 ) QColor( 160,1600,255 )
mStandardPalette = palette();
mStandardPalette.setColor(QColorGroup::Base, mStandardPalette.color( QPalette::Normal, QColorGroup::Background ) );
enableScrollBars( false );
updateConfig();
//connect( mLabel, SIGNAL( clicked( )), SLOT( newEvent() ));
connect( mLabel, SIGNAL( clicked( )), SLOT( showDay() ));
connect( mItemList, SIGNAL( doubleClicked( QListBoxItem *) ),
SLOT( defaultAction( QListBoxItem * ) ) );
connect( mItemList, SIGNAL( rightButtonPressed( QListBoxItem *,
const QPoint &) ),
SLOT( contextMenu( QListBoxItem * ) ) );
connect( mItemList, SIGNAL( highlighted( QListBoxItem *) ),
SLOT( selection( QListBoxItem * ) ) );
connect( mItemList, SIGNAL( clicked( QListBoxItem * ) ),
SLOT( cellClicked( QListBoxItem * ) ) );
connect( mItemList, SIGNAL( clicked( QListBoxItem * ) ),
SLOT( selection( QListBoxItem * ) ) );
}
#ifdef DESKTOP_VERSION
QToolTipGroup *MonthViewCell::toolTipGroup()
{
if (!mToolTipGroup) mToolTipGroup = new QToolTipGroup(0);
return mToolTipGroup;
}
#endif
void MonthViewCell::setDate( const QDate &date )
{
// kdDebug() << "MonthViewCell::setDate(): " << date.toString() << endl;
mDate = date;
//resizeEvent( 0 );
}
QDate MonthViewCell::date() const
{
return mDate;
}
void MonthViewCell::setPrimary( bool primary )
{
mPrimary = primary;
//setMyPalette();
}
void MonthViewCell::setMyPalette()
{
if ( mHoliday) {
setPalette( mHolidayPalette );
} else {
if ( mPrimary ) {
setPalette( mPrimaryPalette );
} else {
setPalette( mNonPrimaryPalette );
}
}
QPalette pal = palette();
mLabel->setPalette( QPalette ( pal.color( QPalette::Normal,QColorGroup::Base),pal.color(QPalette::Normal,QColorGroup::Base ) ));
}
QPalette MonthViewCell::getPalette ()
{
if ( !KOPrefs::instance()->mMonthViewUsesDayColors )
return mStandardPalette;
if ( mHoliday) {
return mHolidayPalette ;
} else {
if ( mPrimary ) {
return mPrimaryPalette ;
}
}
return mNonPrimaryPalette;
}
bool MonthViewCell::isPrimary() const
{
return mPrimary;
}
void MonthViewCell::setHoliday( bool holiday )
{
mHoliday = holiday;
//setMyPalette();
}
void MonthViewCell::setHoliday( const QString &holiday )
{
mHolidayString = holiday;
if ( !holiday.isEmpty() ) {
setHoliday( true );
}
}
void MonthViewCell::keyPressEvent ( QKeyEvent * e )
{
e->ignore();
}
void MonthViewCell::startUpdateCell()
{
mItemList->setFocusPolicy(NoFocus);
if ( !mMonthView->isUpdatePossible() )
return;
/*
if ( !isVisible() ){
return;
}
*/
// qDebug("MonthViewCell::updateCell() ");
setPrimary( mDate.month()%2 );
setHoliday( KOGlobals::self()->calendarSystem()->dayOfWeek(mDate) == KOGlobals::self()->calendarSystem()->weekDayOfPray() || ( mDate.dayOfWeek() == 6 ) && KOPrefs::instance()-> mExcludeSaturdays);
if ( mDate == QDate::currentDate() ) {
mItemList->setLineWidth( 3 );
} else {
mItemList->setLineWidth( 1 );
}
mItemList->clear();
#ifdef DESKTOP_VERSION
QToolTip::remove(this);
#endif
mToolTip.clear();
//qApp->processEvents();
if ( !mHolidayString.isEmpty() ) {
MonthViewItem *item = new MonthViewItem( 0, mDate, mHolidayString );
item->setPalette( mHolidayPalette );
mItemList->insertItem( item );
mToolTip.append ( mHolidayString );
}
}
void MonthViewCell::insertEvent(Event *event)
{
QString mToolTipText;
mItemList->setFocusPolicy(WheelFocus);
if ( !(event->doesRecur() == Recurrence::rNone) ) {
if ( !KOPrefs::instance()->mMonthDailyRecur && event->doesRecur() == Recurrence::rDaily )
return;
else
if ( !KOPrefs::instance()->mMonthWeeklyRecur && event->doesRecur() == Recurrence::rWeekly )
return;
}
if ( event->categories().contains("Holiday") ||
event->categories().contains(i18n("Holiday"))) {
setHoliday( true );
if ( mDate.dayOfWeek() == 7 )
mItemList->setLineWidth( 3 );
}
QString text;
int multiday = 0;// 1 = start, 2 = midddle, 3 = end day
if (event->isMultiDay()) {
QString prefix = "<->";multiday = 2;
QString time;
if ( event->doesRecur() ) {
if ( event->recursOn( mDate) ) {
prefix ="->" ;multiday = 1;
}
else {
int days = event->dtStart().date().daysTo ( event->dtEnd().date() );
if ( event->recursOn( mDate.addDays( -days)) ) {
prefix ="<-" ;multiday = 3;
}
}
} else {
if (mDate == event->dtStart().date()) {
prefix ="->" ;multiday = 1;
} else if (mDate == event->dtEnd().date()) {
prefix ="<-" ;multiday = 3;
}
}
if ( !event->doesFloat() ) {
if ( mDate == event->dtStart().date () )
time = KGlobal::locale()->formatTime(event->dtStart().time())+" ";
else if ( mDate == event->dtEnd().date () )
time = KGlobal::locale()->formatTime(event->dtEnd().time())+" ";
}
text = time + event->summary();
mToolTipText += prefix + text;
} else {
if (event->doesFloat()) {
text = event->summary();
mToolTipText += text;
}
else {
text = KGlobal::locale()->formatTime(event->dtStart().time());
text += " " + event->summary();
mToolTipText += KGlobal::locale()->formatTime(event->dtStart().time()) +"-"+KGlobal::locale()->formatTime(event->dtEnd().time())+" " + event->summary();
}
}
MonthViewItem *item = new MonthViewItem( event, mDate, text );
QPalette pal;
if (KOPrefs::instance()->mMonthViewUsesCategoryColor) {
QStringList categories = event->categories();
QString cat = categories.first();
if ( KOPrefs::instance()->mMonthViewUsesForegroundColor ) {
pal = getPalette();
if (cat.isEmpty()) {
pal.setColor(QColorGroup::Foreground,KOPrefs::instance()->mEventColor);
} else {
pal.setColor(QColorGroup::Foreground, *(KOPrefs::instance()->categoryColor(cat)));
}
} else {
if (cat.isEmpty()) {
pal = QPalette(KOPrefs::instance()->mEventColor, KOPrefs::instance()->mEventColor);
} else {
pal = QPalette(*(KOPrefs::instance()->categoryColor(cat)), *(KOPrefs::instance()->categoryColor(cat)));
}
}
} else {
pal = mStandardPalette ;
}
item->setPalette( pal );
item->setRecur( event->recurrence()->doesRecur() );
item->setAlarm( event->isAlarmEnabled() && multiday < 2 );
item->setMoreInfo( event->description().length() > 0 );
#ifdef DESKTOP_VERSION
Attendee *me = event->attendeeByMails(KOPrefs::instance()->mAdditionalMails,
KOPrefs::instance()->email());
if ( me != 0 ) {
if ( me->status() == Attendee::NeedsAction && me->RSVP())
item->setReply(true && multiday < 2);
else
item->setReply(false);
} else
item->setReply(false);
#endif
item->setMultiDay( multiday );
mItemList->insertItem( item );
mToolTip.append( mToolTipText );
}
void MonthViewCell::insertTodo(Todo *todo)
{
mItemList->setFocusPolicy(WheelFocus);
QString text;
if (todo->hasDueDate()) {
if (!todo->doesFloat()) {
text += KGlobal::locale()->formatTime(todo->dtDue().time());
text += " ";
}
}
text += todo->summary();
MonthViewItem *item = new MonthViewItem( todo, mDate, text );
//item->setPalette( mStandardPalette );
QPalette pal;
if (KOPrefs::instance()->mMonthViewUsesCategoryColor) {
QStringList categories = todo->categories();
QString cat = categories.first();
if ( KOPrefs::instance()->mMonthViewUsesForegroundColor ) {
pal = getPalette();
if (cat.isEmpty()) {
pal.setColor(QColorGroup::Foreground,KOPrefs::instance()->mEventColor);
} else {
pal.setColor(QColorGroup::Foreground, *(KOPrefs::instance()->categoryColor(cat)));
}
} else {
if (cat.isEmpty()) {
pal = QPalette(KOPrefs::instance()->mEventColor, KOPrefs::instance()->mEventColor);
} else {
pal = QPalette(*(KOPrefs::instance()->categoryColor(cat)), *(KOPrefs::instance()->categoryColor(cat)));
}
}
} else {
pal = mStandardPalette ;
}
item->setPalette( pal );
item->setRecur( todo->recurrence()->doesRecur() );
item->setAlarm( todo->isAlarmEnabled() );
item->setMoreInfo( todo->description().length() > 0 );
mItemList->insertItem( item );
mToolTip.append( text );
}
void MonthViewCell::finishUpdateCell()
{
#ifdef DESKTOP_VERSION
if (mToolTip.count() > 0 ) {
mToolTip.sort();
QToolTip::add(this,mToolTip.join("\n"),toolTipGroup(),"");
}
#endif
mItemList->sort();
//setMyPalette();
setMyPalette();
QString text;
bool smallDisplay = QApplication::desktop()->width() < 320 && KOPrefs::instance()->mMonthViewSatSunTog;
if ( KOPrefs::instance()->mMonthViewWeek || KOGlobals::self()->calendarSystem()->day( mDate ) == 1 || (mDate.dayOfWeek() == 7 && !smallDisplay ) || KOPrefs::instance()->mMonthShowShort) {
text = KOGlobals::self()->calendarSystem()->monthName( mDate, true ) + " ";
mLabel->resize( mLabelBigSize );
text += QString::number( KOGlobals::self()->calendarSystem()->day(mDate) );
} else {
mLabel->resize( mLabelSize );
text += QString::number( KOGlobals::self()->calendarSystem()->day(mDate) );
}
mLabel->setText( text );
resizeEvent( 0 );
}
void MonthViewCell::updateCell()
{
//qDebug("MonthViewCell::updateCell() ");
if ( !mMonthView->isUpdatePossible() )
return;
startUpdateCell();
//mLabel->setMaximumWidth( width() - mItemList->lineWidth()*2);
QPtrList<Event> events = mMonthView->calendar()->events( mDate, true );
Event *event;
for( event = events.first(); event; event = events.next() ) { // for event
insertEvent(event);
}
// insert due todos
QPtrList<Todo> todos = mMonthView->calendar()->todos( mDate );
Todo *todo;
for(todo = todos.first(); todo; todo = todos.next()) {
insertTodo( todo );
}
finishUpdateCell();
// if ( isVisible())
//qApp->processEvents();
}
void MonthViewCell::updateConfig( bool bigFont ) // = false
{
if ( bigFont ) {
QFont fo = KOPrefs::instance()->mMonthViewFont;
int ps = fo.pointSize() + 2;
if ( ps < 18 )
ps += 2;
fo.setPointSize( ps );
setFont( fo );
} else
setFont( KOPrefs::instance()->mMonthViewFont );
QFontMetrics fm( font() );
mLabelSize = fm.size( 0, "30" ) + QSize( 4, 2 );
mLabelBigSize = fm.size( 0, "Mag 30" ) + QSize( 2, 2 );
mHolidayPalette = mStandardPalette;
mPrimaryPalette = mStandardPalette;
mNonPrimaryPalette = mStandardPalette;
if ( KOPrefs::instance()->mMonthViewUsesDayColors ) {
mHolidayPalette.setColor(QColorGroup::Base, KOPrefs::instance()->mMonthViewHolidayColor );
mHolidayPalette.setColor(QColorGroup::Background, KOPrefs::instance()->mMonthViewHolidayColor );
mHolidayPalette.setColor(QColorGroup::Foreground, KOPrefs::instance()->mMonthViewHolidayColor.dark());
mPrimaryPalette.setColor(QColorGroup::Foreground,KOPrefs::instance()->mMonthViewOddColor.dark());
mPrimaryPalette.setColor(QColorGroup::Base,KOPrefs::instance()->mMonthViewOddColor);
mPrimaryPalette.setColor(QColorGroup::Background,KOPrefs::instance()->mMonthViewOddColor);
mNonPrimaryPalette.setColor(QColorGroup::Foreground,KOPrefs::instance()->mMonthViewEvenColor.dark());
mNonPrimaryPalette.setColor(QColorGroup::Base,KOPrefs::instance()->mMonthViewEvenColor);
mNonPrimaryPalette.setColor(QColorGroup::Background,KOPrefs::instance()->mMonthViewEvenColor);
}
//updateCell();
}
void MonthViewCell::enableScrollBars( bool enabled )
{
if ( enabled ) {
mItemList->setVScrollBarMode(QScrollView::Auto);
mItemList->setHScrollBarMode(QScrollView::Auto);
} else {
mItemList->setVScrollBarMode(QScrollView::AlwaysOff);
mItemList->setHScrollBarMode(QScrollView::AlwaysOff);
}
}
Incidence *MonthViewCell::selectedIncidence()
{
int index = mItemList->currentItem();
if ( index < 0 ) return 0;
MonthViewItem *item =
static_cast<MonthViewItem *>( mItemList->item( index ) );
if ( !item ) return 0;
return item->incidence();
}
QDate MonthViewCell::selectedIncidenceDate()
{
QDate qd;
int index = mItemList->currentItem();
if ( index < 0 ) return qd;
MonthViewItem *item =
static_cast<MonthViewItem *>( mItemList->item( index ) );
if ( !item ) return qd;
return item->incidenceDate();
}
void MonthViewCell::deselect()
{
mItemList->clearSelection();
enableScrollBars( false );
// updateCell();
}
void MonthViewCell::select()
{
;// updateCell();
}
void MonthViewCell::resizeEvent ( QResizeEvent * )
{
if ( !mMonthView->isUpdatePossible() )
return;
int size = height() - mLabel->height() - 2;
if ( size > 0 )
mItemList->verticalScrollBar()->setMaximumHeight( size );
size = width() - mLabel->width() -2;
if ( size > 0 )
mItemList->horizontalScrollBar()->setMaximumWidth( size );
mLabel->move( width()-mItemList->lineWidth() - mLabel->width(), height()-mItemList->lineWidth() - mLabel->height() );
//mLabel->setMaximumWidth( width() - mItemList->lineWidth()*2);
}
void MonthViewCell::defaultAction( QListBoxItem *item )
{
if ( !item ) return;
MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
Incidence *incidence = eventItem->incidence();
if ( incidence ) mMonthView->defaultAction( incidence );
}
void MonthViewCell::showDay()
{
emit showDaySignal( date() );
}
void MonthViewCell::newEvent()
{
QDateTime dt( date(), QTime( KOPrefs::instance()->mStartTime, 0 ) );
emit newEventSignal( dt );
}
void MonthViewCell::cellClicked( QListBoxItem *item )
{
static QListBoxItem * lastClicked = 0;
if ( item == 0 ) {
QDateTime dt( date(), QTime( KOPrefs::instance()->mStartTime, 0 ) );
emit newEventSignal( dt );
return;
}
/*
if ( lastClicked )
if ( ! item ) {
if ( lastClicked->listBox() != item->listBox() )
lastClicked->listBox()->clearSelection();
}
*/
mMonthView->setSelectedCell( this );
if( KOPrefs::instance()->mEnableMonthScroll || KOPrefs::instance()->mMonthViewWeek ) enableScrollBars( true );
select();
}
void MonthViewCell::contextMenu( QListBoxItem *item )
{
if ( !item ) return;
MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
Incidence *incidence = eventItem->incidence();
if ( incidence ) mMonthView->showContextMenu( incidence );
}
void MonthViewCell::selection( QListBoxItem *item )
{
if ( !item ) return;
mMonthView->setSelectedCell( this );
}
// *******************************************************************************
// *******************************************************************************
// *******************************************************************************
KOMonthView::KOMonthView(Calendar *calendar, QWidget *parent, const char *name)
: KOEventView( calendar, parent, name ),
mDaysPerWeek( 7 ), mNumWeeks( 6 ), mNumCells( mDaysPerWeek * mNumWeeks ),
mWidthLongDayLabel( 0 ), mSelectedCell( 0 )
{
mShortDayLabelsM = false;
mShortDayLabelsW = false;
skipResize = false;
clPending = true;
mNavigatorBar = new NavigatorBar( QDate::currentDate(), this, "useBigPixmaps" );
mWidStack = new QWidgetStack( this );
QVBoxLayout* hb = new QVBoxLayout( this );
mMonthView = new QWidget( mWidStack );
mWeekView = new QWidget( mWidStack );
#if QT_VERSION >= 0x030000
mWidStack->addWidget(mMonthView );
mWidStack->addWidget(mWeekView );
#else
mWidStack->addWidget( mMonthView, 1 );
mWidStack->addWidget( mWeekView , 1 );
#endif
hb->addWidget( mNavigatorBar );
hb->addWidget( mWidStack );
mWeekStartsMonday = KGlobal::locale()->weekStartsMonday();
mShowWeekView = KOPrefs::instance()->mMonthViewWeek;
if ( mShowWeekView )
mWeekStartsMonday = true;
updatePossible = false;
//updatePossible = true;
mCells.setAutoDelete( true );
mShowSatSunComp = KOPrefs::instance()->mMonthViewSatSunTog ;
mDayLabels.resize( mDaysPerWeek );
mDayLabelsW.resize( mDaysPerWeek );
QFont bfont = font();
if ( QApplication::desktop()->width() < 650 ) {
bfont.setPointSize( bfont.pointSize() - 2 );
}
bfont.setBold( true );
int i;
for( i = 0; i < mDaysPerWeek; i++ ) {
QLabel *label = new QLabel( mMonthView );
label->setFont(bfont);
label->setFrameStyle(QFrame::Panel|QFrame::Raised);
label->setLineWidth(1);
label->setAlignment(AlignCenter);
mDayLabels.insert( i, label );
label = new QLabel( mWeekView );
label->setFont(bfont);
label->setFrameStyle(QFrame::Panel|QFrame::Raised);
label->setLineWidth(1);
label->setAlignment(AlignCenter);
mDayLabelsW.insert( i, label );
}
bfont.setBold( false );
mWeekLabels.resize( mNumWeeks+1 );
mWeekLabelsW.resize( 2 );
for( i = 0; i < mNumWeeks+1; i++ ) {
KOWeekButton *label = new KOWeekButton( mMonthView );
label->setFocusPolicy(NoFocus);
label->setFont(bfont);
connect( label, SIGNAL( selectWeekNum ( int )),this, SLOT( selectInternalWeekNum ( int )) );
label->setFlat(true);
QWhatsThis::add(label,i18n("Click on the week number to\nshow week zoomed"));
//label->setFrameStyle(QFrame::Panel|QFrame::Raised);
//label->setLineWidth(1);
//label->setAlignment(AlignCenter);
mWeekLabels.insert( i, label );
}
mWeekLabels[mNumWeeks]->setText( i18n("W"));
QWhatsThis::add(mWeekLabels[mNumWeeks],i18n("Click on this to\nselect week number"));
for( i = 0; i < 1+1; i++ ) {
KOWeekButton *label = new KOWeekButton( mWeekView );
label->setFocusPolicy(NoFocus);
label->setFont(bfont);
connect( label, SIGNAL( selectWeekNum ( int )),this, SLOT( selectInternalWeekNum ( int )) );
label->setFlat(true);
QWhatsThis::add(label,i18n("Click on the week number to\nshow week zoomed"));
//label->setFrameStyle(QFrame::Panel|QFrame::Raised);
//label->setLineWidth(1);
//label->setAlignment(AlignCenter);
mWeekLabelsW.insert( i, label );
}
mWeekLabelsW[1]->setText( i18n("W"));
int row, col;
mCells.resize( mNumCells );
for( row = 0; row < mNumWeeks; ++row ) {
for( col = 0; col < mDaysPerWeek; ++col ) {
MonthViewCell *cell = new MonthViewCell( this, mMonthView );
mCells.insert( row * mDaysPerWeek + col, cell );
connect( cell, SIGNAL( defaultAction( Incidence * ) ),
SLOT( defaultAction( Incidence * ) ) );
connect( cell, SIGNAL( newEventSignal( QDateTime ) ),
SIGNAL( newEventSignal( QDateTime ) ) );
connect( cell, SIGNAL( showDaySignal( QDate ) ),
SIGNAL( showDaySignal( QDate ) ) );
}
}
mCellsW.resize( mDaysPerWeek );
for( col = 0; col < mDaysPerWeek; ++col ) {
MonthViewCell *cell = new MonthViewCell( this, mWeekView );
mCellsW.insert( col, cell );
connect( cell, SIGNAL( defaultAction( Incidence * ) ),
SLOT( defaultAction( Incidence * ) ) );
connect( cell, SIGNAL( newEventSignal( QDateTime ) ),
SIGNAL( newEventSignal( QDateTime ) ) );
connect( cell, SIGNAL( showDaySignal( QDate ) ),
SIGNAL( showDaySignal( QDate ) ) );
cell->updateConfig(KOPrefs::instance()->mMonthViewUsesBigFont );
}
//connect( mWeekLabels[mNumWeeks], SIGNAL( clicked() ), SLOT( switchView() ) );
mContextMenu = eventPopup();
// updateConfig(); //useless here...
// ... but we need mWidthLongDayLabel computed
QFontMetrics fontmetric(mDayLabels[0]->font());
mWidthLongDayLabel = 0;
for (int i = 0; i < 7; i++) {
int width = fontmetric.width(KOGlobals::self()->calendarSystem()->weekDayName(i+1));
if ( width > mWidthLongDayLabel ) mWidthLongDayLabel = width;
}
//mWeekLabels[mNumWeeks]->setText( i18n("W"));
#if 0
if ( mShowWeekView )
mWidStack->raiseWidget( mWeekView );
else
mWidStack->raiseWidget( mMonthView );
#endif
emit incidenceSelected( 0 );
#ifndef DESKTOP_VERSION
resize( QApplication::desktop()->size() );
#else
resize(640, 480 );
updatePossible = true;
#endif
computeLayout();
if ( mShowWeekView )
mWidStack->raiseWidget( mWeekView );
else
mWidStack->raiseWidget( mMonthView );
}
KOMonthView::~KOMonthView()
{
delete mContextMenu;
}
void KOMonthView::selectInternalWeekNum ( int n )
{
switchView();
if ( !KOPrefs::instance()->mMonthViewWeek )
emit selectMonth ();
else
emit selectWeekNum ( n );
}
int KOMonthView::currentWeek()
{
if ( mShowWeekView )
return mWeekLabelsW[0]->getWeekNum();
return mWeekLabels[0]->getWeekNum();
}
void KOMonthView::switchView()
{
if ( selectedCell( ) )
selectedCell()->deselect();
mShowWeekView = !mShowWeekView;
KOPrefs::instance()->mMonthViewWeek = mShowWeekView;
if ( clPending ) {
computeLayout();
updateConfig();
}
if ( mShowWeekView )
mWidStack->raiseWidget( mWeekView );
else
mWidStack->raiseWidget( mMonthView );
clPending = false;
}
int KOMonthView::maxDatesHint()
{
return mNumCells;
}
int KOMonthView::currentDateCount()
{
return mNumCells;
}
QPtrList<Incidence> KOMonthView::selectedIncidences()
{
QPtrList<Incidence> selected;
if ( mSelectedCell ) {
Incidence *incidence = mSelectedCell->selectedIncidence();
if ( incidence ) selected.append( incidence );
}
return selected;
}
DateList KOMonthView::selectedDates()
{
DateList selected;
if ( mSelectedCell ) {
QDate qd = mSelectedCell->selectedIncidenceDate();
if ( qd.isValid() ) selected.append( qd );
}
return selected;
}
void KOMonthView::printPreview(CalPrinter *calPrinter, const QDate &fd,
const QDate &td)
{
#ifndef KORG_NOPRINTER
calPrinter->preview(CalPrinter::Month, fd, td);
#endif
}
void KOMonthView::updateConfig()
{
mWeekStartsMonday = KGlobal::locale()->weekStartsMonday();
if ( mShowWeekView ) {
mWeekStartsMonday = true;
}
QFontMetrics fontmetric(mDayLabels[0]->font());
mWidthLongDayLabel = 0;
for (int i = 0; i < 7; i++) {
int width = fontmetric.width(KOGlobals::self()->calendarSystem()->weekDayName(i+1));
if ( width > mWidthLongDayLabel ) mWidthLongDayLabel = width;
}
bool temp = mShowSatSunComp ;
mShowSatSunComp = KOPrefs::instance()->mMonthViewSatSunTog ;
if ( ! mShowWeekView ) {
if ( temp != KOPrefs::instance()->mMonthViewSatSunTog )
computeLayout();
}
updateDayLabels();
//qDebug("KOMonthView::updateConfig() %d %d %d ",height(), mDayLabels[0]->sizeHint().height() ,mNumWeeks);
//int cellHeight = (height() - mDayLabels[0]->sizeHint().height()) /mNumWeeks;
//resizeEvent( 0 );
for (uint i = 0; i < mCells.count(); ++i) {
mCells[i]->updateConfig();
}
for (uint i = 0; i < mCellsW.count(); ++i) {
mCellsW[i]->updateConfig(KOPrefs::instance()->mMonthViewUsesBigFont);
}
#ifdef DESKTOP_VERSION
MonthViewCell::toolTipGroup()->setEnabled(KOPrefs::instance()->mEnableToolTips);
#endif
updateView();
}
void KOMonthView::updateDayLabels()
{
QPtrVector<QLabel> *mDayLabelsT;
mDayLabelsT = &mDayLabelsW;
for (int i = 0; i < 7; i++) {
if (mWeekStartsMonday) {
bool show = mShortDayLabelsW;
if ( i > 4 && mShowSatSunComp && mWidthLongDayLabel > (*mDayLabelsT)[i]->width() )
show = true;
(*mDayLabelsT)[i]->setText(KOGlobals::self()->calendarSystem()->weekDayName(i+1,show));
} else {
if (i==0) (*mDayLabelsT)[i]->setText(KOGlobals::self()->calendarSystem()->weekDayName(7,mShortDayLabelsW));
else (*mDayLabelsT)[i]->setText(KOGlobals::self()->calendarSystem()->weekDayName(i,mShortDayLabelsW));
}
}
mDayLabelsT = &mDayLabels;
for (int i = 0; i < 7; i++) {
if (mWeekStartsMonday) {
bool show = mShortDayLabelsM;
if ( i > 4 && mShowSatSunComp && mWidthLongDayLabel > (*mDayLabelsT)[i]->width() )
show = true;
(*mDayLabelsT)[i]->setText(KOGlobals::self()->calendarSystem()->weekDayName(i+1,show));
} else {
if (i==0) (*mDayLabelsT)[i]->setText(KOGlobals::self()->calendarSystem()->weekDayName(7,mShortDayLabelsM));
else (*mDayLabelsT)[i]->setText(KOGlobals::self()->calendarSystem()->weekDayName(i,mShortDayLabelsM));
}
}
}
void KOMonthView::showDates(const QDate &start, const QDate &)
{
// kdDebug() << "KOMonthView::showDates(): " << start.toString() << endl;
QPtrVector<MonthViewCell> *cells;
QPtrVector<QLabel> *dayLabels;
QPtrVector<KOWeekButton> *weekLabels;
int weekNum = 6;
if ( mShowWeekView ) {
weekNum = 1;
cells = &mCellsW;
dayLabels = &mDayLabelsW;
weekLabels = &mWeekLabelsW;
} else {
cells = &mCells;
dayLabels = &mDayLabels;
weekLabels = &mWeekLabels;
}
mStartDate = start;
int startWeekDay = mWeekStartsMonday ? 1 : 7;
while( KOGlobals::self()->calendarSystem()->dayOfWeek(mStartDate) != startWeekDay ) {
mStartDate = mStartDate.addDays( -1 );
}
bool primary = false;
uint i;
for( i = 0; i < (*cells).size(); ++i ) {
QDate date = mStartDate.addDays( i );
(*cells)[i]->setDate( date );
#ifndef KORG_NOPLUGINS
// add holiday, if present
QString hstring(KOCore::self()->holiday(date));
(*cells)[i]->setHoliday( hstring );
#endif
}
QDate date = mStartDate.addDays( mWeekStartsMonday ? 3 : 4 );
for( i = 0; i < weekNum; ++i ) {
int wno;
// remember, according to ISO 8601, the first week of the year is the
// first week that contains a thursday. Thus we must subtract off 4,
// not just 1.
int dayOfYear = date.dayOfYear();
if (dayOfYear % 7 != 0)
wno = dayOfYear / 7 + 1;
else
wno =dayOfYear / 7;
(*weekLabels)[i]->setWeekNum( wno );
date = date.addDays( 7 );
}
updateView();
}
void KOMonthView::showEvents(QPtrList<Event>)
{
qDebug("KOMonthView::selectEvents is not implemented yet. ");
}
void KOMonthView::changeEventDisplay(Event *, int)
{
// this should be re-written to be much more efficient, but this
// quick-and-dirty-hack gets the job done for right now.
updateView();
}
void KOMonthView::updateView()
{
if ( !updatePossible )
return;
//QTime ti;
//ti.start();
clearSelection();
QPtrVector<MonthViewCell> *cells;
if ( mShowWeekView ) {
cells = &mCellsW;
} else {
cells = &mCells;
}
#if 1
int i;
int timeSpan = (*cells).size()-1;
if ( KOPrefs::instance()->mMonthViewWeek )
timeSpan = 6;
for( i = 0; i < timeSpan + 1; ++i ) {
(*cells)[i]->startUpdateCell();
}
QPtrList<Event> events = calendar()->events();
Event *event;
QDateTime dt;
bool ok;
QDate endDate = mStartDate.addDays( timeSpan );
for( event = events.first(); event; event = events.next() ) { // for event
if ( event->doesRecur() ) {
bool last;
- qDebug("********************************************** ");
- qDebug("Event summary: %s ", event->summary().latin1());
QDateTime incidenceStart = event->recurrence()->getPreviousDateTime( QDateTime( mStartDate ) , &last );
QDateTime incidenceEnd;
int eventlen = event->dtStart().date().daysTo ( event->dtEnd().date() );
- qDebug("eventlen %d ", eventlen);
bool invalid = false;
while( true ) {
if ( incidenceStart.isValid() ) {
incidenceEnd = incidenceStart.addDays( eventlen );
int st = incidenceStart.date().daysTo( endDate );
if ( st >= 0 ) { // start before timeend
int end = mStartDate.daysTo( incidenceEnd.date() );
if ( end >= 0 ) { // end after timestart --- got one!
//normalize
st = timeSpan - st;
if ( st < 0 ) st = 0;
if ( end > timeSpan ) end = timeSpan;
int iii;
//qDebug("found %s %d %d ",event->summary().latin1(), st, end );
for ( iii = st;iii<= end;++iii)
(*cells)[iii]->insertEvent( event );
}
}
} else {
if ( invalid )
break;
invalid = true;
- qDebug("invalid %s", event->summary().latin1());
+ //qDebug("invalid %s", event->summary().latin1());
incidenceStart = QDateTime( mStartDate ).addSecs( -2 );;
}
if ( last )
break;
bool ok;
- qDebug("TRY next occurence %s ", incidenceStart.toString().latin1());
incidenceStart = event->getNextOccurence( incidenceStart.addSecs( 1 ) ,&ok );
- if ( ! ok ) {
- qDebug("NOT OK ");
+ if ( ! ok )
break;
- }
- if ( incidenceStart.date() > endDate ) {
- qDebug("incidenceStart.date() > endDate ");
+ if ( incidenceStart.date() > endDate )
break;
}
- qDebug("next occurence %s ", incidenceStart.toString().latin1());
- }
} else { // no recur
int st = event->dtStart().date().daysTo( endDate );
if ( st >= 0 ) { // start before timeend
int end = mStartDate.daysTo( event->dtEnd().date() );
if ( end >= 0 ) { // end after timestart --- got one!
//normalize
st = timeSpan - st;
if ( st < 0 ) st = 0;
if ( end > timeSpan ) end = timeSpan;
int iii;
for ( iii = st;iii<= end;++iii)
(*cells)[iii]->insertEvent( event );
}
}
}
}
// insert due todos
QPtrList<Todo> todos = calendar()->todos( );
Todo *todo;
for(todo = todos.first(); todo; todo = todos.next()) {
//insertTodo( todo );
if ( todo->hasDueDate() ) {
int day = mStartDate.daysTo( todo->dtDue().date() );
if ( day >= 0 && day < timeSpan + 1) {
(*cells)[day]->insertTodo( todo );
}
}
}
for( i = 0; i < timeSpan+1; ++i ) {
(*cells)[i]->finishUpdateCell();
}
processSelectionChange();
(*cells)[0]->setFocus();
#else
// old code
//qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ");
int i;
for( i = 0; i < (*cells).count(); ++i ) {
(*cells)[i]->updateCell();
}
//qDebug("KOMonthView::updateView() ");
processSelectionChange();
// qDebug("---------------------------------------------------------------------+ ");
(*cells)[0]->setFocus();
#endif
//qDebug("update time %d ", ti.elapsed());
}
void KOMonthView::resizeEvent(QResizeEvent * e)
{
//qDebug("KOMonthView::resizeEvent %d %d -- %d %d ", e->size().width(), e->size().height(), e->oldSize().width(), e->oldSize().height());
computeLayout();
clPending = true;
if ( mShowWeekView )
mCellsW[0]->setFocus();
else
mCells[0]->setFocus();
}
void KOMonthView::computeLayoutWeek()
{
static int lastWid = 0;
static int lastHei = 0;
int daysToShow;
bool combinedSatSun = false;
if (mShowSatSunComp = KOPrefs::instance()->mMonthViewSatSunTog ) {
daysToShow = 6;
combinedSatSun = true;
}
int tWid = topLevelWidget()->size().width();
int tHei = topLevelWidget()->size().height();
int wid = width();//e
int hei = height()-1-mNavigatorBar->height();
if ( ((wid *3)/2) < tWid && (( hei *3) /2) < tHei )
return;
if ( lastWid == width() && lastHei == height() )
return;
lastWid = width();
lastHei = height();
if ( wid < hei )
daysToShow = 2;
else
daysToShow = 3;
mShowSatSunComp = true;
combinedSatSun = true;
//qDebug("KOMonthView::computeLayout() WWW ------------------------------------ ");
QFontMetrics fm ( mWeekLabels[0]->font() );
int weeklabelwid = fm.width( "888" );
wid -= weeklabelwid;
int colWid = wid / daysToShow;
int lastCol = wid - ( colWid*6 );
int dayLabelHei = mDayLabelsW[0]->sizeHint().height();
int cellHei = (hei - (5- daysToShow )*dayLabelHei) /(5- daysToShow );
int colModulo = wid % daysToShow;
int rowModulo = (hei- (5- daysToShow )*dayLabelHei) % daysToShow-1;
//qDebug("rowmod %d ", rowModulo);
int i;
int x,y,w,h;
x= 0;
y= 0;
w = colWid;
h = dayLabelHei ;
for ( i = 0; i < 7; i++) {
if ( i && !( i % daysToShow) && i < 6) {
y += hei/(5-daysToShow);
x = 0;
w = colWid;
}
if ( ((i) % daysToShow) >= daysToShow-colModulo ) {
++w;
}
if ( i >= 5 ) {
mDayLabelsW[i]->setGeometry( x+weeklabelwid,y,w/2+w%2,h);
x -= (w/2 );
}
else
mDayLabelsW[i]->setGeometry( x+weeklabelwid,y,w,h);
x += w;
}
x= 0;
y= dayLabelHei;
w = colWid;
h = cellHei;
for ( i = 0; i < mCellsW.count(); ++i) {
if ( i > 6 ) {
mCellsW[i]->hide();
continue;
}
w = colWid;
if ( ((i) % daysToShow) >= daysToShow-colModulo ) {
++w;
}
if ( i == (daysToShow-1-rowModulo)*7)
++h;
if ( i >= 5 ) {
if ( i ==5 ) {
mCellsW[i]->setGeometry ( x+weeklabelwid,y,w,h/2 );
x -= w ;y += h/2;
} else {
if ( ((i-1) % daysToShow) >= daysToShow-colModulo ) {
++w;
}
mCellsW[i]->setGeometry ( x+weeklabelwid,y,w,h-h/2 );
y -= h/2;
}
} else
mCellsW[i]->setGeometry ( x+weeklabelwid,y,w,h );
x += w;
if ( x + w/2 > wid ) {
x = 0;
y += h+dayLabelHei ;
}
}
y= dayLabelHei;
h = cellHei ;
mWeekLabelsW[0]->setGeometry( 0,y,weeklabelwid,hei-dayLabelHei);
mWeekLabelsW[1]->setGeometry( 0,0,weeklabelwid,dayLabelHei);
// qDebug("RRRRRRRRRRRRR %d %d old %d %d", e->size().width(),e->size().height() , e->oldSize().width(),e->oldSize().height());
//qDebug("parent %d %d ", topLevelWidget()->size().width(), topLevelWidget()->size().height());
mShortDayLabelsW = mDayLabelsW[0]->width()-2 < mWidthLongDayLabel ;
updateDayLabels();
//bool forceUpdate = !updatePossible;
updatePossible = true;
//mWeekLabels[mNumWeeks]->setText( i18n("M"));
//if ( forceUpdate )
// updateView();
}
void KOMonthView::computeLayout()
{
static int lastWid = 0;
static int lastHei = 0;
if ( mShowWeekView ){
computeLayoutWeek();
return;
}
int daysToShow = 7;
bool combinedSatSun = false;
if (mShowSatSunComp = KOPrefs::instance()->mMonthViewSatSunTog ) {
daysToShow = 6;
combinedSatSun = true;
}
int tWid = topLevelWidget()->size().width();
int tHei = topLevelWidget()->size().height();
int wid = width();//e
int hei = height()-1-mNavigatorBar->height();
if ( ((wid *3)/2) < tWid && (( hei *3) /2) < tHei ) {
return;
}
if ( lastWid == width() && lastHei == height() ){
return;
}
lastWid = width();
lastHei = height();
//qDebug("KOMonthView::computeLayout() MMM ------------------------------------ ");
QFontMetrics fm ( mWeekLabels[0]->font() );
int weeklabelwid = fm.width( "888" );
wid -= weeklabelwid;
int colWid = wid / daysToShow;
int lastCol = wid - ( colWid*6 );
int dayLabelHei = mDayLabels[0]->sizeHint().height();
int cellHei = (hei - dayLabelHei) /6;
int colModulo = wid % daysToShow;
int rowModulo = (hei- dayLabelHei) % 6;
//qDebug("rowmod %d ", rowModulo);
int i;
int x,y,w,h;
x= 0;
y= 0;
w = colWid;
h = dayLabelHei ;
for ( i = 0; i < 7; i++) {
if ( i == daysToShow-colModulo )
++w;
if ( combinedSatSun ) {
if ( i >= daysToShow-1 ) {
mDayLabels[i]->setGeometry( x+weeklabelwid,y,w/2,h);
x -= w/2 ;
}
else
mDayLabels[i]->setGeometry( x+weeklabelwid,y,w,h);
} else
mDayLabels[i]->setGeometry( x+weeklabelwid,y,w,h);
x += w;
}
x= 0;
y= dayLabelHei;
w = colWid;
h = cellHei ;
for ( i = 0; i < mCells.count(); ++i) {
//qDebug("iii %d ", i);
w = colWid;
if ( ((i) % 7) >= 7-colModulo ) {
++w;
}
if ( i == (6-rowModulo)*7)
++h;
if ( combinedSatSun ) {
if ( (i)%7 >= daysToShow-1 ) {
if ( (i)%7 == daysToShow-1 ) {
mCells[i]->setGeometry ( x+weeklabelwid,y,w,h/2 );
x -= w ;y += h/2;
} else {
mCells[i]->setGeometry ( x+weeklabelwid,y,w,h-h/2 );
y -= h/2;
}
} else
mCells[i]->setGeometry ( x+weeklabelwid,y,w,h );
}
else
mCells[i]->setGeometry ( x+weeklabelwid,y,w,h );
x += w;
if ( x + w/2 > wid ) {
x = 0;
y += h;
}
}
y= dayLabelHei;
h = cellHei ;
for ( i = 0; i < 6; i++) {
if ( i == (6-rowModulo))
++h;
mWeekLabels[i]->setGeometry( 0,y,weeklabelwid,h);
y += h;
}
mWeekLabels[6]->setGeometry( 0,0,weeklabelwid,dayLabelHei);
// qDebug("RRRRRRRRRRRRR %d %d old %d %d", e->size().width(),e->size().height() , e->oldSize().width(),e->oldSize().height());
//qDebug("parent %d %d ", topLevelWidget()->size().width(), topLevelWidget()->size().height());
mShortDayLabelsM = mDayLabels[0]->width()-2 < mWidthLongDayLabel ;
updateDayLabels();
//bool forceUpdate = !updatePossible;
updatePossible = true;
//mWeekLabels[mNumWeeks]->setText( i18n("W"));
}
void KOMonthView::showContextMenu( Incidence *incidence )
{
mContextMenu->showIncidencePopup(incidence);
/*
if( incidence && incidence->type() == "Event" ) {
Event *event = static_cast<Event *>(incidence);
mContextMenu->showEventPopup(event);
} else {
kdDebug() << "MonthView::showContextMenu(): cast failed." << endl;
}
*/
}
MonthViewCell * KOMonthView::selectedCell( )
{
return mSelectedCell;
}
void KOMonthView::setSelectedCell( MonthViewCell *cell )
{
//qDebug("KOMonthView::setSelectedCell ");
if ( mSelectedCell && mSelectedCell != cell ) {
MonthViewCell * mvc = mSelectedCell;
mSelectedCell = cell;
mvc->deselect();
} else
mSelectedCell = cell;
// if ( mSelectedCell )
// mSelectedCell->select();
if ( !mSelectedCell )
emit incidenceSelected( 0 );
else
emit incidenceSelected( mSelectedCell->selectedIncidence() );
}
void KOMonthView::processSelectionChange()
{
QPtrList<Incidence> incidences = selectedIncidences();
if (incidences.count() > 0) {
emit incidenceSelected( incidences.first() );
} else {
emit incidenceSelected( 0 );
clearSelection();
}
}
void KOMonthView::clearSelection()
{
if ( mSelectedCell ) {
mSelectedCell->deselect();
mSelectedCell = 0;
}
}
void KOMonthView::keyPressEvent ( QKeyEvent * e )
{
//qDebug("KOMonthView::keyPressEvent ");
switch(e->key()) {
case Key_Up:
{
if ( mShowWeekView ) {
mCellsW[0]->setFocus();
emit selectWeekNum ( currentWeek() - 1 );
}
else {
mCells[0]->setFocus();
emit prevMonth();
}
}
e->accept();
break;
case Key_Down:
{
if ( mShowWeekView ) {
mCellsW[0]->setFocus();
emit selectWeekNum ( currentWeek() +1);
}
else {
mCells[0]->setFocus();
emit nextMonth();
}
}
e->accept();
break;
case Key_Return:
case Key_Enter:
{
selectInternalWeekNum ( currentWeek() );
}
e->accept();
break;
case Key_D:
if ( mSelectedCell ) {
mSelectedCell->showDay();
e->accept();
} else {
e->ignore();
}
break;
default:
e->ignore();
break;
}
}
diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp
index 8a175c9..6ee5499 100644
--- a/libkcal/recurrence.cpp
+++ b/libkcal/recurrence.cpp
@@ -1,3413 +1,3401 @@
/*
This file is part of libkcal.
Copyright (c) 1998 Preston Brown
Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include <limits.h>
#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
#include "incidence.h"
#include "recurrence.h"
using namespace KCal;
Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1;
Recurrence::Recurrence(Incidence *parent, int compatVersion)
: recurs(rNone), // by default, it's not a recurring event
rWeekStart(1), // default is Monday
rDays(7),
mFloats(parent ? parent->doesFloat() : false),
mRecurReadOnly(false),
mRecurExDatesCount(0),
mFeb29YearlyType(mFeb29YearlyDefaultType),
mCompatVersion(compatVersion ? compatVersion : INT_MAX),
mCompatRecurs(rNone),
mCompatDuration(0),
mParent(parent)
{
rMonthDays.setAutoDelete( true );
rMonthPositions.setAutoDelete( true );
rYearNums.setAutoDelete( true );
}
Recurrence::Recurrence(const Recurrence &r, Incidence *parent)
: recurs(r.recurs),
rWeekStart(r.rWeekStart),
rDays(r.rDays.copy()),
rFreq(r.rFreq),
rDuration(r.rDuration),
rEndDateTime(r.rEndDateTime),
mRecurStart(r.mRecurStart),
mFloats(r.mFloats),
mRecurReadOnly(r.mRecurReadOnly),
mRecurExDatesCount(r.mRecurExDatesCount),
mFeb29YearlyType(r.mFeb29YearlyType),
mCompatVersion(r.mCompatVersion),
mCompatRecurs(r.mCompatRecurs),
mCompatDuration(r.mCompatDuration),
mParent(parent)
{
for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions); mp.current(); ++mp) {
rMonthPos *tmp = new rMonthPos;
tmp->rPos = mp.current()->rPos;
tmp->negative = mp.current()->negative;
tmp->rDays = mp.current()->rDays.copy();
rMonthPositions.append(tmp);
}
for (QPtrListIterator<int> md(r.rMonthDays); md.current(); ++md) {
int *tmp = new int;
*tmp = *md.current();
rMonthDays.append(tmp);
}
for (QPtrListIterator<int> yn(r.rYearNums); yn.current(); ++yn) {
int *tmp = new int;
*tmp = *yn.current();
rYearNums.append(tmp);
}
rMonthDays.setAutoDelete( true );
rMonthPositions.setAutoDelete( true );
rYearNums.setAutoDelete( true );
}
Recurrence::~Recurrence()
{
}
bool Recurrence::operator==( const Recurrence& r2 ) const
{
// the following line is obvious
if ( recurs == rNone && r2.recurs == rNone )
return true;
// we need the above line, because two non recurring events may
// differ in the other settings, because one (or both)
// may be not initialized properly
if ( recurs != r2.recurs
|| rFreq != r2.rFreq
|| rDuration != r2.rDuration
|| !rDuration && rEndDateTime != r2.rEndDateTime
|| mRecurStart != r2.mRecurStart
|| mFloats != r2.mFloats
|| mRecurReadOnly != r2.mRecurReadOnly
|| mRecurExDatesCount != r2.mRecurExDatesCount )
return false;
// no need to compare mCompat* and mParent
// OK to compare the pointers
switch ( recurs )
{
case rWeekly:
return rDays == r2.rDays
&& rWeekStart == r2.rWeekStart;
case rMonthlyPos: {
QPtrList<rMonthPos> MonthPositions = rMonthPositions;
QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions;
if ( !MonthPositions.count() )
return false;
if ( !MonthPositions2.count() )
return false;
return MonthPositions.first()->rPos == MonthPositions2.first()->rPos;
}
case rMonthlyDay: {
QPtrList<int> MonthDays = rMonthDays ;
QPtrList<int> MonthDays2 = r2.rMonthDays ;
if ( !MonthDays.count() )
return false;
if ( !MonthDays2.count() )
return false;
return *MonthDays.first() == *MonthDays2.first() ;
}
case rYearlyPos: {
QPtrList<int> YearNums = rYearNums;
QPtrList<int> YearNums2 = r2.rYearNums;
if ( *YearNums.first() != *YearNums2.first() )
return false;
QPtrList<rMonthPos> MonthPositions = rMonthPositions;
QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions;
if ( !MonthPositions.count() )
return false;
if ( !MonthPositions2.count() )
return false;
return MonthPositions.first()->rPos == MonthPositions2.first()->rPos;
}
case rYearlyMonth: {
QPtrList<int> YearNums = rYearNums;
QPtrList<int> YearNums2 = r2.rYearNums;
return ( *YearNums.first() == *YearNums2.first() && mFeb29YearlyType == r2.mFeb29YearlyType);
}
case rYearlyDay: {
QPtrList<int> YearNums = rYearNums;
QPtrList<int> YearNums2 = r2.rYearNums;
return ( *YearNums.first() == *YearNums2.first() );
}
case rNone:
case rMinutely:
case rHourly:
case rDaily:
default:
return true;
}
}
/*
bool Recurrence::compareLists( const QPtrList<int> &l1 ,const QPtrList<int> &l2)
{
if ( l1.count() != l2.count() )
return false;
int count = l1.count();
int i;
for ( i = 0; i < count ; ++i ) {
// if ( l1.at(i) != l2.at(i) )
return false;
qDebug("compüare ");
}
return true;
}
*/
QString Recurrence::recurrenceText() const
{
QString recurText;
if ( recurs == Recurrence::rMinutely )
recurText = i18n("minutely");
else if ( recurs == Recurrence::rHourly )
recurText = i18n("hourly");
else if ( recurs == Recurrence::rDaily )
recurText = i18n("daily");
else if ( recurs == Recurrence::rWeekly )
recurText = i18n("weekly");
else if ( recurs == Recurrence::rMonthlyPos )
recurText = i18n("monthly");
else if ( recurs == Recurrence::rMonthlyDay )
recurText = i18n("monthly");
else if ( recurs == Recurrence::rYearlyMonth )
recurText = i18n("yearly");
else if ( recurs == Recurrence::rYearlyDay )
recurText = i18n("day-yearly");
else if ( recurs == Recurrence::rYearlyPos )
recurText = i18n("position-yearly");
if ( !recurText.isEmpty() ) {
if ( rFreq > 1 ){
recurText = i18n("(%1) ").arg(rFreq ) + recurText;
}
} else
recurText = i18n("No");
return recurText;
}
void Recurrence::setCompatVersion(int version)
{
mCompatVersion = version ? version : INT_MAX;
}
ushort Recurrence::doesRecur() const
{
return recurs;
}
bool Recurrence::recursOnPure(const QDate &qd) const
{
switch(recurs) {
case rMinutely:
return recursSecondly(qd, rFreq*60);
case rHourly:
return recursSecondly(qd, rFreq*3600);
case rDaily:
return recursDaily(qd);
case rWeekly:
return recursWeekly(qd);
case rMonthlyPos:
case rMonthlyDay:
return recursMonthly(qd);
case rYearlyMonth:
return recursYearlyByMonth(qd);
case rYearlyDay:
return recursYearlyByDay(qd);
case rYearlyPos:
return recursYearlyByPos(qd);
default:
return false;
case rNone:
return false;
} // case
return false;
}
bool Recurrence::recursAtPure(const QDateTime &dt) const
{
switch(recurs) {
case rMinutely:
return recursMinutelyAt(dt, rFreq);
case rHourly:
return recursMinutelyAt(dt, rFreq*60);
default:
if (dt.time() != mRecurStart.time())
return false;
switch(recurs) {
case rDaily:
return recursDaily(dt.date());
case rWeekly:
return recursWeekly(dt.date());
case rMonthlyPos:
case rMonthlyDay:
return recursMonthly(dt.date());
case rYearlyMonth:
return recursYearlyByMonth(dt.date());
case rYearlyDay:
return recursYearlyByDay(dt.date());
case rYearlyPos:
return recursYearlyByPos(dt.date());
default:
return false;
case rNone:
return false;
}
} // case
return false;
}
QDate Recurrence::endDate() const
{
int count = 0;
QDate end;
if (recurs != rNone) {
if (rDuration < 0)
return QDate(); // infinite recurrence
if (rDuration == 0)
return rEndDateTime.date();
// The end date is determined by the recurrence count
QDate dStart = mRecurStart.date();
switch (recurs)
{
case rMinutely:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60).date();
case rHourly:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600).date();
case rDaily:
return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
case rWeekly:
count = weeklyCalc(END_DATE_AND_COUNT, end);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyMonth:
count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyDay:
count = yearlyDayCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyPos:
count = yearlyPosCalc(END_DATE_AND_COUNT, end);
break;
default:
// catch-all. Should never get here.
kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
break;
}
}
if (!count)
return QDate(); // error - there is no recurrence
return end;
}
QDateTime Recurrence::endDateTime() const
{
int count = 0;
QDate end;
if (recurs != rNone) {
if (rDuration < 0)
return QDateTime(); // infinite recurrence
if (rDuration == 0)
return rEndDateTime;
// The end date is determined by the recurrence count
QDate dStart = mRecurStart.date();
switch (recurs)
{
case rMinutely:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60);
case rHourly:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600);
case rDaily:
return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
case rWeekly:
count = weeklyCalc(END_DATE_AND_COUNT, end);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyMonth:
count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyDay:
count = yearlyDayCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyPos:
count = yearlyPosCalc(END_DATE_AND_COUNT, end);
break;
default:
// catch-all. Should never get here.
kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
break;
}
}
if (!count)
return QDateTime(); // error - there is no recurrence
return QDateTime(end, mRecurStart.time());
}
int Recurrence::durationTo(const QDate &date) const
{
QDate d = date;
return recurCalc(COUNT_TO_DATE, d);
}
int Recurrence::durationTo(const QDateTime &datetime) const
{
QDateTime dt = datetime;
return recurCalc(COUNT_TO_DATE, dt);
}
void Recurrence::unsetRecurs()
{
if (mRecurReadOnly) return;
recurs = rNone;
rMonthPositions.clear();
rMonthDays.clear();
rYearNums.clear();
}
void Recurrence::setRecurStart(const QDateTime &start)
{
mRecurStart = start;
mFloats = false;
switch (recurs)
{
case rMinutely:
case rHourly:
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
default:
rEndDateTime.setTime(start.time());
break;
}
}
void Recurrence::setRecurStart(const QDate &start)
{
mRecurStart.setDate(start);
mRecurStart.setTime(QTime(0,0,0));
switch (recurs)
{
case rMinutely:
case rHourly:
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
default:
mFloats = true;
break;
}
}
void Recurrence::setFloats(bool f)
{
switch (recurs)
{
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
break;
case rMinutely:
case rHourly:
default:
return; // can't set sub-daily to floating
}
mFloats = f;
if (f) {
mRecurStart.setTime(QTime(0,0,0));
rEndDateTime.setTime(QTime(0,0,0));
}
}
int Recurrence::frequency() const
{
return rFreq;
}
int Recurrence::duration() const
{
return rDuration;
}
void Recurrence::setDuration(int _rDuration)
{
if (mRecurReadOnly) return;
if (_rDuration > 0) {
rDuration = _rDuration;
// Compatibility mode is only needed when reading the calendar in ICalFormatImpl,
// so explicitly setting the duration means no backwards compatibility is needed.
mCompatDuration = 0;
}
}
QString Recurrence::endDateStr(bool shortfmt) const
{
return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt);
}
const QBitArray &Recurrence::days() const
{
return rDays;
}
const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const
{
return rMonthPositions;
}
const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const
{
return rMonthPositions;
}
const QPtrList<int> &Recurrence::monthDays() const
{
return rMonthDays;
}
void Recurrence::setMinutely(int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
setDailySub(rMinutely, _rFreq, _rDuration);
}
void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime)
{
if (mRecurReadOnly) return;
rEndDateTime = _rEndDateTime;
setDailySub(rMinutely, _rFreq, 0);
}
void Recurrence::setHourly(int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
setDailySub(rHourly, _rFreq, _rDuration);
}
void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime)
{
if (mRecurReadOnly) return;
rEndDateTime = _rEndDateTime;
setDailySub(rHourly, _rFreq, 0);
}
void Recurrence::setDaily(int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
setDailySub(rDaily, _rFreq, _rDuration);
}
void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
setDailySub(rDaily, _rFreq, 0);
}
void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
int _rDuration, int _rWeekStart)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
recurs = rWeekly;
rFreq = _rFreq;
rDays = _rDays;
rWeekStart = _rWeekStart;
rDuration = _rDuration;
if (mCompatVersion < 310 && _rDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur,
// with week start always on a Monday.
// Convert this to the number of occurrences.
mCompatDuration = _rDuration;
int weeks = ((mCompatDuration-1+mRecurExDatesCount)*7) + (7 - mRecurStart.date().dayOfWeek());
QDate end(mRecurStart.date().addDays(weeks * rFreq));
rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly
rDuration = weeklyCalc(COUNT_TO_DATE, end);
} else {
mCompatDuration = 0;
}
rMonthPositions.clear();
rMonthDays.clear();
if (mParent) mParent->updated();
}
void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
const QDate &_rEndDate, int _rWeekStart)
{
if (mRecurReadOnly) return;
recurs = rWeekly;
rFreq = _rFreq;
rDays = _rDays;
rWeekStart = _rWeekStart;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
rDuration = 0; // set to 0 because there is an end date
mCompatDuration = 0;
rMonthPositions.clear();
rMonthDays.clear();
rYearNums.clear();
if (mParent) mParent->updated();
}
void Recurrence::setMonthly(short type, int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
recurs = type;
rFreq = _rFreq;
rDuration = _rDuration;
if (mCompatVersion < 310)
mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
rYearNums.clear();
if (mParent) mParent->updated();
}
void Recurrence::setMonthly(short type, int _rFreq,
const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
recurs = type;
rFreq = _rFreq;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
rDuration = 0; // set to 0 because there is an end date
mCompatDuration = 0;
rYearNums.clear();
if (mParent) mParent->updated();
}
void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays)
{
if (recurs == rMonthlyPos)
addMonthlyPos_(_rPos, _rDays);
}
void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays)
{
if (mRecurReadOnly
|| _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number
return;
for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) {
int itPos = it->negative ? -it->rPos : it->rPos;
if (_rPos == itPos) {
// This week is already in the list.
// Combine the specified days with those in the list.
it->rDays |= _rDays;
if (mParent) mParent->updated();
return;
}
}
// Add the new position to the list
rMonthPos *tmpPos = new rMonthPos;
if (_rPos > 0) {
tmpPos->rPos = _rPos;
tmpPos->negative = false;
} else {
tmpPos->rPos = -_rPos; // take abs()
tmpPos->negative = true;
}
tmpPos->rDays = _rDays;
tmpPos->rDays.detach();
rMonthPositions.append(tmpPos);
if (mCompatVersion < 310 && mCompatDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur.
// Convert this to the number of occurrences.
int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
int month = mRecurStart.date().month() - 1 + monthsAhead;
QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
rDuration = recurCalc(COUNT_TO_DATE, end);
}
if (mParent) mParent->updated();
}
void Recurrence::addMonthlyDay(short _rDay)
{
if (mRecurReadOnly || (recurs != rMonthlyDay && recurs != rYearlyMonth)
|| _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number
return;
for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) {
if (_rDay == *it)
return; // this day is already in the list - avoid duplication
}
int *tmpDay = new int;
*tmpDay = _rDay;
rMonthDays.append(tmpDay);
if (mCompatVersion < 310 && mCompatDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur.
// Convert this to the number of occurrences.
int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
int month = mRecurStart.date().month() - 1 + monthsAhead;
QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
rDuration = recurCalc(COUNT_TO_DATE, end);
}
if (mParent) mParent->updated();
}
void Recurrence::setYearly(int type, int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
if (mCompatVersion < 310)
mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration);
}
void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
mCompatDuration = 0;
setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0);
}
void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
if (mCompatVersion < 310)
mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
setYearly_(rYearlyMonth, type, _rFreq, _rDuration);
}
void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
mCompatDuration = 0;
setYearly_(rYearlyMonth, type, _rFreq, 0);
}
void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays)
{
if (recurs == rYearlyPos)
addMonthlyPos_(_rPos, _rDays);
}
const QPtrList<int> &Recurrence::yearNums() const
{
return rYearNums;
}
void Recurrence::addYearlyMonth(short _rPos )
{
if (mRecurReadOnly || recurs != rYearlyMonth) // invalid day/month number
return;
rMonthPos *tmpPos = new rMonthPos;
if ( _rPos > 0) {
tmpPos->rPos = _rPos;
tmpPos->negative = false;
} else {
tmpPos->rPos = -_rPos; // take abs()
tmpPos->negative = true;
}
rMonthPositions.append(tmpPos);
}
void Recurrence::addYearlyNum(short _rNum)
{
if (mRecurReadOnly
|| (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos)
|| _rNum <= 0) // invalid day/month number
return;
if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) {
// Backwards compatibility for KDE < 3.1.
// Dates were stored as day numbers, with a fiddle to take account of leap years.
// Convert the day number to a month.
if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366))
return; // invalid day number
_rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month();
} else
if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12
|| recurs == rYearlyDay && _rNum > 366)
return; // invalid day number
uint i = 0;
for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) {
if (_rNum == *it)
return; // this day/month is already in the list - avoid duplication
++i;
}
int *tmpNum = new int;
*tmpNum = _rNum;
rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position
if (mCompatVersion < 310 && mCompatDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur.
// Convert this to the number of occurrences.
QDate end(mRecurStart.date().year() + (mCompatDuration-1+mRecurExDatesCount)*rFreq, 12, 31);
rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
rDuration = recurCalc(COUNT_TO_DATE, end);
}
if (mParent) mParent->updated();
}
QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const
{
if (last)
*last = false;
int freq;
switch (recurs)
{
case rMinutely:
freq = rFreq * 60;
break;
case rHourly:
freq = rFreq * 3600;
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos: {
QDate preDate = preDateTime.date();
if (!mFloats && mRecurStart.time() > preDateTime.time())
preDate = preDate.addDays(-1);
return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time());
}
default:
return QDateTime();
}
// It's a sub-daily recurrence
if (preDateTime < mRecurStart)
return mRecurStart;
int count = mRecurStart.secsTo(preDateTime) / freq + 2;
if (rDuration > 0) {
if (count > rDuration)
return QDateTime();
if (last && count == rDuration)
*last = true;
}
QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
if (rDuration == 0) {
if (endtime > rEndDateTime)
return QDateTime();
if (last && endtime == rEndDateTime)
*last = true;
}
return endtime;
}
QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const
{
if (last)
*last = false;
switch (recurs)
{
case rMinutely:
case rHourly:
return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date();
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
qDebug("Recurrence::getNextDate: MAY BE BROKEN ");
return getNextDateNoTime(preDate, last);
default:
return QDate();
}
}
QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const
{
if (last)
*last = false;
int freq;
switch (recurs)
{
case rMinutely:
freq = rFreq * 60;
break;
case rHourly:
freq = rFreq * 3600;
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos: {
QDate afterDate = afterDateTime.date();
if (!mFloats && mRecurStart.time() < afterDateTime.time())
afterDate = afterDate.addDays(1);
return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time());
}
default:
return QDateTime();
}
// It's a sub-daily recurrence
if (afterDateTime <= mRecurStart)
return QDateTime();
int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1;
if (rDuration > 0) {
if (count > rDuration)
count = rDuration;
if (last && count == rDuration)
*last = true;
}
QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
if (rDuration == 0) {
if (endtime > rEndDateTime)
endtime = rEndDateTime;
if (last && endtime == rEndDateTime)
*last = true;
}
return endtime;
}
QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const
{
if (last)
*last = false;
switch (recurs)
{
case rMinutely:
case rHourly:
return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date();
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
return getPreviousDateNoTime(afterDate, last);
default:
return QDate();
}
}
/***************************** PROTECTED FUNCTIONS ***************************/
bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const
{
if ((qd >= mRecurStart.date()) &&
((rDuration > 0) && (qd <= endDate()) ||
((rDuration == 0) && (qd <= rEndDateTime.date())) ||
(rDuration == -1))) {
// The date queried falls within the range of the event.
if (secondFreq < 24*3600)
return true; // the event recurs at least once each day
- int after = mRecurStart.secsTo(QDateTime(qd));
+ int after = mRecurStart.secsTo(QDateTime(qd)) - 1;
if (after / secondFreq != (after + 24*3600) / secondFreq)
return true;
}
return false;
}
bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const
{
if ((dt >= mRecurStart) &&
((rDuration > 0) && (dt <= endDateTime()) ||
((rDuration == 0) && (dt <= rEndDateTime)) ||
(rDuration == -1))) {
// The time queried falls within the range of the event.
if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0)
return true;
}
return false;
}
bool Recurrence::recursDaily(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
if ((dStart.daysTo(qd) % rFreq) == 0) {
// The date is a day which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
return true;
}
}
return false;
}
bool Recurrence::recursWeekly(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
if ((dStart.daysTo(qd)/7) % rFreq == 0) {
// The date is in a week which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
// check if the bits set match today.
int i = qd.dayOfWeek()-1;
if (rDays.testBit((uint) i))
return true;
}
}
return false;
}
bool Recurrence::recursMonthly(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
int year = qd.year();
int month = qd.month();
int day = qd.day();
// calculate how many months ahead this date is from the original
// event's date
int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month());
if ((monthsAhead % rFreq) == 0) {
// The date is in a month which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
QValueList<int> days;
int daysInMonth = qd.daysInMonth();
if (recurs == rMonthlyDay)
getMonthlyDayDays(days, daysInMonth);
else if (recurs == rMonthlyPos)
getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek());
for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
if (*it == day)
return true;
}
// no dates matched
}
}
return false;
}
bool Recurrence::recursYearlyByMonth(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
int startDay = dStart.day();
int qday = qd.day();
int qmonth = qd.month();
int qyear = qd.year();
bool match = (qday == startDay);
if (!match && startDay == 29 && dStart.month() == 2) {
// It's a recurrence on February 29th
switch (mFeb29YearlyType) {
case rFeb28:
if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear))
match = true;
break;
case rMar1:
if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) {
qmonth = 2;
match = true;
}
break;
case rFeb29:
break;
}
}
if (match) {
// The day of the month matches. Calculate how many years ahead
// this date is from the original event's date.
int yearsAhead = (qyear - dStart.year());
if (yearsAhead % rFreq == 0) {
// The date is in a year which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
int i = qmonth;
for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
if (i == *qlin.current())
return true;
}
}
}
}
return false;
}
bool Recurrence::recursYearlyByPos(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
int year = qd.year();
int month = qd.month();
int day = qd.day();
// calculate how many years ahead this date is from the original
// event's date
int yearsAhead = (year - dStart.year());
if (yearsAhead % rFreq == 0) {
// The date is in a year which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
if (month == *qlin.current()) {
// The month recurs
QValueList<int> days;
getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek());
for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
if (*it == day)
return true;
}
}
}
}
}
return false;
}
bool Recurrence::recursYearlyByDay(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
// calculate how many years ahead this date is from the original
// event's date
int yearsAhead = (qd.year() - dStart.year());
if (yearsAhead % rFreq == 0) {
// The date is in a year which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
int i = qd.dayOfYear();
for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
if (i == *qlin.current())
return true;
}
}
}
return false;
}
/* Get the date of the next recurrence, after the specified date.
* If 'last' is non-null, '*last' is set to true if the next recurrence is the
* last recurrence, else false.
* Reply = date of next recurrence, or invalid date if none.
*/
QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const
{
if (last)
*last = false;
QDate dStart = mRecurStart.date();
if (preDate < dStart)
return dStart;
QDate earliestDate = preDate.addDays(1);
QDate nextDate;
switch (recurs) {
case rDaily:
nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq);
break;
case rWeekly: {
- QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
+ QDate start = dStart.addDays(-((dStart.dayOfWeek() - rWeekStart + 7)%7)); // start of week for dStart
int earliestDayOfWeek = earliestDate.dayOfWeek();
int weeksAhead = start.daysTo(earliestDate) / 7;
int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
weeksAhead -= notThisWeek; // latest week which recurred
int weekday = 0;
// First check for any remaining day this week, if this week is a recurring week
if (!notThisWeek)
weekday = getFirstDayInWeek(earliestDayOfWeek);
// Check for a day in the next scheduled week
- if (!weekday && earliestDayOfWeek > 1)
+ if (!weekday )
weekday = getFirstDayInWeek(rWeekStart) + rFreq*7;
- if (weekday)
nextDate = start.addDays(weeksAhead*7 + weekday - 1);
break;
}
case rMonthlyDay:
case rMonthlyPos: {
int startYear = dStart.year();
int startMonth = dStart.month(); // 1..12
int earliestYear = earliestDate.year();
int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth;
int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
monthsAhead -= notThisMonth; // latest month which recurred
// Check for the first later day in the current month
if (!notThisMonth)
nextDate = getFirstDateInMonth(earliestDate);
- if (!nextDate.isValid() && earliestDate.day() > 1) {
+ if (!nextDate.isValid() ) {
// Check for a day in the next scheduled month
int months = startMonth - 1 + monthsAhead + rFreq;
nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1));
}
break;
}
case rYearlyMonth:
case rYearlyPos:
case rYearlyDay: {
int startYear = dStart.year();
int yearsAhead = earliestDate.year() - startYear;
int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
yearsAhead -= notThisYear; // latest year which recurred
// Check for the first later date in the current year
if (!notThisYear)
nextDate = getFirstDateInYear(earliestDate);
// Check for a date in the next scheduled year
if (!nextDate.isValid() && earliestDate.dayOfYear() > 1)
nextDate = getFirstDateInYear(QDate(startYear + yearsAhead + rFreq, 1, 1));
break;
}
case rNone:
default:
return QDate();
}
if (rDuration >= 0 && nextDate.isValid()) {
// Check that the date found is within the range of the recurrence
QDate end = endDate();
if (nextDate > end)
return QDate();
if (last && nextDate == end)
*last = true;
}
return nextDate;
}
/* Get the date of the last previous recurrence, before the specified date.
* Reply = date of previous recurrence, or invalid date if none.
*/
QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const
{
if (last)
*last = false;
QDate dStart = mRecurStart.date();
QDate latestDate = afterDate.addDays(-1);
if (latestDate < dStart)
return QDate();
QDate prevDate;
switch (recurs) {
case rDaily:
prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq);
break;
case rWeekly: {
- QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
+ QDate start = dStart.addDays(-((dStart.dayOfWeek() - rWeekStart + 7)%7)); // start of week for dStart
int latestDayOfWeek = latestDate.dayOfWeek();
int weeksAhead = start.daysTo(latestDate) / 7;
int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
weeksAhead -= notThisWeek; // latest week which recurred
int weekday = 0;
// First check for any previous day this week, if this week is a recurring week
if (!notThisWeek)
weekday = getLastDayInWeek(latestDayOfWeek);
// Check for a day in the previous scheduled week
if (!weekday) {
- int weekEnd = (rWeekStart + 5)%7 + 1;
- if (latestDayOfWeek < weekEnd) {
if (!notThisWeek)
weeksAhead -= rFreq;
+ int weekEnd = (rWeekStart + 5)%7 + 1;
weekday = getLastDayInWeek(weekEnd);
}
- }
if (weekday)
prevDate = start.addDays(weeksAhead*7 + weekday - 1);
break;
}
case rMonthlyDay:
case rMonthlyPos: {
int startYear = dStart.year();
int startMonth = dStart.month(); // 1..12
int latestYear = latestDate.year();
int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth;
int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
monthsAhead -= notThisMonth; // latest month which recurred
// Check for the last earlier day in the current month
if (!notThisMonth)
prevDate = getLastDateInMonth(latestDate);
if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) {
// Check for a day in the previous scheduled month
if (!notThisMonth)
monthsAhead -= rFreq;
int months = startMonth + monthsAhead; // get the month after the one that recurs
prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1));
}
break;
}
case rYearlyMonth:
case rYearlyPos:
case rYearlyDay: {
int startYear = dStart.year();
int yearsAhead = latestDate.year() - startYear;
int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
yearsAhead -= notThisYear; // latest year which recurred
// Check for the first later date in the current year
if (!notThisYear)
prevDate = getLastDateInYear(latestDate);
if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) {
// Check for a date in the next scheduled year
if (!notThisYear)
yearsAhead -= rFreq;
prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31));
}
break;
}
case rNone:
default:
return QDate();
}
if (prevDate.isValid()) {
// Check that the date found is within the range of the recurrence
if (prevDate < dStart)
return QDate();
if (rDuration >= 0) {
QDate end = endDate();
if (prevDate >= end) {
if (last)
*last = true;
return end;
}
}
}
return prevDate;
}
void Recurrence::setDailySub(short type, int freq, int duration)
{
recurs = type;
rFreq = freq;
rDuration = duration;
rMonthPositions.clear();
rMonthDays.clear();
rYearNums.clear();
if (type != rDaily)
mFloats = false; // sub-daily types can't be floating
if (mParent) mParent->updated();
}
void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration)
{
recurs = type;
if (mCompatVersion < 310 && type == rYearlyDay) {
mCompatRecurs = rYearlyDay;
recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month
feb29type = rMar1; // retain the same day number in the year
}
mFeb29YearlyType = feb29type;
rFreq = freq;
rDuration = duration;
if (type != rYearlyPos)
rMonthPositions.clear();
rMonthDays.clear();
if (mParent) mParent->updated();
}
int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const
{
QDate enddate = endtime.date();
switch (func) {
case END_DATE_AND_COUNT:
if (rDuration < 0) {
endtime = QDateTime();
return 0; // infinite recurrence
}
if (rDuration == 0) {
endtime = rEndDateTime;
func = COUNT_TO_DATE;
}
break;
case COUNT_TO_DATE:
// Count recurrences up to and including the specified date/time.
if (endtime < mRecurStart)
return 0;
if (rDuration == 0 && endtime > rEndDateTime)
enddate = rEndDateTime.date();
else if (!mFloats && mRecurStart.time() > endtime.time())
enddate = enddate.addDays(-1);
break;
case NEXT_AFTER_DATE:
// Find next recurrence AFTER endtime
if (endtime < mRecurStart) {
endtime = mRecurStart;
return 1;
}
if (rDuration == 0 && endtime >= rEndDateTime) {
endtime = QDateTime();
return 0;
}
if (!mFloats && mRecurStart.time() > endtime.time())
enddate = enddate.addDays(-1);
break;
default:
endtime = QDateTime();
return 0;
}
int count = 0; // default = error
bool timed = false;
switch (recurs) {
case rMinutely:
timed = true;
count = secondlyCalc(func, endtime, rFreq*60);
break;
case rHourly:
timed = true;
count = secondlyCalc(func, endtime, rFreq*3600);
break;
case rDaily:
count = dailyCalc(func, enddate);
break;
case rWeekly:
count = weeklyCalc(func, enddate);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(func, enddate);
break;
case rYearlyMonth:
count = yearlyMonthCalc(func, enddate);
break;
case rYearlyPos:
count = yearlyPosCalc(func, enddate);
break;
case rYearlyDay:
count = yearlyDayCalc(func, enddate);
break;
default:
break;
}
switch (func) {
case END_DATE_AND_COUNT:
case NEXT_AFTER_DATE:
if (count == 0)
endtime = QDateTime();
else if (!timed) {
endtime.setDate(enddate);
endtime.setTime(mRecurStart.time());
}
break;
case COUNT_TO_DATE:
break;
}
return count;
}
int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const
{
QDateTime endtime(enddate, QTime(23,59,59));
switch (func) {
case END_DATE_AND_COUNT:
if (rDuration < 0) {
enddate = QDate();
return 0; // infinite recurrence
}
if (rDuration == 0) {
enddate = rEndDateTime.date();
func = COUNT_TO_DATE;
}
break;
case COUNT_TO_DATE:
// Count recurrences up to and including the specified date.
if (enddate < mRecurStart.date())
return 0;
if (rDuration == 0 && enddate > rEndDateTime.date()) {
enddate = rEndDateTime.date();
endtime.setDate(enddate);
}
break;
case NEXT_AFTER_DATE:
if (enddate < mRecurStart.date()) {
enddate = mRecurStart.date();
return 1;
}
if (rDuration == 0 && enddate >= rEndDateTime.date()) {
enddate = QDate();
return 0;
}
break;
default:
enddate = QDate();
return 0;
}
int count = 0; // default = error
bool timed = false;
switch (recurs) {
case rMinutely:
timed = true;
count = secondlyCalc(func, endtime, rFreq*60);
break;
case rHourly:
timed = true;
count = secondlyCalc(func, endtime, rFreq*3600);
break;
case rDaily:
count = dailyCalc(func, enddate);
break;
case rWeekly:
count = weeklyCalc(func, enddate);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(func, enddate);
break;
case rYearlyMonth:
count = yearlyMonthCalc(func, enddate);
break;
case rYearlyPos:
count = yearlyPosCalc(func, enddate);
break;
case rYearlyDay:
count = yearlyDayCalc(func, enddate);
break;
default:
break;
}
switch (func) {
case END_DATE_AND_COUNT:
case NEXT_AFTER_DATE:
if (count == 0)
endtime = QDate();
else if (timed)
enddate = endtime.date();
break;
case COUNT_TO_DATE:
break;
}
return count;
}
/* Find count and, depending on 'func', the end date/time of a secondly recurrence.
* Reply = total number of occurrences up to 'endtime', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the
* recurrence end date/time.
*/
int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const
{
switch (func) {
case END_DATE_AND_COUNT:
endtime = mRecurStart.addSecs((rDuration + mRecurExDatesCount - 1) * freq);
return rDuration + mRecurExDatesCount;
case COUNT_TO_DATE: {
int n = mRecurStart.secsTo(endtime)/freq + 1;
if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
return rDuration + mRecurExDatesCount;
return n;
}
case NEXT_AFTER_DATE: {
int count = mRecurStart.secsTo(endtime) / freq + 2;
if (rDuration > 0 && count > rDuration)
return 0;
endtime = mRecurStart.addSecs((count - 1)*freq);
return count;
}
}
return 0;
}
/* Find count and, depending on 'func', the end date of a daily recurrence.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const
{
QDate dStart = mRecurStart.date();
switch (func) {
case END_DATE_AND_COUNT:
enddate = dStart.addDays((rDuration + mRecurExDatesCount - 1) * rFreq);
return rDuration + mRecurExDatesCount;
case COUNT_TO_DATE: {
int n = dStart.daysTo(enddate)/rFreq + 1;
if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
return rDuration + mRecurExDatesCount;
return n;
}
case NEXT_AFTER_DATE: {
int count = dStart.daysTo(enddate) / rFreq + 2;
if (rDuration > 0 && count > rDuration)
return 0;
enddate = dStart.addDays((count - 1)*rFreq);
return count;
}
}
return 0;
}
/* Find count and, depending on 'func', the end date of a weekly recurrence.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const
{
int daysPerWeek = 0;
for (int i = 0; i < 7; ++i) {
if (rDays.testBit((uint)i))
++daysPerWeek;
}
if (!daysPerWeek)
return 0; // there are no days to recur on
switch (func) {
case END_DATE_AND_COUNT:
return weeklyCalcEndDate(enddate, daysPerWeek);
case COUNT_TO_DATE:
return weeklyCalcToDate(enddate, daysPerWeek);
case NEXT_AFTER_DATE:
return weeklyCalcNextAfter(enddate, daysPerWeek);
}
return 0;
}
int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const
{
int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7
int countGone = 0;
int daysGone = 0;
uint countTogo = rDuration + mRecurExDatesCount;
if (startDayOfWeek != rWeekStart) {
// Check what remains of the start week
for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (--countTogo == 0)
break;
}
}
daysGone += 7 * (rFreq - 1);
}
if (countTogo) {
// Skip the remaining whole weeks
// Leave at least 1 recurrence remaining, in order to get its date
int wholeWeeks = (countTogo - 1) / daysPerWeek;
daysGone += wholeWeeks * 7 * rFreq;
countGone += wholeWeeks * daysPerWeek;
countTogo -= wholeWeeks * daysPerWeek;
// Check the last week in the recurrence
for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (--countTogo == 0)
break;
}
}
}
enddate = mRecurStart.date().addDays(daysGone);
return countGone;
}
int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const
{
QDate dStart = mRecurStart.date();
int startDayOfWeek = dStart.dayOfWeek(); // 1..7
int countGone = 0;
int daysGone = 0;
int totalDays = dStart.daysTo(enddate) + 1;
int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
if (startDayOfWeek != rWeekStart) {
// Check what remains of the start week
for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
if (rDays.testBit((uint)i)) {
if (++countGone >= countMax)
return countMax;
}
if (++daysGone == totalDays)
return countGone;
}
daysGone += 7 * (rFreq - 1);
if (daysGone >= totalDays)
return countGone;
}
// Skip the remaining whole weeks
int wholeWeeks = (totalDays - daysGone) / 7;
countGone += (wholeWeeks / rFreq) * daysPerWeek;
if (countGone >= countMax)
return countMax;
daysGone += wholeWeeks * 7;
if (daysGone >= totalDays // have we reached the end date?
|| wholeWeeks % rFreq) // is end week a recurrence week?
return countGone;
// Check the last week in the recurrence
for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
if (rDays.testBit((uint)i)) {
if (++countGone >= countMax)
return countMax;
}
if (++daysGone == totalDays)
return countGone;
}
return countGone;
}
int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const
{
QDate dStart = mRecurStart.date();
int startDayOfWeek = dStart.dayOfWeek(); // 1..7
int totalDays = dStart.daysTo(enddate) + 1;
uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
int countGone = 0;
int daysGone = 0;
int recurWeeks;
if (startDayOfWeek != rWeekStart) {
// Check what remains of the start week
for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (daysGone > totalDays)
goto ex;
if (--countTogo == 0)
return 0;
}
}
daysGone += 7 * (rFreq - 1);
}
// Skip the remaining whole weeks
recurWeeks = (totalDays - daysGone) / (7 * rFreq);
if (recurWeeks) {
int n = recurWeeks * daysPerWeek;
if (static_cast<uint>(n) > countTogo)
return 0; // reached end of recurrence
countGone += n;
countTogo -= n;
daysGone += recurWeeks * 7 * rFreq;
}
// Check the last week or two in the recurrence
for ( ; ; ) {
for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (daysGone > totalDays)
goto ex;
if (--countTogo == 0)
return 0;
}
}
daysGone += 7 * (rFreq - 1);
}
ex:
enddate = dStart.addDays(daysGone);
return countGone;
}
/* Find count and, depending on 'func', the end date of a monthly recurrence.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
struct Recurrence::MonthlyData {
const Recurrence *recurrence;
int year; // current year
int month; // current month 0..11
int day; // current day of month 1..31
bool varies; // true if recurring days vary between different months
private:
QValueList<int> days28, days29, days30, days31; // recurring days in months of each length
QValueList<int> *recurDays[4];
public:
MonthlyData(const Recurrence* r, const QDate &date)
: recurrence(r), year(date.year()), month(date.month()-1), day(date.day())
{ recurDays[0] = &days28;
recurDays[1] = &days29;
recurDays[2] = &days30;
recurDays[3] = &days31;
varies = (recurrence->recurs == rMonthlyPos)
? true : recurrence->getMonthlyDayDays(days31, 31);
}
const QValueList<int>* dayList() const {
if (!varies)
return &days31;
QDate startOfMonth(year, month + 1, 1);
int daysInMonth = startOfMonth.daysInMonth();
QValueList<int>* days = recurDays[daysInMonth - 28];
if (recurrence->recurs == rMonthlyPos)
recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek());
else if (days->isEmpty())
recurrence->getMonthlyDayDays(*days, daysInMonth);
return days;
}
int yearMonth() const { return year*12 + month; }
void addMonths(int diff) { month += diff; year += month / 12; month %= 12; }
QDate date() const { return QDate(year, month + 1, day); }
};
int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const
{
if (recurs == rMonthlyPos && rMonthPositions.isEmpty()
|| recurs == rMonthlyDay && rMonthDays.isEmpty())
return 0;
MonthlyData data(this, mRecurStart.date());
switch (func) {
case END_DATE_AND_COUNT:
return monthlyCalcEndDate(enddate, data);
case COUNT_TO_DATE:
return monthlyCalcToDate(enddate, data);
case NEXT_AFTER_DATE:
return monthlyCalcNextAfter(enddate, data);
}
return 0;
}
int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const
{
uint countTogo = rDuration + mRecurExDatesCount;
int countGone = 0;
QValueList<int>::ConstIterator it;
const QValueList<int>* days = data.dayList();
if (data.day > 1) {
// Check what remains of the start month
for (it = days->begin(); it != days->end(); ++it) {
if (*it >= data.day) {
++countGone;
if (--countTogo == 0) {
data.day = *it;
break;
}
}
}
if (countTogo) {
data.day = 1;
data.addMonths(rFreq);
}
}
if (countTogo) {
if (data.varies) {
// The number of recurrence days varies from month to month,
// so we need to check month by month.
for ( ; ; ) {
days = data.dayList();
uint n = days->count(); // number of recurrence days in this month
if (n >= countTogo)
break;
countTogo -= n;
countGone += n;
data.addMonths(rFreq);
}
} else {
// The number of recurrences is the same every month,
// so skip the month-by-month check.
// Skip the remaining whole months, but leave at least
// 1 recurrence remaining, in order to get its date.
int daysPerMonth = days->count();
int wholeMonths = (countTogo - 1) / daysPerMonth;
data.addMonths(wholeMonths * rFreq);
countGone += wholeMonths * daysPerMonth;
countTogo -= wholeMonths * daysPerMonth;
}
if (countTogo) {
// Check the last month in the recurrence
for (it = days->begin(); it != days->end(); ++it) {
++countGone;
if (--countTogo == 0) {
data.day = *it;
break;
}
}
}
}
enddate = data.date();
return countGone;
}
int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const
{
int countGone = 0;
int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
int endYear = enddate.year();
int endMonth = enddate.month() - 1; // zero-based
int endDay = enddate.day();
int endYearMonth = endYear*12 + endMonth;
QValueList<int>::ConstIterator it;
const QValueList<int>* days = data.dayList();
if (data.day > 1) {
// Check what remains of the start month
for (it = days->begin(); it != days->end(); ++it) {
if (*it >= data.day) {
if (data.yearMonth() == endYearMonth && *it > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
}
data.day = 1;
data.addMonths(rFreq);
}
if (data.varies) {
// The number of recurrence days varies from month to month,
// so we need to check month by month.
while (data.yearMonth() < endYearMonth) {
countGone += data.dayList()->count();
if (countGone >= countMax)
return countMax;
data.addMonths(rFreq);
}
days = data.dayList();
} else {
// The number of recurrences is the same every month,
// so skip the month-by-month check.
// Skip the remaining whole months.
int daysPerMonth = days->count();
int wholeMonths = endYearMonth - data.yearMonth();
countGone += (wholeMonths / rFreq) * daysPerMonth;
if (countGone >= countMax)
return countMax;
if (wholeMonths % rFreq)
return countGone; // end year isn't a recurrence year
data.year = endYear;
data.month = endMonth;
}
// Check the last month in the recurrence
for (it = days->begin(); it != days->end(); ++it) {
if (*it > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
return countGone;
}
int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const
{
uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
int countGone = 0;
int endYear = enddate.year();
int endDay = enddate.day();
int endYearMonth = endYear*12 + enddate.month() - 1;
QValueList<int>::ConstIterator it;
const QValueList<int>* days = data.dayList();
if (data.day > 1) {
// Check what remains of the start month
for (it = days->begin(); it != days->end(); ++it) {
if (*it >= data.day) {
++countGone;
if (data.yearMonth() == endYearMonth && *it > endDay) {
data.day = *it;
goto ex;
}
if (--countTogo == 0)
return 0;
}
}
data.day = 1;
data.addMonths(rFreq);
}
if (data.varies) {
// The number of recurrence days varies from month to month,
// so we need to check month by month.
while (data.yearMonth() <= endYearMonth) {
days = data.dayList();
uint n = days->count(); // number of recurrence days in this month
if (data.yearMonth() == endYearMonth && days->last() > endDay)
break;
if (n >= countTogo)
return 0;
countGone += n;
countTogo -= n;
data.addMonths(rFreq);
}
days = data.dayList();
} else {
// The number of recurrences is the same every month,
// so skip the month-by-month check.
// Skip the remaining whole months to at least end year/month.
int daysPerMonth = days->count();
int elapsed = endYearMonth - data.yearMonth();
int recurMonths = (elapsed + rFreq - 1) / rFreq;
if (elapsed % rFreq == 0 && days->last() <= endDay)
++recurMonths; // required month is after endYearMonth
if (recurMonths) {
int n = recurMonths * daysPerMonth;
if (static_cast<uint>(n) > countTogo)
return 0; // reached end of recurrence
countTogo -= n;
countGone += n;
data.addMonths(recurMonths * rFreq);
}
}
// Check the last month in the recurrence
for (it = days->begin(); it != days->end(); ++it) {
++countGone;
if (data.yearMonth() > endYearMonth || *it > endDay) {
data.day = *it;
break;
}
if (--countTogo == 0)
return 0;
}
ex:
enddate = data.date();
return countGone;
}
/* Find count and, depending on 'func', the end date of an annual recurrence by date.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
struct Recurrence::YearlyMonthData {
const Recurrence *recurrence;
int year; // current year
int month; // current month 1..12
int day; // current day of month 1..31
bool leapyear; // true if February 29th recurs and current year is a leap year
bool feb29; // true if February 29th recurs
private:
QValueList<int> months; // recurring months in non-leap years 1..12
QValueList<int> leapMonths; // recurring months in leap years 1..12
public:
YearlyMonthData(const Recurrence* r, const QDate &date)
: recurrence(r), year(date.year()), month(date.month()), day(date.day())
{ feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths);
leapyear = feb29 && QDate::leapYear(year);
}
const QValueList<int>* monthList() const
{ return leapyear ? &leapMonths : &months; }
const QValueList<int>* leapMonthList() const { return &leapMonths; }
QDate date() const { return QDate(year, month, day); }
};
int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const
{
if (rYearNums.isEmpty())
return 0;
YearlyMonthData data(this, mRecurStart.date());
switch (func) {
case END_DATE_AND_COUNT:
return yearlyMonthCalcEndDate(enddate, data);
case COUNT_TO_DATE:
return yearlyMonthCalcToDate(enddate, data);
case NEXT_AFTER_DATE:
return yearlyMonthCalcNextAfter(enddate, data);
}
return 0;
}
// Find total count and end date of an annual recurrence by date.
// Reply = total number of occurrences.
int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const
{
uint countTogo = rDuration + mRecurExDatesCount;
int countGone = 0;
QValueList<int>::ConstIterator it;
const QValueList<int>* mons = data.monthList(); // get recurring months for this year
if (data.month > 1) {
// Check what remains of the start year
for (it = mons->begin(); it != mons->end(); ++it) {
if (*it >= data.month) {
++countGone;
if (--countTogo == 0) {
data.month = *it;
if (data.month == 2 && data.feb29 && !data.leapyear) {
// The recurrence should end on February 29th, but it's a non-leap year
switch (mFeb29YearlyType) {
case rFeb28:
data.day = 28;
break;
case rMar1:
data.month = 3;
data.day = 1;
break;
case rFeb29:
break;
}
}
break;
}
}
}
if (countTogo) {
data.month = 1;
data.year += rFreq;
}
}
if (countTogo) {
if (data.feb29 && mFeb29YearlyType == rFeb29) {
// The number of recurrences is different on leap years,
// so check year-by-year.
for ( ; ; ) {
mons = data.monthList();
uint n = mons->count();
if (n >= countTogo)
break;
countTogo -= n;
countGone += n;
data.year += rFreq;
}
} else {
// The number of recurrences is the same every year,
// so skip the year-by-year check.
// Skip the remaining whole years, but leave at least
// 1 recurrence remaining, in order to get its date.
int monthsPerYear = mons->count();
int wholeYears = (countTogo - 1) / monthsPerYear;
data.year += wholeYears * rFreq;
countGone += wholeYears * monthsPerYear;
countTogo -= wholeYears * monthsPerYear;
}
if (countTogo) {
// Check the last year in the recurrence
for (it = mons->begin(); it != mons->end(); ++it) {
++countGone;
if (--countTogo == 0) {
data.month = *it;
if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) {
// The recurrence should end on February 29th, but it's a non-leap year
switch (mFeb29YearlyType) {
case rFeb28:
data.day = 28;
break;
case rMar1:
data.month = 3;
data.day = 1;
break;
case rFeb29:
break;
}
}
break;
}
}
}
}
enddate = data.date();
return countGone;
}
// Find count of an annual recurrence by date.
// Reply = total number of occurrences up to 'enddate'.
int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const
{
int countGone = 0;
int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
int endYear = enddate.year();
int endMonth = enddate.month();
int endDay = enddate.day();
if (endDay < data.day) {
/* The end day of the month is earlier than the recurrence day of the month.
* If Feb 29th recurs and:
* 1) it recurs on Feb 28th in non-leap years, don't adjust the end month
* if enddate is Feb 28th on a non-leap year.
* 2) it recurs on Mar 1st in non-leap years, allow the end month to be
* adjusted to February, to simplify calculations.
*/
if (data.feb29 && !QDate::leapYear(endYear)
&& mFeb29YearlyType == rFeb28 && endDay == 28 && endMonth == 2) {
}
else if (--endMonth == 0) {
endMonth = 12;
--endYear;
}
}
QValueList<int>::ConstIterator it;
const QValueList<int>* mons = data.monthList();
if (data.month > 1) {
// Check what remains of the start year
for (it = mons->begin(); it != mons->end(); ++it) {
if (*it >= data.month) {
if (data.year == endYear && *it > endMonth)
return countGone;
if (++countGone >= countMax)
return countMax;
}
}
data.month = 1;
data.year += rFreq;
}
if (data.feb29 && mFeb29YearlyType == rFeb29) {
// The number of recurrences is different on leap years,
// so check year-by-year.
while (data.year < endYear) {
countGone += data.monthList()->count();
if (countGone >= countMax)
return countMax;
data.year += rFreq;
}
mons = data.monthList();
} else {
// The number of recurrences is the same every year,
// so skip the year-by-year check.
// Skip the remaining whole years.
int monthsPerYear = mons->count();
int wholeYears = endYear - data.year;
countGone += (wholeYears / rFreq) * monthsPerYear;
if (countGone >= countMax)
return countMax;
if (wholeYears % rFreq)
return countGone; // end year isn't a recurrence year
data.year = endYear;
}
// Check the last year in the recurrence
for (it = mons->begin(); it != mons->end(); ++it) {
if (*it > endMonth)
return countGone;
if (++countGone >= countMax)
return countMax;
}
return countGone;
}
// Find count and date of first recurrence after 'enddate' of an annual recurrence by date.
// Reply = total number of occurrences up to 'enddate'.
int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const
{
uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
int countGone = 0;
int endYear = enddate.year();
int endMonth = enddate.month();
int endDay = enddate.day();
bool mar1TooEarly = false;
bool feb28ok = false;
if (endDay < data.day) {
if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3)
mar1TooEarly = true;
if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28)
feb28ok = true;
else if (--endMonth == 0) {
endMonth = 12;
--endYear;
}
}
QValueList<int>::ConstIterator it;
const QValueList<int>* mons = data.monthList();
if (data.month > 1) {
// Check what remains of the start year
for (it = mons->begin(); it != mons->end(); ++it) {
if (*it >= data.month) {
++countGone;
if (data.year == endYear
&& ( *it > endMonth && (*it > 3 || !mar1TooEarly)
|| *it == 2 && feb28ok && data.leapyear)) {
if (*it == 2 && data.feb29 && !data.leapyear) {
// The next recurrence should be on February 29th, but it's a non-leap year
switch (mFeb29YearlyType) {
case rFeb28:
data.month = 2;
data.day = 28;
break;
case rMar1:
data.month = 3;
data.day = 1;
break;
case rFeb29: // impossible in this context!
break;
}
}
else
data.month = *it;
goto ex;
}
if (--countTogo == 0)
return 0;
}
}
data.month = 1;
data.year += rFreq;
}
if (data.feb29 && mFeb29YearlyType == rFeb29) {
// The number of recurrences is different on leap years,
// so check year-by-year.
while (data.year <= endYear) {
mons = data.monthList();
if (data.year == endYear && mons->last() > endMonth)
break;
uint n = mons->count();
if (n >= countTogo)
break;
countTogo -= n;
countGone += n;
data.year += rFreq;
}
mons = data.monthList();
} else {
// The number of recurrences is the same every year,
// so skip the year-by-year check.
// Skip the remaining whole years to at least endYear.
int monthsPerYear = mons->count();
int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
if ((endYear - data.year)%rFreq == 0
&& mons->last() <= endMonth)
++recurYears; // required year is after endYear
if (recurYears) {
int n = recurYears * monthsPerYear;
if (static_cast<uint>(n) > countTogo)
return 0; // reached end of recurrence
countTogo -= n;
countGone += n;
data.year += recurYears * rFreq;
}
}
// Check the last year in the recurrence
for (it = mons->begin(); it != mons->end(); ++it) {
++countGone;
if (data.year > endYear
|| ( *it > endMonth && (*it > 3 || !mar1TooEarly)
|| *it == 2 && feb28ok && QDate::leapYear(data.year))) {
if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) {
// The next recurrence should be on February 29th, but it's a non-leap year
switch (mFeb29YearlyType) {
case rFeb28:
data.month = 2;
data.day = 28;
break;
case rMar1:
data.month = 3;
data.day = 1;
break;
case rFeb29: // impossible in this context!
break;
}
}
else
data.month = *it;
break;
}
if (--countTogo == 0)
return 0;
}
ex:
enddate = data.date();
return countGone;
}
/* Find count and, depending on 'func', the end date of an annual recurrence by date.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
struct Recurrence::YearlyPosData {
const Recurrence *recurrence;
int year; // current year
int month; // current month 1..12
int day; // current day of month 1..31
int daysPerMonth; // number of days which recur each month, or -1 if variable
int count; // number of days which recur each year, or -1 if variable
bool varies; // true if number of days varies from year to year
private:
mutable QValueList<int> days;
public:
YearlyPosData(const Recurrence* r, const QDate &date)
: recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1)
{ if ((daysPerMonth = r->countMonthlyPosDays()) > 0)
count = daysPerMonth * r->rYearNums.count();
varies = (daysPerMonth < 0);
}
const QValueList<int>* dayList() const {
QDate startOfMonth(year, month, 1);
recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek());
return &days;
}
int yearMonth() const { return year*12 + month - 1; }
void addMonths(int diff) { month += diff - 1; year += month / 12; month = month % 12 + 1; }
QDate date() const { return QDate(year, month, day); }
};
int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const
{
if (rYearNums.isEmpty() || rMonthPositions.isEmpty())
return 0;
YearlyPosData data(this, mRecurStart.date());
switch (func) {
case END_DATE_AND_COUNT:
return yearlyPosCalcEndDate(enddate, data);
case COUNT_TO_DATE:
return yearlyPosCalcToDate(enddate, data);
case NEXT_AFTER_DATE:
return yearlyPosCalcNextAfter(enddate, data);
}
return 0;
}
int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const
{
uint countTogo = rDuration + mRecurExDatesCount;
int countGone = 0;
QValueList<int>::ConstIterator id;
const QValueList<int>* days;
if (data.month > 1 || data.day > 1) {
// Check what remains of the start year
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
if (*im.current() >= data.month) {
// Check what remains of the start month
if (data.day > 1 || data.varies
|| static_cast<uint>(data.daysPerMonth) >= countTogo) {
data.month = *im.current();
days = data.dayList();
for (id = days->begin(); id != days->end(); ++id) {
if (*id >= data.day) {
++countGone;
if (--countTogo == 0) {
data.month = *im.current();
data.day = *id;
goto ex;
}
}
}
data.day = 1;
} else {
// The number of days per month is constant, so skip
// the whole month.
countTogo -= data.daysPerMonth;
countGone += data.daysPerMonth;
}
}
}
data.month = 1;
data.year += rFreq;
}
if (data.varies) {
// The number of recurrences varies from year to year.
for ( ; ; ) {
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
data.month = *im.current();
days = data.dayList();
int n = days->count();
if (static_cast<uint>(n) >= countTogo) {
// Check the last month in the recurrence
for (id = days->begin(); id != days->end(); ++id) {
++countGone;
if (--countTogo == 0) {
data.day = *id;
goto ex;
}
}
}
countTogo -= n;
countGone += n;
}
data.year += rFreq;
}
} else {
// The number of recurrences is the same every year,
// so skip the year-by-year check.
// Skip the remaining whole years, but leave at least
// 1 recurrence remaining, in order to get its date.
int wholeYears = (countTogo - 1) / data.count;
data.year += wholeYears * rFreq;
countGone += wholeYears * data.count;
countTogo -= wholeYears * data.count;
// Check the last year in the recurrence.
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
if (static_cast<uint>(data.daysPerMonth) >= countTogo) {
// Check the last month in the recurrence
data.month = *im.current();
days = data.dayList();
for (id = days->begin(); id != days->end(); ++id) {
++countGone;
if (--countTogo == 0) {
data.day = *id;
goto ex;
}
}
}
countTogo -= data.daysPerMonth;
countGone += data.daysPerMonth;
}
data.year += rFreq;
}
ex:
enddate = data.date();
return countGone;
}
int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const
{
int countGone = 0;
int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
int endYear = enddate.year();
int endMonth = enddate.month();
int endDay = enddate.day();
if (endDay < data.day && --endMonth == 0) {
endMonth = 12;
--endYear;
}
int endYearMonth = endYear*12 + endMonth;
QValueList<int>::ConstIterator id;
const QValueList<int>* days;
if (data.month > 1 || data.day > 1) {
// Check what remains of the start year
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
if (*im.current() >= data.month) {
data.month = *im.current();
if (data.yearMonth() > endYearMonth)
return countGone;
// Check what remains of the start month
bool lastMonth = (data.yearMonth() == endYearMonth);
if (lastMonth || data.day > 1 || data.varies) {
days = data.dayList();
if (lastMonth || data.day > 1) {
for (id = days->begin(); id != days->end(); ++id) {
if (*id >= data.day) {
if (lastMonth && *id > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
}
} else {
countGone += days->count();
if (countGone >= countMax)
return countMax;
}
data.day = 1;
} else {
// The number of days per month is constant, so skip
// the whole month.
countGone += data.daysPerMonth;
if (countGone >= countMax)
return countMax;
}
}
}
data.month = 1;
data.year += rFreq;
}
if (data.varies) {
// The number of recurrences varies from year to year.
for ( ; ; ) {
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
data.month = *im.current();
days = data.dayList();
if (data.yearMonth() >= endYearMonth) {
if (data.yearMonth() > endYearMonth)
return countGone;
// Check the last month in the recurrence
for (id = days->begin(); id != days->end(); ++id) {
if (*id > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
} else {
countGone += days->count();
if (countGone >= countMax)
return countMax;
}
}
data.year += rFreq;
}
} else {
// The number of recurrences is the same every year,
// so skip the year-by-year check.
// Skip the remaining whole years, but leave at least
// 1 recurrence remaining, in order to get its date.
int wholeYears = endYear - data.year;
countGone += (wholeYears / rFreq) * data.count;
if (countGone >= countMax)
return countMax;
if (wholeYears % rFreq)
return countGone; // end year isn't a recurrence year
data.year = endYear;
// Check the last year in the recurrence.
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
data.month = *im.current();
if (data.month >= endMonth) {
if (data.month > endMonth)
return countGone;
// Check the last month in the recurrence
days = data.dayList();
for (id = days->begin(); id != days->end(); ++id) {
if (*id > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
} else {
countGone += data.daysPerMonth;
if (countGone >= countMax)
return countMax;
}
}
}
return countGone;
}
int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const
{
uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
int countGone = 0;
int endYear = enddate.year();
int endMonth = enddate.month();
int endDay = enddate.day();
if (endDay < data.day && --endMonth == 0) {
endMonth = 12;
--endYear;
}
int endYearMonth = endYear*12 + endMonth;
QValueList<int>::ConstIterator id;
const QValueList<int>* days;
if (data.varies) {
// The number of recurrences varies from year to year.
for ( ; ; ) {
// Check the next year
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
if (*im.current() >= data.month) {
// Check the next month
data.month = *im.current();
int ended = data.yearMonth() - endYearMonth;
days = data.dayList();
if (ended >= 0 || data.day > 1) {
// This is the start or end month, so check each day
for (id = days->begin(); id != days->end(); ++id) {
if (*id >= data.day) {
++countGone;
if (ended > 0 || (ended == 0 && *id > endDay)) {
data.day = *id;
goto ex;
}
if (--countTogo == 0)
return 0;
}
}
} else {
// Skip the whole month
uint n = days->count();
if (n >= countTogo)
return 0;
countGone += n;
}
data.day = 1; // we've checked the start month now
}
}
data.month = 1; // we've checked the start year now
data.year += rFreq;
}
} else {
// The number of recurrences is the same every year.
if (data.month > 1 || data.day > 1) {
// Check what remains of the start year
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
if (*im.current() >= data.month) {
// Check what remains of the start month
data.month = *im.current();
int ended = data.yearMonth() - endYearMonth;
if (ended >= 0 || data.day > 1) {
// This is the start or end month, so check each day
days = data.dayList();
for (id = days->begin(); id != days->end(); ++id) {
if (*id >= data.day) {
++countGone;
if (ended > 0 || (ended == 0 && *id > endDay)) {
data.day = *id;
goto ex;
}
if (--countTogo == 0)
return 0;
}
}
data.day = 1; // we've checked the start month now
} else {
// Skip the whole month.
if (static_cast<uint>(data.daysPerMonth) >= countTogo)
return 0;
countGone += data.daysPerMonth;
}
}
}
data.year += rFreq;
}
// Skip the remaining whole years to at least endYear.
int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
if ((endYear - data.year)%rFreq == 0
&& *rYearNums.getLast() <= endMonth)
++recurYears; // required year is after endYear
if (recurYears) {
int n = recurYears * data.count;
if (static_cast<uint>(n) > countTogo)
return 0; // reached end of recurrence
countTogo -= n;
countGone += n;
data.year += recurYears * rFreq;
}
// Check the last year in the recurrence
for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
data.month = *im.current();
int ended = data.yearMonth() - endYearMonth;
if (ended >= 0) {
// This is the end month, so check each day
days = data.dayList();
for (id = days->begin(); id != days->end(); ++id) {
++countGone;
if (ended > 0 || (ended == 0 && *id > endDay)) {
data.day = *id;
goto ex;
}
if (--countTogo == 0)
return 0;
}
} else {
// Skip the whole month.
if (static_cast<uint>(data.daysPerMonth) >= countTogo)
return 0;
countGone += data.daysPerMonth;
}
}
}
ex:
enddate = data.date();
return countGone;
}
/* Find count and, depending on 'func', the end date of an annual recurrence by day.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
struct Recurrence::YearlyDayData {
int year; // current year
int day; // current day of year 1..366
bool varies; // true if day 366 recurs
private:
int daycount;
public:
YearlyDayData(const Recurrence* r, const QDate &date)
: year(date.year()), day(date.dayOfYear()), varies(*r->rYearNums.getLast() == 366),
daycount(r->rYearNums.count()) { }
bool leapYear() const { return QDate::leapYear(year); }
int dayCount() const { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); }
bool isMaxDayCount() const { return !varies || QDate::leapYear(year); }
QDate date() const { return QDate(year, 1, 1).addDays(day - 1); }
};
int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const
{
if (rYearNums.isEmpty())
return 0;
YearlyDayData data(this, mRecurStart.date());
switch (func) {
case END_DATE_AND_COUNT:
return yearlyDayCalcEndDate(enddate, data);
case COUNT_TO_DATE:
return yearlyDayCalcToDate(enddate, data);
case NEXT_AFTER_DATE:
return yearlyDayCalcNextAfter(enddate, data);
}
return 0;
}
int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const
{
uint countTogo = rDuration + mRecurExDatesCount;
int countGone = 0;
if (data.day > 1) {
// Check what remains of the start year
bool leapOK = data.isMaxDayCount();
for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
int d = *it.current();
if (d >= data.day && (leapOK || d < 366)) {
++countGone;
if (--countTogo == 0) {
data.day = d;
goto ex;
}
}
}
data.day = 1;
data.year += rFreq;
}
if (data.varies) {
// The number of recurrences is different in leap years,
// so check year-by-year.
for ( ; ; ) {
uint n = data.dayCount();
if (n >= countTogo)
break;
countTogo -= n;
countGone += n;
data.year += rFreq;
}
} else {
// The number of recurrences is the same every year,
// so skip the year-by-year check.
// Skip the remaining whole years, but leave at least
// 1 recurrence remaining, in order to get its date.
int daysPerYear = rYearNums.count();
int wholeYears = (countTogo - 1) / daysPerYear;
data.year += wholeYears * rFreq;
countGone += wholeYears * daysPerYear;
countTogo -= wholeYears * daysPerYear;
}
if (countTogo) {
// Check the last year in the recurrence
for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
++countGone;
if (--countTogo == 0) {
data.day = *it.current();
break;
}
}
}
ex:
enddate = data.date();
return countGone;
}
int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const
{
int countGone = 0;
int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
int endYear = enddate.year();
int endDay = enddate.dayOfYear();
if (data.day > 1) {
// Check what remains of the start year
bool leapOK = data.isMaxDayCount();
for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
int d = *it.current();
if (d >= data.day && (leapOK || d < 366)) {
if (data.year == endYear && d > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
}
data.day = 1;
data.year += rFreq;
}
if (data.varies) {
// The number of recurrences is different in leap years,
// so check year-by-year.
while (data.year < endYear) {
uint n = data.dayCount();
countGone += n;
if (countGone >= countMax)
return countMax;
data.year += rFreq;
}
if (data.year > endYear)
return countGone;
} else {
// The number of recurrences is the same every year.
// Skip the remaining whole years.
int wholeYears = endYear - data.year;
countGone += (wholeYears / rFreq) * rYearNums.count();
if (countGone >= countMax)
return countMax;
if (wholeYears % rFreq)
return countGone; // end year isn't a recurrence year
data.year = endYear;
}
if (data.year <= endYear) {
// Check the last year in the recurrence
for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
if (*it.current() > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
}
return countGone;
}
int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const
{
uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
int countGone = 0;
int endYear = enddate.year();
int endDay = enddate.dayOfYear();
if (data.day > 1) {
// Check what remains of the start year
bool leapOK = data.isMaxDayCount();
for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
int d = *it.current();
if (d >= data.day && (leapOK || d < 366)) {
++countGone;
if (data.year == endYear && d > endDay) {
data.day = d;
goto ex;
}
if (--countTogo == 0)
return 0;
}
}
data.day = 1;
data.year += rFreq;
}
if (data.varies) {
// The number of recurrences is different in leap years,
// so check year-by-year.
while (data.year <= endYear) {
uint n = data.dayCount();
if (data.year == endYear && *rYearNums.getLast() > endDay)
break;
if (n >= countTogo)
break;
countTogo -= n;
countGone += n;
data.year += rFreq;
}
} else {
// The number of recurrences is the same every year,
// so skip the year-by-year check.
// Skip the remaining whole years to at least endYear.
int daysPerYear = rYearNums.count();
int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
if ((endYear - data.year)%rFreq == 0
&& *rYearNums.getLast() <= endDay)
++recurYears; // required year is after endYear
if (recurYears) {
int n = recurYears * daysPerYear;
if (static_cast<uint>(n) > countTogo)
return 0; // reached end of recurrence
countTogo -= n;
countGone += n;
data.year += recurYears * rFreq;
}
}
// Check the last year in the recurrence
for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
++countGone;
int d = *it.current();
if (data.year > endYear || d > endDay) {
data.day = d;
break;
}
if (--countTogo == 0)
return 0;
}
ex:
enddate = data.date();
return countGone;
}
// Get the days in this month which recur, in numerical order.
// Parameters: daysInMonth = number of days in this month
// startDayOfWeek = day of week for first day of month.
void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const
{
list.clear();
int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1;
// Go through the list, compiling a bit list of actual day numbers
Q_UINT32 days = 0;
for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
int weeknum = pos.current()->rPos - 1; // get 0-based week number
QBitArray &rdays = pos.current()->rDays;
if (pos.current()->negative) {
// nth days before the end of the month
for (uint i = 1; i <= 7; ++i) {
if (rdays.testBit(i - 1)) {
int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7;
if (day > 0)
days |= 1 << (day - 1);
}
}
} else {
// nth days after the start of the month
for (uint i = 1; i <= 7; ++i) {
if (rdays.testBit(i - 1)) {
int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7;
if (day <= daysInMonth)
days |= 1 << (day - 1);
}
}
}
}
// Compile the ordered list
Q_UINT32 mask = 1;
for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
if (days & mask)
list.append(i + 1);
}
}
// Get the number of days in the month which recur.
// Reply = -1 if the number varies from month to month.
int Recurrence::countMonthlyPosDays() const
{
int count = 0;
Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 };
Q_UINT8 negative[4] = { 0, 0, 0, 0 };
for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
int weeknum = pos.current()->rPos;
Q_UINT8* wk;
if (pos.current()->negative) {
// nth days before the end of the month
if (weeknum > 4)
return -1; // days in 5th week are often missing
wk = &negative[4 - weeknum];
} else {
// nth days after the start of the month
if (weeknum > 4)
return -1; // days in 5th week are often missing
wk = &positive[weeknum - 1];
}
QBitArray &rdays = pos.current()->rDays;
for (uint i = 0; i < 7; ++i) {
if (rdays.testBit(i)) {
++count;
*wk |= (1 << i);
}
}
}
// Check for any possible days which could be duplicated by
// a positive and a negative position.
for (int i = 0; i < 4; ++i) {
if (negative[i] & (positive[i] | positive[i+1]))
return -1;
}
return count;
}
// Get the days in this month which recur, in numerical order.
// Reply = true if day numbers varies from month to month.
bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const
{
list.clear();
bool variable = false;
Q_UINT32 days = 0;
for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
int day = *it.current();
if (day > 0) {
// date in the month
if (day <= daysInMonth)
days |= 1 << (day - 1);
if (day > 28 && day <= 31)
variable = true; // this date does not appear in some months
} else if (day < 0) {
// days before the end of the month
variable = true; // this date varies depending on the month length
day = daysInMonth + day; // zero-based day of month
if (day >= 0)
days |= 1 << day;
}
}
// Compile the ordered list
Q_UINT32 mask = 1;
for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
if (days & mask)
list.append(i + 1);
}
return variable;
}
// Get the months which recur, in numerical order, for both leap years and non-leap years.
// N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is
// included in the non-leap year month list.
// Reply = true if February 29th also recurs.
bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const
{
list.clear();
leaplist.clear();
bool feb29 = false;
for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
int month = *it.current();
if (month == 2) {
if (day <= 28) {
list.append(month); // date appears in February
leaplist.append(month);
}
else if (day == 29) {
// February 29th
leaplist.append(month);
switch (mFeb29YearlyType) {
case rFeb28:
case rMar1:
list.append(2);
break;
case rFeb29:
break;
}
feb29 = true;
}
}
else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) {
list.append(month); // date appears in every month
leaplist.append(month);
}
}
return feb29;
}
/* From the recurrence day of the week list, get the earliest day in the
* specified week which is >= the startDay.
* Parameters: startDay = 1..7 (Monday..Sunday)
* useWeekStart = true to end search at day before next rWeekStart
* = false to search for a full 7 days
* Reply = day of the week (1..7), or 0 if none found.
*/
int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const
{
int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7;
for (int i = startDay - 1; ; i = (i + 1)%7) {
if (rDays.testBit(i))
return i + 1;
if (i == last)
return 0;
}
}
/* From the recurrence day of the week list, get the latest day in the
* specified week which is <= the endDay.
* Parameters: endDay = 1..7 (Monday..Sunday)
* useWeekStart = true to end search at rWeekStart
* = false to search for a full 7 days
* Reply = day of the week (1..7), or 0 if none found.
*/
int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const
{
int last = useWeekStart ? rWeekStart - 1 : endDay%7;
for (int i = endDay - 1; ; i = (i + 6)%7) {
if (rDays.testBit(i))
return i + 1;
if (i == last)
return 0;
}
}
/* From the recurrence monthly day number list or monthly day of week/week of
* month list, get the earliest day in the specified month which is >= the
* earliestDate.
*/
QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const
{
int earliestDay = earliestDate.day();
int daysInMonth = earliestDate.daysInMonth();
switch (recurs) {
case rMonthlyDay: {
int minday = daysInMonth + 1;
for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
int day = *it.current();
if (day < 0)
day = daysInMonth + day + 1;
if (day >= earliestDay && day < minday)
minday = day;
}
if (minday <= daysInMonth)
return earliestDate.addDays(minday - earliestDay);
break;
}
case rMonthlyPos:
case rYearlyPos: {
QDate monthBegin(earliestDate.addDays(1 - earliestDay));
QValueList<int> dayList;
getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) {
if (*id >= earliestDay)
return monthBegin.addDays(*id - 1);
}
break;
}
}
return QDate();
}
/* From the recurrence monthly day number list or monthly day of week/week of
* month list, get the latest day in the specified month which is <= the
* latestDate.
*/
QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const
{
int latestDay = latestDate.day();
int daysInMonth = latestDate.daysInMonth();
switch (recurs) {
case rMonthlyDay: {
int maxday = -1;
for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
int day = *it.current();
if (day < 0)
day = daysInMonth + day + 1;
if (day <= latestDay && day > maxday)
maxday = day;
}
if (maxday > 0)
return QDate(latestDate.year(), latestDate.month(), maxday);
break;
}
case rMonthlyPos:
case rYearlyPos: {
QDate monthBegin(latestDate.addDays(1 - latestDay));
QValueList<int> dayList;
getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) {
if (*id <= latestDay)
return monthBegin.addDays(*id - 1);
}
break;
}
}
return QDate();
}
/* From the recurrence yearly month list or yearly day list, get the earliest
* month or day in the specified year which is >= the earliestDate.
* Note that rYearNums is sorted in numerical order.
*/
QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const
{
QPtrListIterator<int> it(rYearNums);
switch (recurs) {
case rYearlyMonth: {
int day = recurStart().date().day();
int earliestYear = earliestDate.year();
int earliestMonth = earliestDate.month();
int earliestDay = earliestDate.day();
if (earliestDay > day) {
// The earliest date is later in the month than the recurrence date,
// so skip to the next month before starting to check
if (++earliestMonth > 12)
return QDate();
}
for ( ; it.current(); ++it) {
int month = *it.current();
if (month >= earliestMonth) {
if (day <= 28 || QDate::isValid(earliestYear, month, day))
return QDate(earliestYear, month, day);
if (day == 29 && month == 2) {
// It's a recurrence on February 29th, in a non-leap year
switch (mFeb29YearlyType) {
case rMar1:
return QDate(earliestYear, 3, 1);
case rFeb28:
if (earliestDay <= 28)
return QDate(earliestYear, 2, 28);
break;
case rFeb29:
break;
}
}
}
}
break;
}
case rYearlyPos: {
QValueList<int> dayList;
int earliestYear = earliestDate.year();
int earliestMonth = earliestDate.month();
int earliestDay = earliestDate.day();
for ( ; it.current(); ++it) {
int month = *it.current();
if (month >= earliestMonth) {
QDate monthBegin(earliestYear, month, 1);
getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek());
for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) {
if (*id >= earliestDay)
return monthBegin.addDays(*id - 1);
}
earliestDay = 1;
}
}
break;
}
case rYearlyDay: {
int earliestDay = earliestDate.dayOfYear();
for ( ; it.current(); ++it) {
int day = *it.current();
if (day >= earliestDay && (day <= 365 || day <= earliestDate.daysInYear()))
return earliestDate.addDays(day - earliestDay);
}
break;
}
}
return QDate();
}
/* From the recurrence yearly month list or yearly day list, get the latest
* month or day in the specified year which is <= the latestDate.
* Note that rYearNums is sorted in numerical order.
*/
QDate Recurrence::getLastDateInYear(const QDate &latestDate) const
{
QPtrListIterator<int> it(rYearNums);
switch (recurs) {
case rYearlyMonth: {
int day = recurStart().date().day();
int latestYear = latestDate.year();
int latestMonth = latestDate.month();
if (latestDate.day() > day) {
// The latest date is earlier in the month than the recurrence date,
// so skip to the previous month before starting to check
if (--latestMonth <= 0)
return QDate();
}
for (it.toLast(); it.current(); --it) {
int month = *it.current();
if (month <= latestMonth) {
if (day <= 28 || QDate::isValid(latestYear, month, day))
return QDate(latestYear, month, day);
if (day == 29 && month == 2) {
// It's a recurrence on February 29th, in a non-leap year
switch (mFeb29YearlyType) {
case rMar1:
if (latestMonth >= 3)
return QDate(latestYear, 3, 1);
break;
case rFeb28:
return QDate(latestYear, 2, 28);
case rFeb29:
break;
}
}
}
}
break;
}
case rYearlyPos: {
QValueList<int> dayList;
int latestYear = latestDate.year();
int latestMonth = latestDate.month();
int latestDay = latestDate.day();
for (it.toLast(); it.current(); --it) {
int month = *it.current();
if (month <= latestMonth) {
QDate monthBegin(latestYear, month, 1);
getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek());
for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) {
if (*id <= latestDay)
return monthBegin.addDays(*id - 1);
}
latestDay = 31;
}
}
break;
}
case rYearlyDay: {
int latestDay = latestDate.dayOfYear();
for (it.toLast(); it.current(); --it) {
int day = *it.current();
if (day <= latestDay)
return latestDate.addDays(day - latestDay);
}
break;
}
}
return QDate();
}
void Recurrence::dump() const
{
- kdDebug() << "Recurrence::dump():" << endl;
-
- kdDebug() << " type: " << recurs << endl;
-
- kdDebug() << " rDays: " << endl;
- int i;
- for( i = 0; i < 7; ++i ) {
- kdDebug() << " " << i << ": "
- << ( rDays.testBit( i ) ? "true" : "false" ) << endl;
- }
+ ;
}