-rw-r--r-- | korganizer/koeditorrecurrence.cpp | 95 | ||||
-rw-r--r-- | korganizer/koeditorrecurrence.h | 9 | ||||
-rw-r--r-- | libkcal/icalformatimpl.cpp | 8 | ||||
-rw-r--r-- | libkcal/recurrence.cpp | 17 | ||||
-rw-r--r-- | libkcal/recurrence.h | 6 |
5 files changed, 89 insertions, 46 deletions
diff --git a/korganizer/koeditorrecurrence.cpp b/korganizer/koeditorrecurrence.cpp index 98356fe..ffc0fac 100644 --- a/korganizer/koeditorrecurrence.cpp +++ b/korganizer/koeditorrecurrence.cpp @@ -1,1116 +1,1139 @@ /* This file is part of KOrganizer. Copyright (c) 2000-2003 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. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include <qtooltip.h> #include <qfiledialog.h> #include <qlayout.h> #include <qvbox.h> #include <qbuttongroup.h> #include <qvgroupbox.h> #include <qwidgetstack.h> #include <qdatetime.h> #include <qlistbox.h> #include <qspinbox.h> #include <qcheckbox.h> #include <qapplication.h> #include <kdialog.h> #include <kglobal.h> #include <klocale.h> #include <kiconloader.h> #include <kdebug.h> #include <knumvalidator.h> #include <libkcal/event.h> #include <libkdepim/kdateedit.h> #include "koprefs.h" #include "koeditorrecurrence.h" /////////////////////////// RecurBase /////////////////////////////// RecurBase::RecurBase( QWidget *parent, const char *name ) : QWidget( parent, name ) { mFrequencyEdit = new QSpinBox( 1, 9999, 1, this ); mFrequencyEdit->setValue( 1 ); } QWidget *RecurBase::frequencyEdit() { return mFrequencyEdit; } void RecurBase::setFrequency( int f ) { if ( f < 1 ) f = 1; mFrequencyEdit->setValue( f ); } int RecurBase::frequency() { return mFrequencyEdit->value(); } /////////////////////////// RecurDaily /////////////////////////////// RecurDaily::RecurDaily( QWidget *parent, const char *name ) : RecurBase( parent, name ) { QBoxLayout *topLayout = new QHBoxLayout( this ); topLayout->setSpacing( KDialog::spacingHint() ); QLabel *preLabel = new QLabel( i18n("Recur every"), this ); topLayout->addWidget( preLabel ); topLayout->addWidget( frequencyEdit() ); QLabel *postLabel = new QLabel( i18n("day(s)"), this ); topLayout->addWidget( postLabel ); } /////////////////////////// RecurWeekly /////////////////////////////// RecurWeekly::RecurWeekly( QWidget *parent, const char *name ) : RecurBase( parent, name ) { QBoxLayout *topLayout = new QVBoxLayout( this ); topLayout->setSpacing( KDialog::spacingHint() ); topLayout->addStretch( 1 ); QBoxLayout *weeksLayout = new QHBoxLayout( topLayout ); QLabel *preLabel = new QLabel( i18n("Recur every"), this ); weeksLayout->addWidget( preLabel ); weeksLayout->addWidget( frequencyEdit() ); QLabel *postLabel = new QLabel( i18n("week(s) on:"), this ); weeksLayout->addWidget( postLabel ); QHBox *dayBox = new QHBox( this ); topLayout->addWidget( dayBox, 1, AlignVCenter ); // TODO: Respect start of week setting for ( int i = 0; i < 7; ++i ) { QString weekDayName = KGlobal::locale()->weekDayName( i + 1, true ); if ( KOPrefs::instance()->mCompactDialogs ) { weekDayName = weekDayName.left( 1 ); } mDayBoxes[ i ] = new QCheckBox( weekDayName, dayBox ); } topLayout->addStretch( 1 ); } void RecurWeekly::setDays( const QBitArray &days ) { for ( int i = 0; i < 7; ++i ) { mDayBoxes[ i ]->setChecked( days.testBit( i ) ); } } QBitArray RecurWeekly::days() { QBitArray days( 7 ); for ( int i = 0; i < 7; ++i ) { days.setBit( i, mDayBoxes[ i ]->isChecked() ); } return days; } /////////////////////////// RecurMonthly /////////////////////////////// RecurMonthly::RecurMonthly( QWidget *parent, const char *name ) : RecurBase( parent, name ) { QBoxLayout *topLayout = new QVBoxLayout( this ); topLayout->setSpacing( KDialog::spacingHint() ); QBoxLayout *freqLayout = new QHBoxLayout( topLayout ); QLabel *preLabel = new QLabel( i18n("every"), this ); freqLayout->addWidget( preLabel ); freqLayout->addWidget( frequencyEdit() ); QLabel *postLabel = new QLabel( i18n("month(s)"), this ); freqLayout->addWidget( postLabel ); QButtonGroup *buttonGroup = new QButtonGroup( this ); buttonGroup->setFrameStyle( QFrame::NoFrame ); topLayout->addWidget( buttonGroup, 1, AlignVCenter ); QGridLayout *buttonLayout = new QGridLayout( buttonGroup, 3, 2 ); buttonLayout->setSpacing( KDialog::spacingHint() ); QString recurOnText; if ( !KOPrefs::instance()->mCompactDialogs ) { recurOnText = i18n("Recur on the"); } mByDayRadio = new QRadioButton( recurOnText, buttonGroup ); buttonLayout->addWidget( mByDayRadio, 0, 0 ); mByDayCombo = new QComboBox( buttonGroup ); mByDayCombo->setSizeLimit( 7 ); mByDayCombo->insertItem( i18n("1st") ); mByDayCombo->insertItem( i18n("2nd") ); mByDayCombo->insertItem( i18n("3rd") ); mByDayCombo->insertItem( i18n("4th") ); mByDayCombo->insertItem( i18n("5th") ); mByDayCombo->insertItem( i18n("6th") ); mByDayCombo->insertItem( i18n("7th") ); mByDayCombo->insertItem( i18n("8th") ); mByDayCombo->insertItem( i18n("9th") ); mByDayCombo->insertItem( i18n("10th") ); mByDayCombo->insertItem( i18n("11th") ); mByDayCombo->insertItem( i18n("12th") ); mByDayCombo->insertItem( i18n("13th") ); mByDayCombo->insertItem( i18n("14th") ); mByDayCombo->insertItem( i18n("15th") ); mByDayCombo->insertItem( i18n("16th") ); mByDayCombo->insertItem( i18n("17th") ); mByDayCombo->insertItem( i18n("18th") ); mByDayCombo->insertItem( i18n("19th") ); mByDayCombo->insertItem( i18n("20th") ); mByDayCombo->insertItem( i18n("21st") ); mByDayCombo->insertItem( i18n("22nd") ); mByDayCombo->insertItem( i18n("23rd") ); mByDayCombo->insertItem( i18n("24th") ); mByDayCombo->insertItem( i18n("25th") ); mByDayCombo->insertItem( i18n("26th") ); mByDayCombo->insertItem( i18n("27th") ); mByDayCombo->insertItem( i18n("28th") ); mByDayCombo->insertItem( i18n("29th") ); mByDayCombo->insertItem( i18n("30th") ); mByDayCombo->insertItem( i18n("31st") ); buttonLayout->addWidget( mByDayCombo, 0, 1 ); QLabel *byDayLabel = new QLabel( i18n("day"), buttonGroup ); buttonLayout->addWidget( byDayLabel, 0, 2 ); mByPosRadio = new QRadioButton( recurOnText, buttonGroup); buttonLayout->addWidget( mByPosRadio, 1, 0 ); mByPosCountCombo = new QComboBox( buttonGroup ); mByPosCountCombo->insertItem( i18n("1st") ); mByPosCountCombo->insertItem( i18n("2nd") ); mByPosCountCombo->insertItem( i18n("3rd") ); mByPosCountCombo->insertItem( i18n("4th") ); mByPosCountCombo->insertItem( i18n("5th") ); buttonLayout->addWidget( mByPosCountCombo, 1, 1 ); mByPosWeekdayCombo = new QComboBox( buttonGroup ); mByPosWeekdayCombo->insertItem( i18n("Monday") ); mByPosWeekdayCombo->insertItem( i18n("Tuesday") ); mByPosWeekdayCombo->insertItem( i18n("Wednesday") ); mByPosWeekdayCombo->insertItem( i18n("Thursday") ); mByPosWeekdayCombo->insertItem( i18n("Friday") ); mByPosWeekdayCombo->insertItem( i18n("Saturday") ); mByPosWeekdayCombo->insertItem( i18n("Sunday") ); buttonLayout->addWidget( mByPosWeekdayCombo, 1, 2 ); } void RecurMonthly::setByDay( int day ) { mByDayRadio->setChecked( true ); mByDayCombo->setCurrentItem( day ); } void RecurMonthly::setByPos( int count, int weekday ) { mByPosRadio->setChecked( true ); mByPosCountCombo->setCurrentItem( count ); mByPosWeekdayCombo->setCurrentItem( weekday ); } bool RecurMonthly::byDay() { return mByDayRadio->isChecked(); } bool RecurMonthly::byPos() { return mByPosRadio->isChecked(); } int RecurMonthly::day() { return mByDayCombo->currentItem() + 1; } int RecurMonthly::count() { return mByPosCountCombo->currentItem() + 1; } int RecurMonthly::weekday() { return mByPosWeekdayCombo->currentItem(); } /////////////////////////// RecurYearly /////////////////////////////// RecurYearly::RecurYearly( QWidget *parent, const char *name ) : RecurBase( parent, name ) { QBoxLayout *topLayout = new QVBoxLayout( this ); topLayout->setSpacing( KDialog::spacingHint() ); QBoxLayout *freqLayout = new QHBoxLayout( topLayout ); QLabel *preLabel = new QLabel( i18n("every"), this ); freqLayout->addWidget( preLabel ); freqLayout->addWidget( frequencyEdit() ); QLabel *postLabel = new QLabel( i18n("year(s)"), this ); freqLayout->addWidget( postLabel ); QButtonGroup *buttonGroup = new QButtonGroup( this ); buttonGroup->setFrameStyle( QFrame::NoFrame ); topLayout->addWidget( buttonGroup, 1, AlignVCenter ); - QGridLayout *buttonLayout = new QGridLayout( buttonGroup, 3, 2 ); + QGridLayout *buttonLayout = new QGridLayout( buttonGroup, 2, 3 ); - QString recurInMonthText; - if ( !KOPrefs::instance()->mCompactDialogs ) { - recurInMonthText = i18n("Recur in the month of"); - } - - mByMonthRadio = new QRadioButton( recurInMonthText, buttonGroup); - buttonLayout->addWidget( mByMonthRadio, 0, 0 ); + mByMonthRadio = new QRadioButton( i18n("On day "), buttonGroup); + buttonLayout->addWidget( mByMonthRadio, 0, 0 , Qt::AlignRight); + mByDayLabel = new QLabel( i18n(" 1 of "), buttonGroup ); + buttonLayout->addWidget( mByDayLabel, 0, 1 ); mByMonthCombo = new QComboBox( buttonGroup ); mByMonthCombo->insertItem( i18n("January") ); mByMonthCombo->insertItem( i18n("February") ); mByMonthCombo->insertItem( i18n("March") ); mByMonthCombo->insertItem( i18n("April") ); mByMonthCombo->insertItem( i18n("May") ); mByMonthCombo->insertItem( i18n("June") ); mByMonthCombo->insertItem( i18n("July") ); mByMonthCombo->insertItem( i18n("August") ); mByMonthCombo->insertItem( i18n("September") ); mByMonthCombo->insertItem( i18n("October") ); mByMonthCombo->insertItem( i18n("November") ); mByMonthCombo->insertItem( i18n("December") ); - buttonLayout->addWidget( mByMonthCombo, 0, 1 ); - + buttonLayout->addWidget( mByMonthCombo, 0, 2,Qt::AlignLeft ); + if ( QApplication::desktop()->width() <= 640 ) { mByMonthCombo->setSizeLimit( 6 ); - - buttonLayout->setRowStretch( 1, 1 ); - - QString recurOnDayText; - if ( KOPrefs::instance()->mCompactDialogs ) { - recurOnDayText = i18n("This day"); - } else { - recurOnDayText = i18n("Recur on this day"); } - mByDayRadio = new QRadioButton( recurOnDayText, buttonGroup); - buttonLayout->addMultiCellWidget( mByDayRadio, 2, 2, 0, 1 ); + mByDayRadio = new QRadioButton( i18n("On day "), buttonGroup); + buttonLayout->addWidget( mByDayRadio, 1, 0 , Qt::AlignRight); + mDayOfLabel = new QLabel( i18n("1 of the year"), buttonGroup ); + buttonLayout->addMultiCellWidget( mDayOfLabel, 1, 1, 1,3 ); + } -void RecurYearly::setByDay() +void RecurYearly::setByDay( int doy ) { mByDayRadio->setChecked( true ); + mDayOfLabel->setText(i18n("%1 of the year").arg( doy ) ); } -void RecurYearly::setByMonth( int month ) +void RecurYearly::setByMonth( int month, int day ) { mByMonthRadio->setChecked( true ); mByMonthCombo->setCurrentItem( month - 1 ); + mByDayLabel->setText(i18n("%1 of ").arg( day ) ); + mDay = day; } bool RecurYearly::byMonth() { return mByMonthRadio->isChecked(); } bool RecurYearly::byDay() { return mByDayRadio->isChecked(); } int RecurYearly::month() { return mByMonthCombo->currentItem() + 1; } +int RecurYearly::day() +{ + return mDay;//mByDayCombo->currentItem() + 1; +} //////////////////////////// ExceptionsWidget ////////////////////////// ExceptionsWidget::ExceptionsWidget( QWidget *parent, const char *name ) : QWidget( parent, name ) { QBoxLayout *topLayout = new QVBoxLayout( this ); QGroupBox *groupBox = new QGroupBox( 1, Horizontal, i18n("Exceptions"), this ); topLayout->addWidget( groupBox ); QWidget *box = new QWidget( groupBox ); QGridLayout *boxLayout = new QGridLayout( box ); mExceptionDateEdit = new KDateEdit( box ); boxLayout->addWidget( mExceptionDateEdit, 0, 0 ); QPushButton *addExceptionButton = new QPushButton( i18n("Add"), box ); boxLayout->addWidget( addExceptionButton, 1, 0 ); QPushButton *changeExceptionButton = new QPushButton( i18n("Change"), box ); boxLayout->addWidget( changeExceptionButton, 2, 0 ); QPushButton *deleteExceptionButton = new QPushButton( i18n("Delete"), box ); boxLayout->addWidget( deleteExceptionButton, 3, 0 ); mExceptionList = new QListBox( box ); boxLayout->addMultiCellWidget( mExceptionList, 0, 3, 1, 1 ); boxLayout->setRowStretch( 4, 1 ); boxLayout->setColStretch( 1, 3 ); connect( addExceptionButton, SIGNAL( clicked() ), SLOT( addException() ) ); connect( changeExceptionButton, SIGNAL( clicked() ), SLOT( changeException() ) ); connect( deleteExceptionButton, SIGNAL( clicked() ), SLOT( deleteException() ) ); if ( QApplication::desktop()->width() < 480 ) { setMinimumWidth( 220 ); } else { setMinimumWidth( 440 ); mExceptionDateEdit->setMinimumWidth( 200 ); } } void ExceptionsWidget::setDefaults( const QDateTime &from ) { mExceptionDateEdit->setDate( from.date() ); } void ExceptionsWidget::addException() { QDate date = mExceptionDateEdit->date(); QString dateStr = KGlobal::locale()->formatDate( date ); if( !mExceptionList->findItem( dateStr ) ) { mExceptionDates.append( date ); mExceptionList->insertItem( dateStr ); } } void ExceptionsWidget::changeException() { int pos = mExceptionList->currentItem(); if ( pos < 0 ) return; QDate date = mExceptionDateEdit->date(); mExceptionDates[ pos ] = date; mExceptionList->changeItem( KGlobal::locale()->formatDate( date ), pos ); } void ExceptionsWidget::deleteException() { int pos = mExceptionList->currentItem(); if ( pos < 0 ) return; mExceptionDates.remove( mExceptionDates.at( pos ) ); mExceptionList->removeItem( pos ); } void ExceptionsWidget::setDates( const DateList &dates ) { mExceptionList->clear(); mExceptionDates.clear(); DateList::ConstIterator dit; for ( dit = dates.begin(); dit != dates.end(); ++dit ) { mExceptionList->insertItem( KGlobal::locale()->formatDate(* dit ) ); mExceptionDates.append( *dit ); } } DateList ExceptionsWidget::dates() { return mExceptionDates; } ///////////////////////// ExceptionsDialog /////////////////////////// ExceptionsDialog::ExceptionsDialog( QWidget *parent, const char *name ) : KDialogBase( parent, name, true, i18n("Edit exceptions"), Ok|Cancel ) { mExceptions = new ExceptionsWidget( this ); setMainWidget( mExceptions ); resize(220,10); } void ExceptionsDialog::setDefaults( const QDateTime &from ) { mExceptions->setDefaults( from ); } void ExceptionsDialog::setDates( const DateList &dates ) { mExceptions->setDates( dates ); } DateList ExceptionsDialog::dates() { return mExceptions->dates(); } ///////////////////////// RecurrenceRangeWidget /////////////////////////// RecurrenceRangeWidget::RecurrenceRangeWidget( QWidget *parent, const char *name ) : QWidget( parent, name ) { QBoxLayout *topLayout = new QVBoxLayout( this ); mRangeGroupBox = new QGroupBox( 1, Horizontal, i18n("Recurrence Range"), this ); topLayout->addWidget( mRangeGroupBox ); QWidget *rangeBox = new QWidget( mRangeGroupBox ); QVBoxLayout *rangeLayout = new QVBoxLayout( rangeBox ); rangeLayout->setSpacing( KDialog::spacingHint() ); mStartDateLabel = new QLabel( i18n("Begin on:"), rangeBox ); rangeLayout->addWidget( mStartDateLabel ); QButtonGroup *rangeButtonGroup = new QButtonGroup; mNoEndDateButton = new QRadioButton( i18n("No ending date"), rangeBox ); rangeButtonGroup->insert( mNoEndDateButton ); rangeLayout->addWidget( mNoEndDateButton ); QBoxLayout *durationLayout = new QHBoxLayout( rangeLayout ); durationLayout->setSpacing( KDialog::spacingHint() ); mEndDurationButton = new QRadioButton( i18n("End after"), rangeBox ); rangeButtonGroup->insert( mEndDurationButton ); durationLayout->addWidget( mEndDurationButton ); mEndDurationEdit = new QSpinBox( 1, 9999, 1, rangeBox ); durationLayout->addWidget( mEndDurationEdit ); QLabel *endDurationLabel = new QLabel( i18n("occurrence(s)"), rangeBox ); durationLayout ->addWidget( endDurationLabel ); QBoxLayout *endDateLayout = new QHBoxLayout( rangeLayout ); endDateLayout->setSpacing( KDialog::spacingHint() ); mEndDateButton = new QRadioButton( i18n("End by:"), rangeBox ); rangeButtonGroup->insert( mEndDateButton ); endDateLayout->addWidget( mEndDateButton ); mEndDateEdit = new KDateEdit( rangeBox ); endDateLayout->addWidget( mEndDateEdit ); endDateLayout->addStretch( 1 ); connect( mNoEndDateButton, SIGNAL( toggled( bool ) ), SLOT( showCurrentRange() ) ); connect( mEndDurationButton, SIGNAL( toggled( bool ) ), SLOT( showCurrentRange() ) ); connect( mEndDateButton, SIGNAL( toggled( bool ) ), SLOT( showCurrentRange() ) ); } void RecurrenceRangeWidget::setDefaults( const QDateTime &from ) { mNoEndDateButton->setChecked( true ); setDateTimes( from ); mEndDateEdit->setDate( from.date() ); } void RecurrenceRangeWidget::setDuration( int duration ) { if ( duration == -1 ) { mNoEndDateButton->setChecked( true ); } else if ( duration == 0 ) { mEndDateButton->setChecked( true ); } else { mEndDurationButton->setChecked( true ); mEndDurationEdit->setValue( duration ); } } int RecurrenceRangeWidget::duration() { if ( mNoEndDateButton->isChecked() ) { return -1; } else if ( mEndDurationButton->isChecked() ) { return mEndDurationEdit->value(); } else { return 0; } } void RecurrenceRangeWidget::setEndDate( const QDate &date ) { mEndDateEdit->setDate( date ); } QDate RecurrenceRangeWidget::endDate() { return mEndDateEdit->date(); } void RecurrenceRangeWidget::showCurrentRange() { mEndDurationEdit->setEnabled( mEndDurationButton->isChecked() ); mEndDateEdit->setEnabled( mEndDateButton->isChecked() ); } void RecurrenceRangeWidget::setDateTimes( const QDateTime &start, const QDateTime & ) { mStartDateLabel->setText( i18n("Start date: %1") .arg( KGlobal::locale()->formatDate( start.date() ) ) ); if(!mEndDateButton->isChecked()) mEndDateEdit->setDate( start.date() ); } ///////////////////////// RecurrenceRangeDialog /////////////////////////// RecurrenceRangeDialog::RecurrenceRangeDialog( QWidget *parent, const char *name ) : KDialogBase( parent, name, true, i18n("Edit Recurrence Range"), Ok|Cancel ) { mRecurrenceRangeWidget = new RecurrenceRangeWidget( this ); setMainWidget( mRecurrenceRangeWidget ); } void RecurrenceRangeDialog::setDefaults( const QDateTime &from ) { mRecurrenceRangeWidget->setDefaults( from ); } void RecurrenceRangeDialog::setDuration( int duration ) { mRecurrenceRangeWidget->setDuration( duration ); } int RecurrenceRangeDialog::duration() { return mRecurrenceRangeWidget->duration(); } void RecurrenceRangeDialog::setEndDate( const QDate &date ) { mRecurrenceRangeWidget->setEndDate( date ); } QDate RecurrenceRangeDialog::endDate() { return mRecurrenceRangeWidget->endDate(); } void RecurrenceRangeDialog::setDateTimes( const QDateTime &start, const QDateTime &end ) { mRecurrenceRangeWidget->setDateTimes( start, end ); } //////////////////////////// RecurrenceChooser //////////////////////// RecurrenceChooser::RecurrenceChooser( QWidget *parent, const char *name ) : QWidget( parent, name ) { QBoxLayout *topLayout = new QVBoxLayout( this ); if ( KOPrefs::instance()->mCompactDialogs ) { mTypeCombo = new QComboBox( this ); mTypeCombo->insertItem( i18n("Daily") ); mTypeCombo->insertItem( i18n("Weekly") ); mTypeCombo->insertItem( i18n("Monthly") ); mTypeCombo->insertItem( i18n("Yearly") ); topLayout->addWidget( mTypeCombo ); connect( mTypeCombo, SIGNAL( activated( int ) ), SLOT( emitChoice() ) ); } else { mTypeCombo = 0; QButtonGroup *ruleButtonGroup = new QButtonGroup( 1, Horizontal, this ); ruleButtonGroup->setFrameStyle( QFrame::NoFrame ); topLayout->addWidget( ruleButtonGroup ); mDailyButton = new QRadioButton( i18n("Daily"), ruleButtonGroup ); mWeeklyButton = new QRadioButton( i18n("Weekly"), ruleButtonGroup ); mMonthlyButton = new QRadioButton( i18n("Monthly"), ruleButtonGroup ); mYearlyButton = new QRadioButton( i18n("Yearly"), ruleButtonGroup ); connect( mDailyButton, SIGNAL( toggled( bool ) ), SLOT( emitChoice() ) ); connect( mWeeklyButton, SIGNAL( toggled( bool ) ), SLOT( emitChoice() ) ); connect( mMonthlyButton, SIGNAL( toggled( bool ) ), SLOT( emitChoice() ) ); connect( mYearlyButton, SIGNAL( toggled( bool ) ), SLOT( emitChoice() ) ); } } int RecurrenceChooser::type() { if ( mTypeCombo ) { return mTypeCombo->currentItem(); } else { if ( mDailyButton->isChecked() ) return Daily; else if ( mWeeklyButton->isChecked() ) return Weekly; else if ( mMonthlyButton->isChecked() ) return Monthly; else return Yearly; } } void RecurrenceChooser::setType( int type ) { if ( mTypeCombo ) { mTypeCombo->setCurrentItem( type ); } else { switch ( type ) { case Daily: mDailyButton->setChecked( true ); break; case Weekly: mWeeklyButton->setChecked( true ); break; case Monthly: mMonthlyButton->setChecked( true ); break; case Yearly: default: mYearlyButton->setChecked( true ); break; } } } void RecurrenceChooser::emitChoice() { emit chosen ( type() ); } /////////////////////////////// Main Widget ///////////////////////////// KOEditorRecurrence::KOEditorRecurrence( QWidget* parent, const char *name ) : QWidget( parent, name ) { QGridLayout *topLayout = new QGridLayout( this, 2,2 ); topLayout->setSpacing( KDialog::spacingHint() ); mEnabledCheck = new QCheckBox( i18n("Enable Recurrence"), this ); connect( mEnabledCheck, SIGNAL( toggled( bool ) ), SLOT( setEnabled( bool ) ) ); topLayout->addMultiCellWidget( mEnabledCheck, 0, 0, 0, 1 ); mTimeGroupBox = new QGroupBox( 1, Horizontal, i18n("Appointment Time "), this ); topLayout->addMultiCellWidget( mTimeGroupBox, 1, 1 , 0 , 1 ); if ( KOPrefs::instance()->mCompactDialogs ) { mTimeGroupBox->hide(); } // QFrame *timeFrame = new QFrame( mTimeGroupBox ); // QBoxLayout *layoutTimeFrame = new QHBoxLayout( timeFrame ); // layoutTimeFrame->setSpacing( KDialog::spacingHint() ); mDateTimeLabel = new QLabel( mTimeGroupBox ); // mDateTimeLabel = new QLabel( timeFrame ); // layoutTimeFrame->addWidget( mDateTimeLabel ); Qt::Orientation orientation; if ( KOPrefs::instance()->mCompactDialogs ) orientation = Horizontal; else orientation = Vertical; mRuleBox = new QGroupBox( 1, orientation, i18n("Recurrence Rule"), this ); if ( KOPrefs::instance()->mCompactDialogs ) { topLayout->addMultiCellWidget( mRuleBox, 2, 2, 0, 1 ); } else { topLayout->addMultiCellWidget( mRuleBox, 2, 2, 0, 1 ); } mRecurrenceChooser = new RecurrenceChooser( mRuleBox ); connect( mRecurrenceChooser, SIGNAL( chosen( int ) ), SLOT( showCurrentRule( int ) ) ); if ( !KOPrefs::instance()->mCompactDialogs ) { QFrame *ruleSepFrame = new QFrame( mRuleBox ); ruleSepFrame->setFrameStyle( QFrame::VLine | QFrame::Sunken ); } mRuleStack = new QWidgetStack( mRuleBox ); mDaily = new RecurDaily( mRuleStack ); mRuleStack->addWidget( mDaily, 0 ); mWeekly = new RecurWeekly( mRuleStack ); mRuleStack->addWidget( mWeekly, 0 ); mMonthly = new RecurMonthly( mRuleStack ); mRuleStack->addWidget( mMonthly, 0 ); mYearly = new RecurYearly( mRuleStack ); mRuleStack->addWidget( mYearly, 0 ); showCurrentRule( mRecurrenceChooser->type() ); if ( KOPrefs::instance()->mCompactDialogs ) { mRecurrenceRangeWidget = 0; mRecurrenceRangeDialog = new RecurrenceRangeDialog( this ); mRecurrenceRange = mRecurrenceRangeDialog; mRecurrenceRangeButton = new QPushButton( i18n("Recurrence Range..."), this ); connect( mRecurrenceRangeButton, SIGNAL( clicked() ), SLOT( showRecurrenceRangeDialog() ) ); mExceptionsWidget = 0; mExceptionsDialog = new ExceptionsDialog( this ); mExceptions = mExceptionsDialog; mExceptionsButton = new QPushButton( i18n("Exceptions..."), this ); if ( QApplication::desktop()->width() < 320 ) { topLayout->addMultiCellWidget( mRecurrenceRangeButton, 3, 3, 0, 1 ); topLayout->addMultiCellWidget( mExceptionsButton, 4, 4, 0, 1 ); } else { topLayout->addWidget( mRecurrenceRangeButton, 3, 0 ); topLayout->addWidget( mExceptionsButton, 3, 1 ); } connect( mExceptionsButton, SIGNAL( clicked() ), SLOT( showExceptionsDialog() ) ); } else { mRecurrenceRangeWidget = new RecurrenceRangeWidget( this ); mRecurrenceRangeDialog = 0; mRecurrenceRange = mRecurrenceRangeWidget; mRecurrenceRangeButton = 0; topLayout->addWidget( mRecurrenceRangeWidget, 3, 0 ); mExceptionsWidget = new ExceptionsWidget( this ); mExceptionsDialog = 0; mExceptions = mExceptionsWidget; mExceptionsButton = 0; topLayout->addWidget( mExceptionsWidget, 3, 1 ); } } KOEditorRecurrence::~KOEditorRecurrence() { } void KOEditorRecurrence::setEnabled( bool enabled ) { // kdDebug() << "KOEditorRecurrence::setEnabled(): " << (enabled ? "on" : "off") << endl; mTimeGroupBox->setEnabled( enabled ); if ( mRecurrenceRangeWidget ) mRecurrenceRangeWidget->setEnabled( enabled ); if ( mRecurrenceRangeButton ) mRecurrenceRangeButton->setEnabled( enabled ); if ( mExceptionsWidget ) mExceptionsWidget->setEnabled( enabled ); if ( mExceptionsButton ) mExceptionsButton->setEnabled( enabled ); + mRuleBox->setEnabled( enabled ); } void KOEditorRecurrence::showCurrentRule( int current ) { switch ( current ) { case Daily: mRuleStack->raiseWidget( mDaily ); break; case Weekly: mRuleStack->raiseWidget( mWeekly ); break; case Monthly: mRuleStack->raiseWidget( mMonthly ); break; default: case Yearly: mRuleStack->raiseWidget( mYearly ); break; } } void KOEditorRecurrence::setDateTimes( QDateTime start, QDateTime end ) { // kdDebug() << "KOEditorRecurrence::setDateTimes" << endl; mRecurrenceRange->setDateTimes( start, end ); mExceptions->setDefaults( end ); } void KOEditorRecurrence::setDefaults( QDateTime from, QDateTime to, bool ) { // qDebug("KOEditorRecurrence::setDefaults %s %s ",from.toString().latin1(),to.toString().latin1() ); setDateTimes( from, to ); bool enabled = false; mEnabledCheck->setChecked( enabled ); setEnabled( enabled ); mExceptions->setDefaults( to ); mRecurrenceRange->setDefaults( to ); mRecurrenceChooser->setType( RecurrenceChooser::Weekly ); showCurrentRule( mRecurrenceChooser->type() ); mDaily->setFrequency( 1 ); mWeekly->setFrequency( 1 ); QBitArray days( 7 ); days.fill( 0 ); + days.setBit( from.date().dayOfWeek()- 1); mWeekly->setDays( days ); - mMonthly->setFrequency( 1 ); + mMonthly->setByPos((from.date().day()/7), from.date().dayOfWeek()-1 ); mMonthly->setByDay( from.date().day()-1 ); - mYearly->setFrequency( 1 ); - mYearly->setByDay(); + mYearly->setByDay( from.date().dayOfYear() ); + mYearly->setByMonth( from.date().month(), from.date().day() ); } void KOEditorRecurrence::readEvent(Event *event) { + setDefaults( event->dtStart(), event->dtEnd(), true ); QBitArray rDays( 7 ); QPtrList<Recurrence::rMonthPos> rmp; QPtrList<int> rmd; int day = 0; int count = 0; int month = 0; setDateTimes( event->dtStart(), event->dtEnd() ); Recurrence *r = event->recurrence(); int f = r->frequency(); int recurs = r->doesRecur(); mEnabledCheck->setChecked( recurs ); setEnabled( recurs ); int recurrenceType = RecurrenceChooser::Weekly; switch ( recurs ) { case Recurrence::rNone: setDefaults( event->dtStart(), event->dtEnd(), true ); break; case Recurrence::rDaily: recurrenceType = RecurrenceChooser::Daily; mDaily->setFrequency( f ); break; case Recurrence::rWeekly: recurrenceType = RecurrenceChooser::Weekly; mWeekly->setFrequency( f ); mWeekly->setDays( r->days() ); break; case Recurrence::rMonthlyPos: // we only handle one possibility in the list right now, // so I have hardcoded calls with first(). If we make the GUI // more extended, this can be changed. recurrenceType = RecurrenceChooser::Monthly; rmp = r->monthPositions(); if ( rmp.first()->negative ) count = 5 - rmp.first()->rPos - 1; else count = rmp.first()->rPos - 1; day = 0; while ( !rmp.first()->rDays.testBit( day ) ) ++day; mMonthly->setByPos( count, day ); mMonthly->setFrequency( f ); break; case Recurrence::rMonthlyDay: recurrenceType = RecurrenceChooser::Monthly; rmd = r->monthDays(); day = *rmd.first() - 1; mMonthly->setByDay( day ); mMonthly->setFrequency( f ); break; case Recurrence::rYearlyMonth: - case Recurrence::rYearlyDay: + { recurrenceType = RecurrenceChooser::Yearly; - + qDebug("Recurrence::rYearlyMonth: "); + day = event->dtStart().date().day(); rmd = r->yearNums(); + if ( rmd.count() > 0 ) month = *rmd.first(); - if ( month == event->dtStart().date().month() ) { - mYearly->setByDay(); + else + month = event->dtStart().date().month() ; + mYearly->setByMonth( month, day ); +#if 0 + qDebug("2day = %d ",day ); + QPtrList<Recurrence::rMonthPos> monthlist = r->yearMonthPositions(); + int month; + if ( !monthlist.isEmpty() ) { + month = monthlist.first()->rPos ; } else { - mYearly->setByMonth( month ); + month = event->dtStart().date().month() ; + } + mYearly->setByMonth( day, month ); +#endif + mYearly->setFrequency( f ); } + break; + case Recurrence::rYearlyDay: + qDebug("Recurrence::rYearlyDay: "); + recurrenceType = RecurrenceChooser::Yearly; + mYearly->setByDay( event->dtStart().date().dayOfYear() ); mYearly->setFrequency( f ); break; default: setDefaults( event->dtStart(), event->dtEnd(), true ); break; } mRecurrenceChooser->setType( recurrenceType ); showCurrentRule( recurrenceType ); mRecurrenceRange->setDateTimes( event->dtStart() ); if ( r->doesRecur() ) { mRecurrenceRange->setDuration( r->duration() ); if ( r->duration() == 0 ) { if ( r->endDate() < event->dtStart().date() ) mRecurrenceRange->setEndDate( event->dtStart().date() ); else mRecurrenceRange->setEndDate( r->endDate() ); } else mRecurrenceRange->setEndDate( event->dtStart().date() ); } mExceptions->setDates( event->exDates() ); } void KOEditorRecurrence::writeEvent( Event *event ) { Recurrence *r = event->recurrence(); // clear out any old settings; r->unsetRecurs(); if ( mEnabledCheck->isChecked() ) { int duration = mRecurrenceRange->duration(); QDate endDate; if ( duration == 0 ) endDate = mRecurrenceRange->endDate(); int recurrenceType = mRecurrenceChooser->type(); if ( recurrenceType == RecurrenceChooser::Daily ) { int freq = mDaily->frequency(); if ( duration != 0 ) r->setDaily( freq, duration ); else r->setDaily( freq, endDate ); } else if ( recurrenceType == RecurrenceChooser::Weekly ) { int freq = mWeekly->frequency(); QBitArray days = mWeekly->days(); int j; bool found = false; for (j = 0; j < 7 ; ++j ) { found |=days.at(j); } if ( !found ) { days.setBit( event->dtStart().date().dayOfWeek()-1); qDebug("bit set %d "); } if ( duration != 0 ) r->setWeekly( freq, days, duration ); else r->setWeekly( freq, days, endDate ); } else if ( recurrenceType == RecurrenceChooser::Monthly ) { int freq = mMonthly->frequency(); if ( mMonthly->byPos() ) { int pos = mMonthly->count(); QBitArray days( 7 ); days.fill( false ); days.setBit( mMonthly->weekday() ); if ( duration != 0 ) r->setMonthly( Recurrence::rMonthlyPos, freq, duration ); else r->setMonthly( Recurrence::rMonthlyPos, freq, endDate ); r->addMonthlyPos( pos, days ); } else { // it's by day int day = mMonthly->day(); if ( duration != 0 ) { r->setMonthly( Recurrence::rMonthlyDay, freq, duration ); } else { r->setMonthly( Recurrence::rMonthlyDay, freq, endDate ); } r->addMonthlyDay( day ); } } else if ( recurrenceType == RecurrenceChooser::Yearly ) { + qDebug("RecurrenceChooser::Yearly "); int freq = mYearly->frequency(); - - int month; - if ( mYearly->byMonth() ) { - month = mYearly->month(); + if ( mYearly->byDay() ) { + if ( duration != 0 ) { + r->setYearly( Recurrence::rYearlyDay, freq, duration ); } else { - month = event->dtStart().date().month(); + r->setYearly( Recurrence::rYearlyDay, freq, endDate ); } + r->addYearlyNum( event->dtStart().date().dayOfYear() ); + } else { if ( duration != 0 ) { r->setYearly( Recurrence::rYearlyMonth, freq, duration ); } else { r->setYearly( Recurrence::rYearlyMonth, freq, endDate ); } + r->addYearlyNum( mYearly->month() ); + } - r->addYearlyNum( month ); } event->setExDates( mExceptions->dates() ); } } void KOEditorRecurrence::setDateTimeStr( const QString &str ) { mDateTimeLabel->setText( str ); } bool KOEditorRecurrence::validateInput() { // Check input here return true; } void KOEditorRecurrence::showExceptionsDialog() { DateList dates = mExceptions->dates(); int result = mExceptionsDialog->exec(); if ( result == QDialog::Rejected ) mExceptions->setDates( dates ); } void KOEditorRecurrence::showRecurrenceRangeDialog() { int duration = mRecurrenceRange->duration(); QDate endDate = mRecurrenceRange->endDate(); int result = mRecurrenceRangeDialog->exec(); if ( result == QDialog::Rejected ) { mRecurrenceRange->setDuration( duration ); mRecurrenceRange->setEndDate( endDate ); } } diff --git a/korganizer/koeditorrecurrence.h b/korganizer/koeditorrecurrence.h index 4f0f0b2..2b59085 100644 --- a/korganizer/koeditorrecurrence.h +++ b/korganizer/koeditorrecurrence.h @@ -1,321 +1,324 @@ /* This file is part of KOrganizer. Copyright (c) 2000-2003 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. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #ifndef _KOEDITORRECURRENCE_H #define _KOEDITORRECURRENCE_H #include <qframe.h> #include <qlabel.h> #include <qcheckbox.h> #include <qpushbutton.h> #include <qgroupbox.h> #include <qlineedit.h> #include <qcombobox.h> #include <qmultilineedit.h> #include <qlistview.h> #include <qradiobutton.h> #include <kdialogbase.h> #include <libkcal/event.h> #include "ktimeedit.h" class QWidgetStack; class QSpinBox; class KDateEdit; using namespace KCal; class RecurBase : public QWidget { public: RecurBase( QWidget *parent = 0, const char *name = 0 ); void setFrequency( int ); int frequency(); QWidget *frequencyEdit(); private: QSpinBox *mFrequencyEdit; }; class RecurDaily : public RecurBase { public: RecurDaily( QWidget *parent = 0, const char *name = 0 ); }; class RecurWeekly : public RecurBase { public: RecurWeekly( QWidget *parent = 0, const char *name = 0 ); void setDays( const QBitArray & ); QBitArray days(); private: QCheckBox *mDayBoxes[7]; }; class RecurMonthly : public RecurBase { public: RecurMonthly( QWidget *parent = 0, const char *name = 0 ); void setByDay( int day ); void setByPos( int count, int weekday ); bool byDay(); bool byPos(); int day(); int count(); int weekday(); private: QRadioButton *mByDayRadio; QComboBox *mByDayCombo; QRadioButton *mByPosRadio; QComboBox *mByPosCountCombo; QComboBox *mByPosWeekdayCombo; }; class RecurYearly : public RecurBase { public: RecurYearly( QWidget *parent = 0, const char *name = 0 ); - void setByDay(); - void setByMonth( int month ); + void setByDay( int doy ); + void setByMonth( int month, int day ); bool byMonth(); bool byDay(); int month(); + int day(); private: + int mDay; QRadioButton *mByMonthRadio; QComboBox *mByMonthCombo; - + QLabel* mByDayLabel; + QLabel* mDayOfLabel; QRadioButton *mByDayRadio; }; class RecurrenceChooser : public QWidget { Q_OBJECT public: RecurrenceChooser( QWidget *parent = 0, const char *name = 0 ); enum { Daily, Weekly, Monthly, Yearly }; void setType( int ); int type(); signals: void chosen( int ); protected slots: void emitChoice(); private: QComboBox *mTypeCombo; QRadioButton *mDailyButton; QRadioButton *mWeeklyButton; QRadioButton *mMonthlyButton; QRadioButton *mYearlyButton; }; class ExceptionsBase { public: virtual void setDefaults( const QDateTime &from ) = 0; virtual void setDates( const DateList & ) = 0; virtual DateList dates() = 0; }; class ExceptionsWidget : public QWidget, public ExceptionsBase { Q_OBJECT public: ExceptionsWidget( QWidget *parent = 0, const char *name = 0 ); void setDefaults( const QDateTime &from ); void setDates( const DateList & ); DateList dates(); protected slots: void addException(); void changeException(); void deleteException(); private: KDateEdit *mExceptionDateEdit; QListBox *mExceptionList; DateList mExceptionDates; }; class ExceptionsDialog : public KDialogBase, public ExceptionsBase { public: ExceptionsDialog( QWidget *parent, const char *name = 0 ); void setDefaults( const QDateTime &from ); void setDates( const DateList & ); DateList dates(); private: ExceptionsWidget *mExceptions; }; class RecurrenceRangeBase { public: virtual void setDefaults( const QDateTime &from ) = 0; virtual void setDuration( int ) = 0; virtual int duration() = 0; virtual void setEndDate( const QDate & ) = 0; virtual QDate endDate() = 0; virtual void setDateTimes( const QDateTime &start, const QDateTime &end = QDateTime() ) = 0; }; class RecurrenceRangeWidget : public QWidget, public RecurrenceRangeBase { Q_OBJECT public: RecurrenceRangeWidget( QWidget *parent = 0, const char *name = 0 ); void setDefaults( const QDateTime &from ); void setDuration( int ); int duration(); void setEndDate( const QDate & ); QDate endDate(); void setDateTimes( const QDateTime &start, const QDateTime &end = QDateTime() ); protected slots: void showCurrentRange(); private: QGroupBox *mRangeGroupBox; QLabel *mStartDateLabel; QRadioButton *mNoEndDateButton; QRadioButton *mEndDurationButton; QSpinBox *mEndDurationEdit; QRadioButton *mEndDateButton; KDateEdit *mEndDateEdit; }; class RecurrenceRangeDialog : public KDialogBase, public RecurrenceRangeBase { public: RecurrenceRangeDialog( QWidget *parent = 0, const char *name = 0 ); void setDefaults( const QDateTime &from ); void setDuration( int ); int duration(); void setEndDate( const QDate & ); QDate endDate(); void setDateTimes( const QDateTime &start, const QDateTime &end = QDateTime() ); private: RecurrenceRangeWidget *mRecurrenceRangeWidget; }; class KOEditorRecurrence : public QWidget { Q_OBJECT public: KOEditorRecurrence ( QWidget *parent = 0, const char *name = 0 ); virtual ~KOEditorRecurrence(); enum { Daily, Weekly, Monthly, Yearly }; /** Set widgets to default values */ void setDefaults( QDateTime from, QDateTime to, bool allday ); /** Read event object and setup widgets accordingly */ void readEvent( Event * ); /** Write event settings to event object */ void writeEvent( Event * ); /** Check if the input is valid. */ bool validateInput(); public slots: void setEnabled( bool ); void setDateTimes( QDateTime start, QDateTime end ); void setDateTimeStr( const QString & ); signals: void dateTimesChanged( QDateTime start, QDateTime end ); protected slots: void showCurrentRule( int ); void showExceptionsDialog(); void showRecurrenceRangeDialog(); private: QCheckBox *mEnabledCheck; QGroupBox *mTimeGroupBox; QLabel *mDateTimeLabel; QGroupBox *mRuleBox; QWidgetStack *mRuleStack; RecurrenceChooser *mRecurrenceChooser; RecurDaily *mDaily; RecurWeekly *mWeekly; RecurMonthly *mMonthly; RecurYearly *mYearly; RecurrenceRangeBase *mRecurrenceRange; RecurrenceRangeWidget *mRecurrenceRangeWidget; RecurrenceRangeDialog *mRecurrenceRangeDialog; QPushButton *mRecurrenceRangeButton; ExceptionsBase *mExceptions; ExceptionsDialog *mExceptionsDialog; ExceptionsWidget *mExceptionsWidget; QPushButton *mExceptionsButton; }; #endif diff --git a/libkcal/icalformatimpl.cpp b/libkcal/icalformatimpl.cpp index 32a1337..964ffe3 100644 --- a/libkcal/icalformatimpl.cpp +++ b/libkcal/icalformatimpl.cpp @@ -1011,938 +1011,942 @@ FreeBusy *ICalFormatImpl::readFreeBusy(icalcomponent *vfreebusy) break; default: kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind << endl; break; } p = icalcomponent_get_next_property(vfreebusy,ICAL_ANY_PROPERTY); } return freebusy; } Journal *ICalFormatImpl::readJournal(icalcomponent *vjournal) { Journal *journal = new Journal; readIncidence(vjournal,journal); return journal; } Attendee *ICalFormatImpl::readAttendee(icalproperty *attendee) { icalparameter *p = 0; QString email = QString::fromUtf8(icalproperty_get_attendee(attendee)); QString name; QString uid = QString::null; p = icalproperty_get_first_parameter(attendee,ICAL_CN_PARAMETER); if (p) { name = QString::fromUtf8(icalparameter_get_cn(p)); } else { } bool rsvp=false; p = icalproperty_get_first_parameter(attendee,ICAL_RSVP_PARAMETER); if (p) { icalparameter_rsvp rsvpParameter = icalparameter_get_rsvp(p); if (rsvpParameter == ICAL_RSVP_TRUE) rsvp = true; } Attendee::PartStat status = Attendee::NeedsAction; p = icalproperty_get_first_parameter(attendee,ICAL_PARTSTAT_PARAMETER); if (p) { icalparameter_partstat partStatParameter = icalparameter_get_partstat(p); switch(partStatParameter) { default: case ICAL_PARTSTAT_NEEDSACTION: status = Attendee::NeedsAction; break; case ICAL_PARTSTAT_ACCEPTED: status = Attendee::Accepted; break; case ICAL_PARTSTAT_DECLINED: status = Attendee::Declined; break; case ICAL_PARTSTAT_TENTATIVE: status = Attendee::Tentative; break; case ICAL_PARTSTAT_DELEGATED: status = Attendee::Delegated; break; case ICAL_PARTSTAT_COMPLETED: status = Attendee::Completed; break; case ICAL_PARTSTAT_INPROCESS: status = Attendee::InProcess; break; } } Attendee::Role role = Attendee::ReqParticipant; p = icalproperty_get_first_parameter(attendee,ICAL_ROLE_PARAMETER); if (p) { icalparameter_role roleParameter = icalparameter_get_role(p); switch(roleParameter) { case ICAL_ROLE_CHAIR: role = Attendee::Chair; break; default: case ICAL_ROLE_REQPARTICIPANT: role = Attendee::ReqParticipant; break; case ICAL_ROLE_OPTPARTICIPANT: role = Attendee::OptParticipant; break; case ICAL_ROLE_NONPARTICIPANT: role = Attendee::NonParticipant; break; } } p = icalproperty_get_first_parameter(attendee,ICAL_X_PARAMETER); uid = icalparameter_get_xvalue(p); // This should be added, but there seems to be a libical bug here. /*while (p) { // if (icalparameter_get_xname(p) == "X-UID") { uid = icalparameter_get_xvalue(p); p = icalproperty_get_next_parameter(attendee,ICAL_X_PARAMETER); } */ return new Attendee( name, email, rsvp, status, role, uid ); } Attachment *ICalFormatImpl::readAttachment(icalproperty *attach) { icalattach *a = icalproperty_get_attach(attach); icalparameter_value v = ICAL_VALUE_NONE; icalparameter_encoding e = ICAL_ENCODING_NONE; Attachment *attachment = 0; /* icalparameter *vp = icalproperty_get_first_parameter(attach, ICAL_VALUE_PARAMETER); if (vp) v = icalparameter_get_value(vp); icalparameter *ep = icalproperty_get_first_parameter(attach, ICAL_ENCODING_PARAMETER); if (ep) e = icalparameter_get_encoding(ep); */ int isurl = icalattach_get_is_url (a); if (isurl == 0) attachment = new Attachment((const char*)icalattach_get_data(a)); else { attachment = new Attachment(QString(icalattach_get_url(a))); } icalparameter *p = icalproperty_get_first_parameter(attach, ICAL_FMTTYPE_PARAMETER); if (p) attachment->setMimeType(QString(icalparameter_get_fmttype(p))); return attachment; } #include <qtextcodec.h> void ICalFormatImpl::readIncidence(icalcomponent *parent,Incidence *incidence) { readIncidenceBase(parent,incidence); icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY); bool readrec = false; const char *text; int intvalue; icaltimetype icaltime; icaldurationtype icalduration; struct icalrecurrencetype rectype; QStringList categories; while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_CREATED_PROPERTY: icaltime = icalproperty_get_created(p); incidence->setCreated(readICalDateTime(icaltime)); break; case ICAL_SEQUENCE_PROPERTY: // sequence intvalue = icalproperty_get_sequence(p); incidence->setRevision(intvalue); break; case ICAL_LASTMODIFIED_PROPERTY: // last modification date icaltime = icalproperty_get_lastmodified(p); incidence->setLastModified(readICalDateTime(icaltime)); break; case ICAL_DTSTART_PROPERTY: // start date and time icaltime = icalproperty_get_dtstart(p); if (icaltime.is_date) { incidence->setDtStart(QDateTime(readICalDate(icaltime),QTime(0,0,0))); incidence->setFloats(true); } else { incidence->setDtStart(readICalDateTime(icaltime)); } break; case ICAL_DURATION_PROPERTY: // start date and time icalduration = icalproperty_get_duration(p); incidence->setDuration(readICalDuration(icalduration)); break; case ICAL_DESCRIPTION_PROPERTY: // description text = icalproperty_get_description(p); incidence->setDescription(QString::fromUtf8(text)); break; case ICAL_SUMMARY_PROPERTY: // summary { text = icalproperty_get_summary(p); incidence->setSummary(QString::fromUtf8(text)); } break; case ICAL_STATUS_PROPERTY: // summary { if ( ICAL_STATUS_CANCELLED == icalproperty_get_status(p) ) incidence->setCancelled( true ); } break; case ICAL_LOCATION_PROPERTY: // location text = icalproperty_get_location(p); incidence->setLocation(QString::fromUtf8(text)); break; #if 0 // status if ((vo = isAPropertyOf(vincidence, VCStatusProp)) != 0) { incidence->setStatus(s = fakeCString(vObjectUStringZValue(vo))); deleteStr(s); } else incidence->setStatus("NEEDS ACTION"); #endif case ICAL_PRIORITY_PROPERTY: // priority intvalue = icalproperty_get_priority(p); incidence->setPriority(intvalue); break; case ICAL_CATEGORIES_PROPERTY: // categories text = icalproperty_get_categories(p); categories.append(QString::fromUtf8(text)); break; //******************************************* case ICAL_RRULE_PROPERTY: // we do need (maybe )start datetime of incidence for recurrence // such that we can read recurrence only after we read incidence completely readrec = true; rectype = icalproperty_get_rrule(p); break; case ICAL_EXDATE_PROPERTY: icaltime = icalproperty_get_exdate(p); incidence->addExDate(readICalDate(icaltime)); break; case ICAL_CLASS_PROPERTY: { int inttext = icalproperty_get_class(p); if (inttext == ICAL_CLASS_PUBLIC ) { incidence->setSecrecy(Incidence::SecrecyPublic); } else if (inttext == ICAL_CLASS_CONFIDENTIAL ) { incidence->setSecrecy(Incidence::SecrecyConfidential); } else { incidence->setSecrecy(Incidence::SecrecyPrivate); } } break; case ICAL_ATTACH_PROPERTY: // attachments incidence->addAttachment(readAttachment(p)); break; default: // kdDebug(5800) << "ICALFormat::readIncidence(): Unknown property: " << kind // << endl; break; } p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY); } if ( readrec ) { readRecurrenceRule(rectype,incidence); } // kpilot stuff // TODO: move this application-specific code to kpilot QString kp = incidence->nonKDECustomProperty("X-PILOTID"); if (!kp.isNull()) { incidence->setPilotId(kp.toInt()); } kp = incidence->nonKDECustomProperty("X-PILOTSTAT"); if (!kp.isNull()) { incidence->setSyncStatus(kp.toInt()); } kp = incidence->nonKDECustomProperty("X-ZAURUSID"); if (!kp.isNull()) { incidence->setZaurusId(kp.toInt()); } kp = incidence->nonKDECustomProperty("X-ZAURUSUID"); if (!kp.isNull()) { incidence->setZaurusUid(kp.toInt()); } kp = incidence->nonKDECustomProperty("X-ZAURUSSTAT"); if (!kp.isNull()) { incidence->setZaurusStat(kp.toInt()); } // Cancel backwards compatibility mode for subsequent changes by the application incidence->recurrence()->setCompatVersion(); // add categories incidence->setCategories(categories); // iterate through all alarms for (icalcomponent *alarm = icalcomponent_get_first_component(parent,ICAL_VALARM_COMPONENT); alarm; alarm = icalcomponent_get_next_component(parent,ICAL_VALARM_COMPONENT)) { readAlarm(alarm,incidence); } } void ICalFormatImpl::readIncidenceBase(icalcomponent *parent,IncidenceBase *incidenceBase) { icalproperty *p = icalcomponent_get_first_property(parent,ICAL_ANY_PROPERTY); while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_UID_PROPERTY: // unique id incidenceBase->setUid(QString::fromUtf8(icalproperty_get_uid(p))); break; case ICAL_ORGANIZER_PROPERTY: // organizer incidenceBase->setOrganizer(QString::fromUtf8(icalproperty_get_organizer(p))); break; case ICAL_ATTENDEE_PROPERTY: // attendee incidenceBase->addAttendee(readAttendee(p)); break; default: break; } p = icalcomponent_get_next_property(parent,ICAL_ANY_PROPERTY); } // custom properties readCustomProperties(parent, incidenceBase); } void ICalFormatImpl::readCustomProperties(icalcomponent *parent,CustomProperties *properties) { QMap<QCString, QString> customProperties; icalproperty *p = icalcomponent_get_first_property(parent,ICAL_X_PROPERTY); while (p) { QString value = QString::fromUtf8(icalproperty_get_x(p)); customProperties[icalproperty_get_x_name(p)] = value; //qDebug("ICalFormatImpl::readCustomProperties %s %s",value.latin1(), icalproperty_get_x_name(p) ); p = icalcomponent_get_next_property(parent,ICAL_X_PROPERTY); } properties->setCustomProperties(customProperties); } void ICalFormatImpl::readRecurrenceRule(struct icalrecurrencetype rrule,Incidence *incidence) { // kdDebug(5800) << "Read recurrence for " << incidence->summary() << endl; Recurrence *recur = incidence->recurrence(); recur->setCompatVersion(mCalendarVersion); recur->unsetRecurs(); struct icalrecurrencetype r = rrule; dumpIcalRecurrence(r); readRecurrence( r, recur, incidence); } void ICalFormatImpl::readRecurrence( const struct icalrecurrencetype &r, Recurrence* recur, Incidence *incidence) { int wkst; int index = 0; short day = 0; QBitArray qba(7); int frequ = r.freq; int interv = r.interval; // preprocessing for odd recurrence definitions if ( r.freq == ICAL_MONTHLY_RECURRENCE ) { if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX) { interv = 12; } } if ( r.freq == ICAL_YEARLY_RECURRENCE ) { if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX && r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX ) { frequ = ICAL_MONTHLY_RECURRENCE; - interv = 12; + interv = 12* r.interval; } } switch (frequ) { case ICAL_MINUTELY_RECURRENCE: if (!icaltime_is_null_time(r.until)) { recur->setMinutely(interv,readICalDateTime(r.until)); } else { if (r.count == 0) recur->setMinutely(interv,-1); else recur->setMinutely(interv,r.count); } break; case ICAL_HOURLY_RECURRENCE: if (!icaltime_is_null_time(r.until)) { recur->setHourly(interv,readICalDateTime(r.until)); } else { if (r.count == 0) recur->setHourly(interv,-1); else recur->setHourly(interv,r.count); } break; case ICAL_DAILY_RECURRENCE: if (!icaltime_is_null_time(r.until)) { recur->setDaily(interv,readICalDate(r.until)); } else { if (r.count == 0) recur->setDaily(interv,-1); else recur->setDaily(interv,r.count); } break; case ICAL_WEEKLY_RECURRENCE: // kdDebug(5800) << "WEEKLY_RECURRENCE" << endl; wkst = (r.week_start + 5)%7 + 1; if (!icaltime_is_null_time(r.until)) { recur->setWeekly(interv,qba,readICalDate(r.until),wkst); } else { if (r.count == 0) recur->setWeekly(interv,qba,-1,wkst); else recur->setWeekly(interv,qba,r.count,wkst); } if ( r.by_day[0] == ICAL_RECURRENCE_ARRAY_MAX) { int wday = incidence->dtStart().date().dayOfWeek ()-1; //qDebug("weekly error found "); qba.setBit(wday); } else { while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { // kdDebug(5800) << " " << day << endl; qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 } } break; case ICAL_MONTHLY_RECURRENCE: if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { if (!icaltime_is_null_time(r.until)) { recur->setMonthly(Recurrence::rMonthlyPos,interv, readICalDate(r.until)); } else { if (r.count == 0) recur->setMonthly(Recurrence::rMonthlyPos,interv,-1); else recur->setMonthly(Recurrence::rMonthlyPos,interv,r.count); } bool useSetPos = false; short pos = 0; while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { // kdDebug(5800) << "----a " << index << ": " << day << endl; pos = icalrecurrencetype_day_position(day); if (pos) { day = icalrecurrencetype_day_day_of_week(day); QBitArray ba(7); // don't wipe qba ba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 recur->addMonthlyPos(pos,ba); } else { qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 useSetPos = true; } } if (useSetPos) { if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) { recur->addMonthlyPos(r.by_set_pos[0],qba); } } } else if (r.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { if (!icaltime_is_null_time(r.until)) { recur->setMonthly(Recurrence::rMonthlyDay,interv, readICalDate(r.until)); } else { if (r.count == 0) recur->setMonthly(Recurrence::rMonthlyDay,interv,-1); else recur->setMonthly(Recurrence::rMonthlyDay,interv,r.count); } while((day = r.by_month_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { // kdDebug(5800) << "----b " << day << endl; recur->addMonthlyDay(day); } } break; case ICAL_YEARLY_RECURRENCE: if (r.by_year_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + qDebug(" YEARLY DAY OF YEAR"); if (!icaltime_is_null_time(r.until)) { recur->setYearly(Recurrence::rYearlyDay,interv, readICalDate(r.until)); } else { if (r.count == 0) recur->setYearly(Recurrence::rYearlyDay,interv,-1); else recur->setYearly(Recurrence::rYearlyDay,interv,r.count); } while((day = r.by_year_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { recur->addYearlyNum(day); } } else if ( true /*r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX*/) { if (r.by_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { + qDebug("YEARLY POS NOT SUPPORTED BY GUI"); if (!icaltime_is_null_time(r.until)) { recur->setYearly(Recurrence::rYearlyPos,interv, readICalDate(r.until)); } else { if (r.count == 0) recur->setYearly(Recurrence::rYearlyPos,interv,-1); else recur->setYearly(Recurrence::rYearlyPos,interv,r.count); } bool useSetPos = false; short pos = 0; while((day = r.by_day[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { // kdDebug(5800) << "----a " << index << ": " << day << endl; pos = icalrecurrencetype_day_position(day); if (pos) { day = icalrecurrencetype_day_day_of_week(day); QBitArray ba(7); // don't wipe qba ba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 recur->addYearlyMonthPos(pos,ba); } else { qba.setBit((day+5)%7); // convert from Sunday=1 to Monday=0 useSetPos = true; } } if (useSetPos) { if (r.by_set_pos[0] != ICAL_RECURRENCE_ARRAY_MAX) { recur->addYearlyMonthPos(r.by_set_pos[0],qba); } } } else { + qDebug("YEARLY MONTH "); if (!icaltime_is_null_time(r.until)) { recur->setYearly(Recurrence::rYearlyMonth,interv, readICalDate(r.until)); } else { if (r.count == 0) recur->setYearly(Recurrence::rYearlyMonth,interv,-1); else recur->setYearly(Recurrence::rYearlyMonth,interv,r.count); } - } if ( r.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX ) { index = 0; while((day = r.by_month[index++]) != ICAL_RECURRENCE_ARRAY_MAX) { recur->addYearlyNum(day); } } else { recur->addYearlyNum(incidence->dtStart().date().month()); } } + + } break; default: ; break; } } void ICalFormatImpl::readAlarm(icalcomponent *alarm,Incidence *incidence) { //kdDebug(5800) << "Read alarm for " << incidence->summary() << endl; Alarm* ialarm = incidence->newAlarm(); ialarm->setRepeatCount(0); ialarm->setEnabled(true); // Determine the alarm's action type icalproperty *p = icalcomponent_get_first_property(alarm,ICAL_ACTION_PROPERTY); if ( !p ) { return; } icalproperty_action action = icalproperty_get_action(p); Alarm::Type type = Alarm::Display; switch ( action ) { case ICAL_ACTION_DISPLAY: type = Alarm::Display; break; case ICAL_ACTION_AUDIO: type = Alarm::Audio; break; case ICAL_ACTION_PROCEDURE: type = Alarm::Procedure; break; case ICAL_ACTION_EMAIL: type = Alarm::Email; break; default: ; return; } ialarm->setType(type); p = icalcomponent_get_first_property(alarm,ICAL_ANY_PROPERTY); while (p) { icalproperty_kind kind = icalproperty_isa(p); switch (kind) { case ICAL_TRIGGER_PROPERTY: { icaltriggertype trigger = icalproperty_get_trigger(p); if (icaltime_is_null_time(trigger.time)) { if (icaldurationtype_is_null_duration(trigger.duration)) { kdDebug(5800) << "ICalFormatImpl::readAlarm(): Trigger has no time and no duration." << endl; } else { Duration duration = icaldurationtype_as_int( trigger.duration ); icalparameter *param = icalproperty_get_first_parameter(p,ICAL_RELATED_PARAMETER); if (param && icalparameter_get_related(param) == ICAL_RELATED_END) ialarm->setEndOffset(duration); else ialarm->setStartOffset(duration); } } else { ialarm->setTime(readICalDateTime(trigger.time)); } break; } case ICAL_DURATION_PROPERTY: { icaldurationtype duration = icalproperty_get_duration(p); ialarm->setSnoozeTime(icaldurationtype_as_int(duration)/60); break; } case ICAL_REPEAT_PROPERTY: ialarm->setRepeatCount(icalproperty_get_repeat(p)); break; // Only in DISPLAY and EMAIL and PROCEDURE alarms case ICAL_DESCRIPTION_PROPERTY: { QString description = QString::fromUtf8(icalproperty_get_description(p)); switch ( action ) { case ICAL_ACTION_DISPLAY: ialarm->setText( description ); break; case ICAL_ACTION_PROCEDURE: ialarm->setProgramArguments( description ); break; case ICAL_ACTION_EMAIL: ialarm->setMailText( description ); break; default: break; } break; } // Only in EMAIL alarm case ICAL_SUMMARY_PROPERTY: ialarm->setMailSubject(QString::fromUtf8(icalproperty_get_summary(p))); break; // Only in EMAIL alarm case ICAL_ATTENDEE_PROPERTY: { QString email = QString::fromUtf8(icalproperty_get_attendee(p)); QString name; icalparameter *param = icalproperty_get_first_parameter(p,ICAL_CN_PARAMETER); if (param) { name = QString::fromUtf8(icalparameter_get_cn(param)); } ialarm->addMailAddress(Person(name, email)); break; } // Only in AUDIO and EMAIL and PROCEDURE alarms case ICAL_ATTACH_PROPERTY: { icalattach *attach = icalproperty_get_attach(p); QString url = QFile::decodeName(icalattach_get_url(attach)); switch ( action ) { case ICAL_ACTION_AUDIO: ialarm->setAudioFile( url ); break; case ICAL_ACTION_PROCEDURE: ialarm->setProgramFile( url ); break; case ICAL_ACTION_EMAIL: ialarm->addMailAttachment( url ); break; default: break; } break; } default: break; } p = icalcomponent_get_next_property(alarm,ICAL_ANY_PROPERTY); } // custom properties readCustomProperties(alarm, ialarm); // TODO: check for consistency of alarm properties } icaltimetype ICalFormatImpl::writeICalDate(const QDate &date) { icaltimetype t; t.year = date.year(); t.month = date.month(); t.day = date.day(); t.hour = 0; t.minute = 0; t.second = 0; t.is_date = 1; t.is_utc = 0; t.zone = 0; return t; } icaltimetype ICalFormatImpl::writeICalDateTime(const QDateTime &dt ) { icaltimetype t; t.is_date = 0; t.zone = 0; QDateTime datetime; if ( mParent->utc() ) { int offset = KGlobal::locale()->localTimeOffset( dt ); datetime = dt.addSecs ( -offset*60); t.is_utc = 1; } else { datetime = dt; t.is_utc = 0; } t.year = datetime.date().year(); t.month = datetime.date().month(); t.day = datetime.date().day(); t.hour = datetime.time().hour(); t.minute = datetime.time().minute(); t.second = datetime.time().second(); //qDebug("*** time %s localtime %s ",dt .toString().latin1() ,datetime .toString().latin1() ); // if ( mParent->utc() ) { // datetime = KGlobal::locale()->localTime( dt ); // qDebug("*** time %s localtime %s ",dt .toString().latin1() ,datetime .toString().latin1() ); // if (mParent->timeZoneId().isEmpty()) // t = icaltime_as_utc(t, 0); // else // t = icaltime_as_utc(t,mParent->timeZoneId().local8Bit()); // } return t; } QDateTime ICalFormatImpl::readICalDateTime(icaltimetype t) { QDateTime dt (QDate(t.year,t.month,t.day), QTime(t.hour,t.minute,t.second) ); if (t.is_utc) { int offset = KGlobal::locale()->localTimeOffset( dt ); dt = dt.addSecs ( offset*60); } return dt; } QDate ICalFormatImpl::readICalDate(icaltimetype t) { return QDate(t.year,t.month,t.day); } icaldurationtype ICalFormatImpl::writeICalDuration(int seconds) { icaldurationtype d; d.weeks = seconds % gSecondsPerWeek; seconds -= d.weeks * gSecondsPerWeek; d.days = seconds % gSecondsPerDay; seconds -= d.days * gSecondsPerDay; d.hours = seconds % gSecondsPerHour; seconds -= d.hours * gSecondsPerHour; d.minutes = seconds % gSecondsPerMinute; seconds -= d.minutes * gSecondsPerMinute; d.seconds = seconds; d.is_neg = 0; return d; } int ICalFormatImpl::readICalDuration(icaldurationtype d) { int result = 0; result += d.weeks * gSecondsPerWeek; result += d.days * gSecondsPerDay; result += d.hours * gSecondsPerHour; result += d.minutes * gSecondsPerMinute; result += d.seconds; if (d.is_neg) result *= -1; return result; } icalcomponent *ICalFormatImpl::createCalendarComponent(Calendar *cal) { icalcomponent *calendar; // Root component calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT); icalproperty *p; // Product Identifier p = icalproperty_new_prodid(CalFormat::productId().utf8()); icalcomponent_add_property(calendar,p); // TODO: Add time zone // iCalendar version (2.0) p = icalproperty_new_version(const_cast<char *>(_ICAL_VERSION)); icalcomponent_add_property(calendar,p); // Custom properties if( cal != 0 ) writeCustomProperties(calendar, cal); return calendar; } // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. // and break it down from its tree-like format into the dictionary format // that is used internally in the ICalFormatImpl. bool ICalFormatImpl::populate( Calendar *cal, icalcomponent *calendar) { // this function will populate the caldict dictionary and other event // lists. It turns vevents into Events and then inserts them. if (!calendar) return false; // TODO: check for METHOD #if 0 if ((curVO = isAPropertyOf(vcal, ICMethodProp)) != 0) { char *methodType = 0; methodType = fakeCString(vObjectUStringZValue(curVO)); if (mEnableDialogs) KMessageBox::information(mTopWidget, i18n("This calendar is an iTIP transaction of type \"%1\".") .arg(methodType), i18n("%1: iTIP Transaction").arg(CalFormat::application())); delete methodType; } #endif icalproperty *p; p = icalcomponent_get_first_property(calendar,ICAL_PRODID_PROPERTY); if (!p) { // TODO: does no PRODID really matter? // mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); // return false; mLoadedProductId = ""; mCalendarVersion = 0; } else { mLoadedProductId = QString::fromUtf8(icalproperty_get_prodid(p)); mCalendarVersion = CalFormat::calendarVersion(mLoadedProductId); delete mCompat; mCompat = CompatFactory::createCompat( mLoadedProductId ); } // TODO: check for unknown PRODID #if 0 if (!mCalendarVersion && CalFormat::productId() != mLoadedProductId) { // warn the user that we might have trouble reading non-known calendar. if (mEnableDialogs) KMessageBox::information(mTopWidget, i18n("This vCalendar file was not created by KOrganizer " "or any other product we support. Loading anyway..."), i18n("%1: Unknown vCalendar Vendor").arg(CalFormat::application())); } #endif p = icalcomponent_get_first_property(calendar,ICAL_VERSION_PROPERTY); if (!p) { mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); return false; } else { const char *version = icalproperty_get_version(p); if (strcmp(version,"1.0") == 0) { mParent->setException(new ErrorFormat(ErrorFormat::CalVersion1, i18n("Expected iCalendar format"))); return false; } else if (strcmp(version,"2.0") != 0) { mParent->setException(new ErrorFormat(ErrorFormat::CalVersionUnknown)); return false; } } // TODO: check for calendar format version #if 0 // warn the user we might have trouble reading this unknown version. if ((curVO = isAPropertyOf(vcal, VCVersionProp)) != 0) { char *s = fakeCString(vObjectUStringZValue(curVO)); if (strcmp(_VCAL_VERSION, s) != 0) if (mEnableDialogs) KMessageBox::sorry(mTopWidget, i18n("This vCalendar file has version %1.\n" "We only support %2.") .arg(s).arg(_VCAL_VERSION), i18n("%1: Unknown vCalendar Version").arg(CalFormat::application())); deleteStr(s); } #endif // custom properties readCustomProperties(calendar, cal); // TODO: set time zone #if 0 // set the time zone if ((curVO = isAPropertyOf(vcal, VCTimeZoneProp)) != 0) { char *s = fakeCString(vObjectUStringZValue(curVO)); cal->setTimeZone(s); deleteStr(s); } #endif // Store all events with a relatedTo property in a list for post-processing mEventsRelate.clear(); mTodosRelate.clear(); // TODO: make sure that only actually added ecvens go to this lists. icalcomponent *c; // Iterate through all todos c = icalcomponent_get_first_component(calendar,ICAL_VTODO_COMPONENT); while (c) { // kdDebug(5800) << "----Todo found" << endl; Todo *todo = readTodo(c); if (!cal->todo(todo->uid())) cal->addTodo(todo); diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp index 5fc5d1f..dd74e10 100644 --- a/libkcal/recurrence.cpp +++ b/libkcal/recurrence.cpp @@ -273,840 +273,853 @@ QDate Recurrence::endDate() const 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 + 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: 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)); 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; } } diff --git a/libkcal/recurrence.h b/libkcal/recurrence.h index a0f6d84..b13d14f 100644 --- a/libkcal/recurrence.h +++ b/libkcal/recurrence.h @@ -1,401 +1,401 @@ /* 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. */ #ifndef KCAL_RECURRENCE_H #define KCAL_RECURRENCE_H #include <qstring.h> #include <qbitarray.h> #include <qptrlist.h> namespace KCal { class Incidence; /** This class represents a recurrence rule for a calendar incidence. */ class Recurrence { public: /** enumeration for describing how an event recurs, if at all. */ enum { rNone = 0, rMinutely = 0x001, rHourly = 0x0002, rDaily = 0x0003, rWeekly = 0x0004, rMonthlyPos = 0x0005, rMonthlyDay = 0x0006, rYearlyMonth = 0x0007, rYearlyDay = 0x0008, rYearlyPos = 0x0009 }; /** Enumeration for specifying what date yearly recurrences of February 29th occur * in non-leap years. */ enum Feb29Type { rMar1, // recur on March 1st (default) rFeb28, // recur on February 28th rFeb29 // only recur on February 29th, i.e. don't recur in non-leap years }; /** structure for Recurs rMonthlyPos */ struct rMonthPos { QBitArray rDays; short rPos; bool negative; }; Recurrence(Incidence *parent, int compatVersion = 0); Recurrence(const Recurrence&, Incidence *parent); ~Recurrence(); bool operator==( const Recurrence& ) const; bool operator!=( const Recurrence& r ) const { return !operator==(r); } Incidence *parent() { return mParent; } /** Return the start of the recurrence */ QDateTime recurStart() const { return mRecurStart; } /** Returns the number of exception dates for the recurrence */ int recurExDatesCount() const { return mRecurExDatesCount; } /** Set start of recurrence, as a date and time. */ void setRecurStart(const QDateTime &start); /** Set start of recurrence, as a date with no time. * Recurrence types which are sub-daily (e.g. rHourly) always have a time; * the time is set to 00:00:00 in these cases. */ void setRecurStart(const QDate &start); /** Set whether the recurrence has no time, just a date. * Recurrence types which are sub-daily (e.g. rHourly) always have a time * and cannot be set to float. * N.B. This property is derived by default from the parent incidence, * or according to whether a time is specified in setRecurStart(). */ void setFloats(bool f); /** Returns whether the recurrence has no time, just a date. */ bool doesFloat() const { return mFloats; } /** Set if recurrence is read-only or can be changed. */ void setRecurReadOnly(bool readOnly) { mRecurReadOnly = readOnly; } bool recurReadOnly() const { return mRecurReadOnly; } /** Set number of exception dates. */ void setRecurExDatesCount(int count) { if (count >= 0) mRecurExDatesCount = count; } /** Set the calendar file version for backwards compatibility. * @var version is the KOrganizer/libkcal version, e.g. 220 for KDE 2.2.0. * Specify version = 0 to cancel compatibility mode. */ void setCompatVersion(int version = 0); /** Returns the event's recurrence status. See the enumeration at the top * of this file for possible values. */ ushort doesRecur() const; /** Returns true if the date specified is one on which the event will * recur. */ bool recursOnPure(const QDate &qd) const; /** Returns true if the date/time specified is one at which the event will * recur. Times are rounded down to the nearest minute to determine the result. */ bool recursAtPure(const QDateTime &) const; /** Turns off recurrence for the event. */ void unsetRecurs(); /** Returns the date of the next recurrence, after the specified date. * @var preDate the date after which to find the recurrence. * @var last if 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 getNextDate(const QDate& preDate, bool* last = 0) const; /** Returns the date and time of the next recurrence, after the specified date/time. * If the recurrence has no time, the next date after the specified date is returned. * @var preDate the date/time after which to find the recurrence. * @var last if non-null, *last is set to true if the next recurrence is the * last recurrence, else false. * Reply = date/time of next recurrence, or invalid date if none. */ QDateTime getNextDateTime(const QDateTime& preDateTime, bool* last = 0) const; /** Returns the date of the last previous recurrence, before the specified date. * @var afterDate the date before which to find the recurrence. * @var last if non-null, *last is set to true if the previous recurrence is the * last recurrence, else false. * Reply = date of previous recurrence, or invalid date if none. */ QDate getPreviousDate(const QDate& afterDate, bool* last = 0) const; /** Returns the date and time of the last previous recurrence, before the specified date/time. * If a time later than 00:00:00 is specified and the recurrence has no time, 00:00:00 on * the specified date is returned if that date recurs. * @var afterDate the date/time before which to find the recurrence. * @var last if non-null, *last is set to true if the previous recurrence is the * last recurrence, else false. * Reply = date/time of previous recurrence, or invalid date if none. */ QDateTime getPreviousDateTime(const QDateTime& afterDateTime, bool* last = 0) const; /** Returns frequency of recurrence, in terms of the recurrence time period type. */ int frequency() const; /** Returns the total number of recurrences, including the initial occurrence. */ int duration() const; /** Sets the total number of times the event is to occur, including both the * first and last. */ void setDuration(int duration); /** Returns the number of recurrences up to and including the date specified. */ int durationTo(const QDate &) const; /** Returns the number of recurrences up to and including the date/time specified. */ int durationTo(const QDateTime &) const; /** Returns the date of the last recurrence. * An invalid date is returned if the recurrence has no end. * Note: for some recurrence types, endDate() can involve significant calculation. */ QDate endDate() const; /** Returns the date and time of the last recurrence. * An invalid date is returned if the recurrence has no end. * Note: for some recurrence types, endDateTime() can involve significant calculation. */ QDateTime endDateTime() const; /** Returns a string representing the recurrence end date in the format according to the user's locale settings. */ QString endDateStr(bool shortfmt=true) const; /** Sets an event to recur minutely. * @var _rFreq the frequency to recur, e.g. 2 is every other minute * @var duration the number of times the event is to occur, or -1 to recur indefinitely. */ void setMinutely(int _rFreq, int duration); /** Sets an event to recur minutely. * @var _rFreq the frequency to recur, e.g. 2 is every other minute * @var endDateTime the ending date/time after which to stop recurring */ void setMinutely(int _rFreq, const QDateTime &endDateTime); /** Sets an event to recur hourly. * @var _rFreq the frequency to recur, e.g. 2 is every other hour * @var duration the number of times the event is to occur, or -1 to recur indefinitely. */ void setHourly(int _rFreq, int duration); /** Sets an event to recur hourly. * @var _rFreq the frequency to recur, e.g. 2 is every other hour * @var endDateTime the ending date/time after which to stop recurring */ void setHourly(int _rFreq, const QDateTime &endDateTime); /** Sets an event to recur daily. * @var _rFreq the frequency to recur, e.g. 2 is every other day * @var duration the number of times the event is to occur, or -1 to recur indefinitely. */ void setDaily(int _rFreq, int duration); /** Sets an event to recur daily. * @var _rFreq the frequency to recur, e.g. 2 is every other day * @var endDate the ending date after which to stop recurring */ void setDaily(int _rFreq, const QDate &endDate); /** Sets an event to recur weekly. * @var _rFreq the frequency to recur, e.g. every other week etc. * @var _rDays a 7 bit array indicating which days on which to recur (bit 0 = Monday). * @var duration the number of times the event is to occur, or -1 to recur indefinitely. * @var weekStart the first day of the week (Monday=1 .. Sunday=7, default is Monday). */ void setWeekly(int _rFreq, const QBitArray &_rDays, int duration, int weekStart = 1); /** Sets an event to recur weekly. * @var _rFreq the frequency to recur, e.g. every other week etc. * @var _rDays a 7 bit array indicating which days on which to recur (bit 0 = Monday). * @var endDate the date on which to stop recurring. * @var weekStart the first day of the week (Monday=1 .. Sunday=7, default is Monday). */ void setWeekly(int _rFreq, const QBitArray &_rDays, const QDate &endDate, int weekStart = 1); /** Returns the first day of the week. Monday=1 .. Sunday=7. */ int weekStart() const { return rWeekStart; } /** Returns week day mask (bit 0 = Monday). */ const QBitArray &days() const; /** Sets an event to recur monthly. * @var type rMonthlyPos or rMonthlyDay * @var _rFreq the frequency to recur, e.g. 3 for every third month. * @var duration the number of times the event is to occur, or -1 to recur indefinitely. */ void setMonthly(short type, int _rFreq, int duration); /** same as above, but with ending date not number of recurrences */ void setMonthly(short type, int _rFreq, const QDate &endDate); /** Adds a position to the recursMonthlyPos recurrence rule, if it is * set. * @var _rPos the position in the month for the recurrence, with valid * values being 1-5 (5 weeks max in a month). * @var _rDays the days for the position to recur on (bit 0 = Monday). * Example: _rPos = 2, and bits 0 and 2 are set in _rDays: * the rule is to repeat every 2nd Monday and Wednesday in the month. */ void addMonthlyPos(short _rPos, const QBitArray &_rDays); /** Adds a position the the recursMonthlyDay list. * @var _rDay the date in the month to recur. */ void addMonthlyDay(short _rDay); /** Returns list of day positions in months. */ const QPtrList<rMonthPos> &monthPositions() const; /** Returns list of day numbers of a month. */ const QPtrList<int> &monthDays() const; /** Sets an event to recur yearly. * @var type rYearlyMonth, rYearlyPos or rYearlyDay * @var freq the frequency to recur, e.g. 3 for every third year. * @var duration the number of times the event is to occur, or -1 to recur indefinitely. */ void setYearly(int type, int freq, int duration); /** Sets an event to recur yearly ending at \a endDate. */ void setYearly(int type, int freq, const QDate &endDate); /** Sets an event to recur yearly on specified dates. * The dates must be specified by calling addYearlyNum(). * @var type the way recurrences of February 29th are to be handled in non-leap years. * @var freq the frequency to recur, e.g. 3 for every third year. * @var duration the number of times the event is to occur, or -1 to recur indefinitely. */ void setYearlyByDate(Feb29Type type, int freq, int duration); /** Sets an event to recur yearly ending at \a endDate. */ void setYearlyByDate(Feb29Type type, int freq, const QDate &endDate); /** Adds position of day or month in year. * N.B. for recursYearlyPos, addYearlyMonthPos() must also be called * to add positions within the month. */ void addYearlyNum(short _rNum); /** Adds a position to the recursYearlyPos recurrence rule, if it is set. * N.B. addYearlyNum() must also be called to add recurrence months. * Parameters are the same as for addMonthlyPos(). */ void addYearlyMonthPos(short _rPos, const QBitArray &_rDays); /** Returns positions of days or months in year. */ const QPtrList<int> &yearNums() const; /** Returns list of day positions in months, for a recursYearlyPos recurrence rule. */ const QPtrList<rMonthPos> &yearMonthPositions() const; /** Returns how yearly recurrences of February 29th are handled. */ Feb29Type feb29YearlyType() const { return mFeb29YearlyType; } /** Sets the default method for handling yearly recurrences of February 29th. */ static void setFeb29YearlyTypeDefault(Feb29Type t) { mFeb29YearlyDefaultType = t; } /** Returns the default method for handling yearly recurrences of February 29th. */ static Feb29Type setFeb29YearlyTypeDefault() { return mFeb29YearlyDefaultType; } - + void addYearlyMonth(short _rPos ); // added LR /** Debug output. */ void dump() const; QString recurrenceText() const; + bool getYearlyMonthMonths(int day, QValueList<int>&, + QValueList<int> &leaplist) const; protected: enum PeriodFunc { END_DATE_AND_COUNT, COUNT_TO_DATE, NEXT_AFTER_DATE }; struct MonthlyData; friend struct MonthlyData; struct YearlyMonthData; friend struct YearlyMonthData; struct YearlyPosData; friend struct YearlyPosData; struct YearlyDayData; friend struct YearlyDayData; bool recursSecondly(const QDate &, int secondFreq) const; bool recursMinutelyAt(const QDateTime &dt, int minuteFreq) const; bool recursDaily(const QDate &) const; bool recursWeekly(const QDate &) const; bool recursMonthly(const QDate &) const; bool recursYearlyByMonth(const QDate &) const; bool recursYearlyByPos(const QDate &) const; bool recursYearlyByDay(const QDate &) const; QDate getNextDateNoTime(const QDate& preDate, bool* last) const; QDate getPreviousDateNoTime(const QDate& afterDate, bool* last) const; void addMonthlyPos_(short _rPos, const QBitArray &_rDays); void setDailySub(short type, int freq, int duration); void setYearly_(short type, Feb29Type, int freq, int duration); int recurCalc(PeriodFunc, QDate &enddate) const; int recurCalc(PeriodFunc, QDateTime &endtime) const; int secondlyCalc(PeriodFunc, QDateTime& endtime, int freq) const; int dailyCalc(PeriodFunc, QDate &enddate) const; int weeklyCalc(PeriodFunc, QDate &enddate) const; int weeklyCalcEndDate(QDate& enddate, int daysPerWeek) const; int weeklyCalcToDate(const QDate& enddate, int daysPerWeek) const; int weeklyCalcNextAfter(QDate& enddate, int daysPerWeek) const; int monthlyCalc(PeriodFunc, QDate &enddate) const; int monthlyCalcEndDate(QDate& enddate, MonthlyData&) const; int monthlyCalcToDate(const QDate& enddate, MonthlyData&) const; int monthlyCalcNextAfter(QDate& enddate, MonthlyData&) const; int yearlyMonthCalc(PeriodFunc, QDate &enddate) const; int yearlyMonthCalcEndDate(QDate& enddate, YearlyMonthData&) const; int yearlyMonthCalcToDate(const QDate& enddate, YearlyMonthData&) const; int yearlyMonthCalcNextAfter(QDate& enddate, YearlyMonthData&) const; int yearlyPosCalc(PeriodFunc, QDate &enddate) const; int yearlyPosCalcEndDate(QDate& enddate, YearlyPosData&) const; int yearlyPosCalcToDate(const QDate& enddate, YearlyPosData&) const; int yearlyPosCalcNextAfter(QDate& enddate, YearlyPosData&) const; int yearlyDayCalc(PeriodFunc, QDate &enddate) const; int yearlyDayCalcEndDate(QDate& enddate, YearlyDayData&) const; int yearlyDayCalcToDate(const QDate& enddate, YearlyDayData&) const; int yearlyDayCalcNextAfter(QDate& enddate, YearlyDayData&) const; int countMonthlyPosDays() const; void getMonthlyPosDays(QValueList<int>&, int daysInMonth, int startDayOfWeek) const; bool getMonthlyDayDays(QValueList<int>&, int daysInMonth) const; - bool getYearlyMonthMonths(int day, QValueList<int>&, - QValueList<int> &leaplist) const; int getFirstDayInWeek(int startDay, bool useWeekStart = true) const; int getLastDayInWeek(int endDay, bool useWeekStart = true) const; QDate getFirstDateInMonth(const QDate& earliestDate) const; QDate getLastDateInMonth(const QDate& latestDate) const; QDate getFirstDateInYear(const QDate& earliestDate) const; QDate getLastDateInYear(const QDate& latestDate) const; private: // Prohibit copying Recurrence(const Recurrence&); Recurrence &operator=(const Recurrence&); short recurs; // should be one of the enums. int rWeekStart; // day which starts the week, Monday=1 .. Sunday=7 QBitArray rDays; // array of days during week it recurs QPtrList<rMonthPos> rMonthPositions; // list of positions during a month // on which an event recurs QPtrList<int> rMonthDays; // list of days during a month on // which the event recurs QPtrList<int> rYearNums; // either months/days to recur on for rYearly, // sorted in numerical order int rFreq; // frequency of period // one of the following must be specified int rDuration; // num times to recur (inc. first occurrence), -1 = infinite QDateTime rEndDateTime; // date/time at which to end recurrence QDateTime mRecurStart; // date/time of first recurrence bool mFloats; // the recurrence has no time, just a date bool mRecurReadOnly; int mRecurExDatesCount; // number of recurrences (in addition to rDuration) which are excluded Feb29Type mFeb29YearlyType; // how to handle yearly recurrences of February 29th static Feb29Type mFeb29YearlyDefaultType; // default value for mFeb29YearlyType // Backwards compatibility for KDE < 3.1. int mCompatVersion; // calendar file version for backwards compatibility short mCompatRecurs; // original 'recurs' in old calendar format, or rNone int mCompatDuration; // original 'rDuration' in old calendar format, or 0 Incidence *mParent; }; } #endif |