/* Copyright (c) 2010 Bertjan Broeksema Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "incidencedatetime.h" #ifdef KDEPIM_MOBILE_UI #include "ui_dialogmobile.h" #include "ui_dialogmoremobile.h" #else #include "ui_dialogdesktop.h" #endif //#ifdef KDEPIM_MOBILE_UI //#include "ui_iedatetimemobile.h" //#else //#include "../editoralarms.h" //#include "ui_incidencedatetime.h" //#endif #include #include #include #include #include using namespace IncidenceEditorNG; /** * Returns true if the incidence's dates are equal to the default ones specified in config. */ static bool incidenceHasDefaultTimes( const KCalCore::Incidence::Ptr &incidence ) { if ( !incidence || incidence->allDay() ) { return false; } QTime defaultDuration = CalendarSupport::KCalPrefs::instance()->defaultDuration().time(); if ( !defaultDuration.isValid() ) { return false; } QTime defaultStart = CalendarSupport::KCalPrefs::instance()->mStartTime.time(); if ( !defaultStart.isValid() ) { return false; } if ( incidence->dtStart().time() == defaultStart ) { if ( incidence->type() == KCalCore::Incidence::TypeJournal ) { return true; // no duration to compare with } const KDateTime start = incidence->dtStart(); const KDateTime end = incidence->dateTime( KCalCore::Incidence::RoleEnd ); if ( !end.isValid() || !start.isValid() ) { return false; } const int durationInSeconds = defaultDuration.hour() * 3600 + defaultDuration.minute() * 60; return start.secsTo( end ) == durationInSeconds; } return false; } IncidenceDateTime::IncidenceDateTime( Ui::EventOrTodoDesktop *ui ) : IncidenceEditor( 0 ), mTimeZones( new KCalCore::ICalTimeZones ), mUi( ui ), mTimezoneCombosWereVisibile( false ) { setTimeZonesVisibility( false ); setObjectName( "IncidenceDateTime" ); #ifdef KDEPIM_MOBILE_UI Q_UNUSED(mTimezoneCombosWereVisibile); mUi->mTimeZoneComboStart->setVisible( false ); mUi->mTimeZoneComboEnd->setVisible( false ); // We don't want to see the combobox list / calendar in the mobile version mUi->mStartDateEdit->setOptions( mUi->mStartDateEdit->options() & ~KDateComboBox::EditDate ); mUi->mEndDateEdit->setOptions( mUi->mEndDateEdit->options() & ~KDateComboBox::EditDate ); mUi->mStartTimeEdit->clear(); mUi->mEndTimeEdit->clear(); // This event filter is not needed in the desktop version mUi->mStartDateEdit->installEventFilter( this ); mUi->mEndDateEdit->installEventFilter( this ); mUi->mStartTimeEdit->installEventFilter( this ); mUi->mEndTimeEdit->installEventFilter( this ); #else mUi->mTimeZoneLabel->setVisible( !mUi->mWholeDayCheck->isChecked() ); mUi->mTimeZoneLabel->setContextMenuPolicy(Qt::NoContextMenu); connect( mUi->mTimeZoneLabel, SIGNAL(linkActivated(QString)), SLOT(toggleTimeZoneVisibility()) ); #endif QList lineEdits; lineEdits << mUi->mStartDateEdit->lineEdit() << mUi->mEndDateEdit->lineEdit() << mUi->mStartTimeEdit->lineEdit() << mUi->mEndTimeEdit->lineEdit(); foreach ( QLineEdit *lineEdit, lineEdits ) { KLineEdit *klineEdit = qobject_cast( lineEdit ); if ( klineEdit ) { klineEdit->setClearButtonShown( false ); } } connect( mUi->mFreeBusyCheck, SIGNAL(toggled(bool)), SLOT(checkDirtyStatus()) ); connect( mUi->mWholeDayCheck, SIGNAL(toggled(bool)), SLOT(enableTimeEdits()) ); connect( mUi->mWholeDayCheck, SIGNAL(toggled(bool)), SLOT(checkDirtyStatus()) ); connect( this, SIGNAL(startDateChanged(QDate)), SLOT(updateStartToolTips()) ); connect( this, SIGNAL(startTimeChanged(QTime)), SLOT(updateStartToolTips()) ); connect( this, SIGNAL(endDateChanged(QDate)), SLOT(updateEndToolTips()) ); connect( this, SIGNAL(endTimeChanged(QTime)), SLOT(updateEndToolTips()) ); connect( mUi->mWholeDayCheck, SIGNAL(toggled(bool)), SLOT(updateStartToolTips()) ); connect( mUi->mWholeDayCheck, SIGNAL(toggled(bool)), SLOT(updateEndToolTips()) ); connect( mUi->mStartCheck, SIGNAL(toggled(bool)), SLOT(updateStartToolTips()) ); connect( mUi->mEndCheck, SIGNAL(toggled(bool)), SLOT(updateEndToolTips()) ); } IncidenceDateTime::~IncidenceDateTime() { delete mTimeZones; } bool IncidenceDateTime::eventFilter( QObject *obj, QEvent *event ) { if ( event->type() == QEvent::FocusIn ) { if ( obj == mUi->mStartDateEdit ) { kDebug() << "emiting startDateTime: " << mUi->mStartDateEdit; emit startDateFocus( obj ); } else if ( obj == mUi->mEndDateEdit ) { kDebug() << "emiting endDateTime: " << mUi->mEndDateEdit; emit endDateFocus( obj ); } else if ( obj == mUi->mStartTimeEdit ) { kDebug() << "emiting startTimeTime: " << mUi->mStartTimeEdit; emit startTimeFocus( obj ); } else if ( obj == mUi->mEndTimeEdit ) { kDebug() << "emiting endTimeTime: " << mUi->mEndTimeEdit; emit endTimeFocus( obj ); } return true; } else { // standard event processing return QObject::eventFilter( obj, event ); } } void IncidenceDateTime::load( const KCalCore::Incidence::Ptr &incidence ) { if ( mLoadedIncidence && *mLoadedIncidence == *incidence ) { return; } const bool isTemplate = incidence->customProperty( "kdepim", "isTemplate" ) == "true"; const bool templateOverridesTimes = incidenceHasDefaultTimes( mLoadedIncidence ); mLoadedIncidence = incidence; mLoadingIncidence = true; // We can only handle events or todos. if ( KCalCore::Todo::Ptr todo = IncidenceDateTime::incidence() ) { load( todo, isTemplate, templateOverridesTimes ); } else if ( KCalCore::Event::Ptr event = IncidenceDateTime::incidence() ) { load( event, isTemplate, templateOverridesTimes ); } else if ( KCalCore::Journal::Ptr journal = IncidenceDateTime::incidence() ) { load( journal, isTemplate, templateOverridesTimes ); } else { kDebug() << "Not an Incidence."; } // Set the initial times before calling enableTimeEdits, as enableTimeEdits // assumes that the initial times are initialized. mInitialStartDT = currentStartDateTime(); mInitialEndDT = currentEndDateTime(); enableTimeEdits(); if ( mUi->mTimeZoneComboStart->currentIndex() == 0 ) { // Floating mInitialStartDT.setTimeSpec( mInitialStartDT.toLocalZone().timeSpec() ); } if ( mUi->mTimeZoneComboEnd->currentIndex() == 0 ) { // Floating mInitialEndDT.setTimeSpec( mInitialEndDT.toLocalZone().timeSpec() ); } mWasDirty = false; mLoadingIncidence = false; } void IncidenceDateTime::save( const KCalCore::Incidence::Ptr &incidence ) { if ( KCalCore::Todo::Ptr todo = IncidenceDateTime::incidence( incidence ) ) { save( todo ); } else if ( KCalCore::Event::Ptr event = IncidenceDateTime::incidence( incidence ) ) { save( event ); } else if ( KCalCore::Journal::Ptr journal = IncidenceDateTime::incidence( incidence ) ) { save( journal ); } else { Q_ASSERT_X( false, "IncidenceDateTimeEditor::save", "Only implemented for todos, events and journals" ); } } bool IncidenceDateTime::isDirty() const { if ( KCalCore::Todo::Ptr todo = IncidenceDateTime::incidence() ) { return isDirty( todo ); } else if ( KCalCore::Event::Ptr event = IncidenceDateTime::incidence() ) { return isDirty( event ); } else if ( KCalCore::Journal::Ptr journal = IncidenceDateTime::incidence() ) { return isDirty( journal ); } else { Q_ASSERT_X( false, "IncidenceDateTimeEditor::isDirty", "Only implemented for todos and events" ); return false; } } void IncidenceDateTime::setActiveDate( const QDate &activeDate ) { mActiveDate = activeDate; } QDate IncidenceDateTime::startDate() const { return currentStartDateTime().date(); } QDate IncidenceDateTime::endDate() const { return currentEndDateTime().date(); } QTime IncidenceDateTime::startTime() const { return currentStartDateTime().time(); } QTime IncidenceDateTime::endTime() const { return currentEndDateTime().time(); } /// private slots for General void IncidenceDateTime::setTimeZonesVisibility( bool visible ) { #ifndef KDEPIM_MOBILE_UI static const QString tz( i18nc( "@action show or hide the time zone widgets", "Time zones" ) ); QString placeholder( "<< %1" ); if ( visible ) { placeholder = placeholder.arg( tz ); } else { placeholder = QString( "%1 >>" ); placeholder = placeholder.arg( tz ); } mUi->mTimeZoneLabel->setText( placeholder ); #endif mUi->mTimeZoneComboStart->setVisible( visible ); mUi->mTimeZoneComboEnd->setVisible( visible && type() != KCalCore::Incidence::TypeJournal ); } void IncidenceDateTime::toggleTimeZoneVisibility() { setTimeZonesVisibility( !mUi->mTimeZoneComboStart->isVisible() ); } void IncidenceDateTime::updateStartTime( const QTime &newTime ) { if ( !newTime.isValid() ) { return; } KDateTime endDateTime = currentEndDateTime(); const int secsep = mCurrentStartDateTime.secsTo( endDateTime ); mCurrentStartDateTime.setTime( newTime ); if ( mUi->mEndCheck->isChecked() ) { // Only update the end time when it is actually enabled, adjust end time so // that the event/todo has the same duration as before. endDateTime = mCurrentStartDateTime.addSecs( secsep ); mUi->mEndTimeEdit->setTime( endDateTime.time() ); mUi->mEndDateEdit->setDate( endDateTime.date() ); } emit startTimeChanged( mCurrentStartDateTime.time() ); checkDirtyStatus(); } void IncidenceDateTime::updateStartDate( const QDate &newDate ) { if ( !newDate.isValid() ) { return; } const bool dateChanged = mCurrentStartDateTime.date().day() != newDate.day() || mCurrentStartDateTime.date().month() != newDate.month(); KDateTime endDateTime = currentEndDateTime(); int daysep = mCurrentStartDateTime.daysTo( endDateTime ); mCurrentStartDateTime.setDate( newDate ); if ( mUi->mEndCheck->isChecked() ) { // Only update the end time when it is actually enabled, adjust end time so // that the event/todo has the same duration as before. endDateTime.setDate( mCurrentStartDateTime.date().addDays( daysep ) ); mUi->mEndDateEdit->setDate( endDateTime.date() ); } checkDirtyStatus(); if ( dateChanged ) { emit startDateChanged( mCurrentStartDateTime.date() ); } } void IncidenceDateTime::updateStartSpec() { const QDate prevDate = mCurrentStartDateTime.date(); if ( mUi->mEndCheck->isChecked() && currentEndDateTime().timeSpec() == mCurrentStartDateTime.timeSpec() ) { mUi->mTimeZoneComboEnd->selectTimeSpec( mUi->mTimeZoneComboStart->selectedTimeSpec() ); } mCurrentStartDateTime.setTimeSpec( mUi->mTimeZoneComboStart->selectedTimeSpec() ); const bool dateChanged = mCurrentStartDateTime.date().day() != prevDate.day() || mCurrentStartDateTime.date().month() != prevDate.month(); if ( dateChanged ) { emit startDateChanged( mCurrentStartDateTime.date() ); } if ( type() == KCalCore::Incidence::TypeJournal ) { checkDirtyStatus(); } } /// private slots for Todo void IncidenceDateTime::enableStartEdit( bool enable ) { mUi->mStartDateEdit->setEnabled( enable ); if ( mUi->mEndCheck->isChecked() || mUi->mStartCheck->isChecked() ) { mUi->mWholeDayCheck->setEnabled( true ); setTimeZoneLabelEnabled( !mUi->mWholeDayCheck->isChecked() ); } else { mUi->mWholeDayCheck->setEnabled( false ); mUi->mWholeDayCheck->setChecked( false ); setTimeZoneLabelEnabled( false ); } if ( enable ) { mUi->mStartTimeEdit->setEnabled( !mUi->mWholeDayCheck->isChecked() ); mUi->mTimeZoneComboStart->setEnabled( !mUi->mWholeDayCheck->isChecked() ); } else { mUi->mStartTimeEdit->setEnabled( false ); mUi->mTimeZoneComboStart->setEnabled( false ); } mUi->mTimeZoneComboStart->setFloating( !mUi->mTimeZoneComboStart->isEnabled() ); checkDirtyStatus(); } void IncidenceDateTime::enableEndEdit( bool enable ) { mUi->mEndDateEdit->setEnabled( enable ); if( mUi->mEndCheck->isChecked() || mUi->mStartCheck->isChecked() ) { mUi->mWholeDayCheck->setEnabled( true ); setTimeZoneLabelEnabled( !mUi->mWholeDayCheck->isChecked() ); } else { mUi->mWholeDayCheck->setEnabled( false ); mUi->mWholeDayCheck->setChecked( false ); setTimeZoneLabelEnabled( false ); } if ( enable ) { mUi->mEndTimeEdit->setEnabled( !mUi->mWholeDayCheck->isChecked() ); mUi->mTimeZoneComboEnd->setEnabled( !mUi->mWholeDayCheck->isChecked() ); } else { mUi->mEndTimeEdit->setEnabled( false ); mUi->mTimeZoneComboEnd->setEnabled( false ); } mUi->mTimeZoneComboEnd->setFloating( !mUi->mTimeZoneComboEnd->isEnabled() ); checkDirtyStatus(); } bool IncidenceDateTime::timeZonesAreLocal( const KDateTime &start, const KDateTime &end ) { // Returns false if the incidence start or end timezone is not the local zone. if ( ( start.isValid() && !start.timeSpec().isLocalZone() ) || ( end.isValid() && !end.timeSpec().isLocalZone() ) ) { return false; } else { return true; } } void IncidenceDateTime::enableTimeEdits() { // NOTE: assumes that the initial times are initialized. const bool wholeDayChecked = mUi->mWholeDayCheck->isChecked(); setTimeZoneLabelEnabled( !wholeDayChecked ); if ( mUi->mStartCheck->isChecked() ) { mUi->mStartTimeEdit->setEnabled( !wholeDayChecked ); mUi->mTimeZoneComboStart->setEnabled( !wholeDayChecked ); mUi->mTimeZoneComboStart->setFloating( wholeDayChecked, mInitialStartDT.timeSpec() ); } if ( mUi->mEndCheck->isChecked() ) { mUi->mEndTimeEdit->setEnabled( !wholeDayChecked ); mUi->mTimeZoneComboEnd->setEnabled( !wholeDayChecked ); mUi->mTimeZoneComboEnd->setFloating( wholeDayChecked, mInitialEndDT.timeSpec() ); } /** When editing a whole-day event, unchecking mWholeDayCheck shouldn't set both times to 00:00. DTSTART must always be smaller than DTEND */ if ( sender() == mUi->mWholeDayCheck && !wholeDayChecked && // Somebody unchecked it, the incidence will now have time. mUi->mStartCheck->isChecked() && mUi->mEndCheck->isChecked() && // The incidence has both start and end/due dates currentStartDateTime() == currentEndDateTime() ) { // DTSTART == DTEND. This is illegal, lets correct it. // Not sure about the best time here... doesn't really matter, when someone unchecks mWholeDayCheck, she will // always want to set a time. mUi->mStartTimeEdit->setTime( QTime( 0, 0 ) ); mUi->mEndTimeEdit->setTime( QTime( 1, 0 ) ); } #ifndef KDEPIM_MOBILE_UI const bool currentlyVisible = mUi->mTimeZoneLabel->text().contains( "<<" ); setTimeZonesVisibility( !wholeDayChecked && mTimezoneCombosWereVisibile ); mTimezoneCombosWereVisibile = currentlyVisible; if ( !wholeDayChecked && !timeZonesAreLocal( currentStartDateTime(), currentEndDateTime() ) ) { setTimeZonesVisibility( true ); mTimezoneCombosWereVisibile = true; } #endif } bool IncidenceDateTime::isDirty( const KCalCore::Todo::Ptr &todo ) const { Q_ASSERT( todo ); const bool hasDateTimes = mUi->mStartCheck->isChecked() || mUi->mEndCheck->isChecked(); // First check the start time/date of the todo if ( todo->hasStartDate() != mUi->mStartCheck->isChecked() ) { return true; } if ( ( hasDateTimes && todo->allDay() ) != mUi->mWholeDayCheck->isChecked() ) { return true; } if ( todo->hasDueDate() != mUi->mEndCheck->isChecked() ) { return true; } if ( mUi->mStartCheck->isChecked() ) { // Use mActiveStartTime. This is the KDateTime::Spec selected on load coming from // the combobox. We use this one as it can slightly differ (e.g. missing // country code in the incidence time spec) from the incidence. if ( currentStartDateTime() != mInitialStartDT ) { return true; } } if ( mUi->mEndCheck->isChecked() && currentEndDateTime() != mInitialEndDT ) { return true; } return false; } /// Event specific methods bool IncidenceDateTime::isDirty( const KCalCore::Event::Ptr &event ) const { if ( event->allDay() != mUi->mWholeDayCheck->isChecked() ) { return true; } if ( mUi->mFreeBusyCheck->isChecked() && event->transparency() != KCalCore::Event::Opaque ) { return true; } if ( !mUi->mFreeBusyCheck->isChecked() && event->transparency() != KCalCore::Event::Transparent ) { return true; } if ( event->allDay() ) { if ( mUi->mStartDateEdit->date() != mInitialStartDT.date() || mUi->mEndDateEdit->date() != mInitialEndDT.date() ) { return true; } } else { if ( currentStartDateTime() != mInitialStartDT || currentEndDateTime() != mInitialEndDT || currentStartDateTime().timeSpec() != mInitialStartDT.timeSpec() || currentEndDateTime().timeSpec() != mInitialEndDT.timeSpec() ) { return true; } } return false; } bool IncidenceDateTime::isDirty( const KCalCore::Journal::Ptr &journal ) const { if ( journal->allDay() != mUi->mWholeDayCheck->isChecked() ) { return true; } if ( journal->allDay() ) { if ( mUi->mStartDateEdit->date() != mInitialStartDT.date() ) { return true; } } else { if ( currentStartDateTime() != mInitialStartDT ) { return true; } } return false; } /// Private methods KDateTime IncidenceDateTime::currentStartDateTime() const { return KDateTime( mUi->mStartDateEdit->date(), mUi->mStartTimeEdit->time(), mUi->mTimeZoneComboStart->selectedTimeSpec() ); } KDateTime IncidenceDateTime::currentEndDateTime() const { return KDateTime( mUi->mEndDateEdit->date(), mUi->mEndTimeEdit->time(), mUi->mTimeZoneComboEnd->selectedTimeSpec() ); } void IncidenceDateTime::load( const KCalCore::Event::Ptr &event, bool isTemplate, bool templateOverridesTimes ) { // First en/disable the necessary ui bits and pieces mUi->mStartCheck->setVisible( false ); mUi->mStartCheck->setChecked( true ); // Set to checked so we can reuse enableTimeEdits. mUi->mEndCheck->setVisible( false ); mUi->mEndCheck->setChecked( true ); // Set to checked so we can reuse enableTimeEdits. // Start time connect( mUi->mStartTimeEdit, SIGNAL(timeChanged(QTime)), // when editing with mouse, or up/down arrows SLOT(updateStartTime(QTime)) ); connect( mUi->mStartTimeEdit, SIGNAL(timeEdited(QTime)), // When editing with any key except up/down SLOT(updateStartTime(QTime)) ); connect( mUi->mStartDateEdit, SIGNAL(dateChanged(QDate)), SLOT(updateStartDate(QDate)) ); connect( mUi->mTimeZoneComboStart, SIGNAL(currentIndexChanged(int)), SLOT(updateStartSpec()) ); // End time connect( mUi->mEndTimeEdit, SIGNAL(timeChanged(QTime)), SLOT(checkDirtyStatus()) ); connect( mUi->mEndTimeEdit, SIGNAL(timeEdited(QTime)), SLOT(checkDirtyStatus()) ); connect( mUi->mEndDateEdit, SIGNAL(dateChanged(QDate)), SLOT(checkDirtyStatus()) ); connect( mUi->mEndTimeEdit, SIGNAL(timeChanged(QTime)), SIGNAL(endTimeChanged(QTime)) ); connect( mUi->mEndTimeEdit, SIGNAL(timeEdited(QTime)), SIGNAL(endTimeChanged(QTime)) ); connect( mUi->mEndDateEdit, SIGNAL(dateChanged(QDate)), SIGNAL(endDateChanged(QDate)) ); connect( mUi->mTimeZoneComboEnd, SIGNAL(currentIndexChanged(int)), SLOT(checkDirtyStatus()) ); mUi->mWholeDayCheck->setChecked( event->allDay() ); enableTimeEdits(); if ( isTemplate ) { if ( templateOverridesTimes ) { // We only use the template times if the user didn't override them. setTimes( event->dtStart(), event->dtEnd() ); } } else { KDateTime startDT = event->dtStart(); KDateTime endDT = event->dtEnd(); setDateTimes( startDT, endDT ); } switch( event->transparency() ) { case KCalCore::Event::Transparent: mUi->mFreeBusyCheck->setChecked( false ); break; case KCalCore::Event::Opaque: mUi->mFreeBusyCheck->setChecked( true ); break; } } void IncidenceDateTime::load( const KCalCore::Journal::Ptr &journal, bool isTemplate, bool templateOverridesTimes ) { // First en/disable the necessary ui bits and pieces mUi->mStartCheck->setVisible( false ); mUi->mStartCheck->setChecked( true ); // Set to checked so we can reuse enableTimeEdits. mUi->mEndCheck->setVisible( false ); mUi->mEndCheck->setChecked( true ); // Set to checked so we can reuse enableTimeEdits. mUi->mEndDateEdit->setVisible( false ); mUi->mEndTimeEdit->setVisible( false ); mUi->mTimeZoneComboEnd->setVisible( false ); mUi->mEndLabel->setVisible( false ); mUi->mFreeBusyCheck->setVisible( false ); // Start time connect( mUi->mStartTimeEdit, SIGNAL(timeChanged(QTime)), SLOT(updateStartTime(QTime)) ); connect( mUi->mStartDateEdit, SIGNAL(dateChanged(QDate)), SLOT(updateStartDate(QDate)) ); connect( mUi->mTimeZoneComboStart, SIGNAL(currentIndexChanged(int)), SLOT(updateStartSpec()) ); mUi->mWholeDayCheck->setChecked( journal->allDay() ); enableTimeEdits(); if ( isTemplate ) { if ( templateOverridesTimes ) { // We only use the template times if the user didn't override them. setTimes( journal->dtStart(), KDateTime() ); } } else { KDateTime startDT = journal->dtStart(); // Convert UTC to local timezone, if needed (i.e. for kolab #204059) if ( startDT.isUtc() ) { startDT = startDT.toLocalZone(); } setDateTimes( startDT, KDateTime() ); } } void IncidenceDateTime::load( const KCalCore::Todo::Ptr &todo, bool isTemplate, bool templateOverridesTimes ) { // First en/disable the necessary ui bits and pieces mUi->mStartCheck->setVisible( true ); mUi->mStartCheck->setChecked( todo->hasStartDate() ); mUi->mStartDateEdit->setEnabled( todo->hasStartDate() ); mUi->mStartTimeEdit->setEnabled( todo->hasStartDate() ); mUi->mTimeZoneComboStart->setEnabled( todo->hasStartDate() ); mUi->mEndLabel->setText( i18nc( "@label The due date/time of a to-do", "Due:" ) ); mUi->mEndCheck->setVisible( true ); mUi->mEndCheck->setChecked( todo->hasDueDate() ); mUi->mEndDateEdit->setEnabled( todo->hasDueDate() ); mUi->mEndTimeEdit->setEnabled( todo->hasDueDate() ); mUi->mTimeZoneComboEnd->setEnabled( todo->hasDueDate() ); // These fields where not enabled in the old code either: mUi->mFreeBusyCheck->setVisible( false ); const bool hasDateTimes = mUi->mEndCheck->isChecked() || mUi->mStartCheck->isChecked(); mUi->mWholeDayCheck->setChecked( hasDateTimes && todo->allDay() ); mUi->mWholeDayCheck->setEnabled( hasDateTimes ); // Connect to the right logic connect( mUi->mStartCheck, SIGNAL(toggled(bool)), SLOT(enableStartEdit(bool)) ); connect( mUi->mStartCheck, SIGNAL(toggled(bool)), SIGNAL(startDateTimeToggled(bool)) ); connect( mUi->mStartDateEdit, SIGNAL(dateChanged(QDate)), SLOT(checkDirtyStatus()) ); connect( mUi->mStartTimeEdit, SIGNAL(timeChanged(QTime)), SLOT(updateStartTime(QTime)) ); connect( mUi->mTimeZoneComboStart, SIGNAL(currentIndexChanged(int)), SLOT(checkDirtyStatus()) ); connect( mUi->mEndCheck, SIGNAL(toggled(bool)), SLOT(enableEndEdit(bool)) ); connect( mUi->mEndCheck, SIGNAL(toggled(bool)), SIGNAL(endDateTimeToggled(bool)) ); connect( mUi->mEndDateEdit, SIGNAL(dateChanged(QDate)), SLOT(checkDirtyStatus()) ); connect( mUi->mEndTimeEdit, SIGNAL(timeChanged(QTime)), SLOT(checkDirtyStatus()) ); connect( mUi->mEndDateEdit, SIGNAL(dateChanged(QDate)), SIGNAL(endDateChanged(QDate)) ); connect( mUi->mEndTimeEdit, SIGNAL(timeChanged(QTime)), SIGNAL(endTimeChanged(QTime)) ); connect( mUi->mTimeZoneComboEnd, SIGNAL(currentIndexChanged(int)), SLOT(checkDirtyStatus()) ); const KDateTime rightNow = KDateTime( QDate::currentDate(), QTime::currentTime() ).toLocalZone(); if ( isTemplate ) { if ( templateOverridesTimes ) { // We only use the template times if the user didn't override them. setTimes( todo->dtStart(), todo->dateTime( KCalCore::Incidence::RoleEnd ) ); } } else { const KDateTime endDT = todo->hasDueDate() ? todo->dtDue( true/** first */ ) : rightNow; const KDateTime startDT = todo->hasStartDate() ? todo->dtStart( true/** first */ ) : rightNow; setDateTimes( startDT, endDT ); } } void IncidenceDateTime::save( const KCalCore::Event::Ptr &event ) { if ( mUi->mWholeDayCheck->isChecked() ) { // All day event event->setAllDay( true ); // TODO: need to change this. KDateTime eventDTStart = currentStartDateTime(); eventDTStart.setDateOnly( true ); event->setDtStart( eventDTStart ); KDateTime eventDTEnd = currentEndDateTime(); eventDTEnd.setDateOnly( true ); event->setDtEnd( eventDTEnd ); } else { // Timed Event event->setAllDay( false ); // set date/time end event->setDtStart( currentStartDateTime() ); event->setDtEnd( currentEndDateTime() ); } // Free == Event::Transparent // Busy == Event::Opaque event->setTransparency( mUi->mFreeBusyCheck->isChecked() ? KCalCore::Event::Opaque : KCalCore::Event::Transparent ); } void IncidenceDateTime::save( const KCalCore::Todo::Ptr &todo ) { if ( mUi->mStartCheck->isChecked() ) { todo->setDtStart( currentStartDateTime() ); // Set allday must be executed after setDtStart todo->setAllDay( mUi->mWholeDayCheck->isChecked() ); if ( currentStartDateTime() != mInitialStartDT ) { // We don't offer any way to edit the current completed occurrence. // So, if the start date changes, reset the dtRecurrence todo->setDtRecurrence( currentStartDateTime() ); } } else { todo->setDtStart( KDateTime() ); } if ( mUi->mEndCheck->isChecked() ) { todo->setDtDue( currentEndDateTime(), true/** first */ ); // Set allday must be executed after setDtDue todo->setAllDay( mUi->mWholeDayCheck->isChecked() ); } else { todo->setDtDue( KDateTime() ); } } void IncidenceDateTime::save( const KCalCore::Journal::Ptr &journal ) { journal->setAllDay( mUi->mWholeDayCheck->isChecked() ); if ( mUi->mWholeDayCheck->isChecked() ) { // All day journal KDateTime journalDTStart = currentStartDateTime(); journalDTStart.setDateOnly( true ); journal->setDtStart( journalDTStart ); } else { // Timed Journal // set date/time end journal->setDtStart( currentStartDateTime() ); } } void IncidenceDateTime::setDateTimes( const KDateTime &start, const KDateTime &end ) { const KDateTime::Spec startSpec = start.timeSpec(); const KDateTime::Spec endSpec = end.timeSpec(); // Combo boxes only have system time zones if ( startSpec.type() == KDateTime::TimeZone ) { const KTimeZone systemTz = KSystemTimeZones::zone( startSpec.timeZone().name() ); if ( !systemTz.isValid() ) { const KCalCore::ICalTimeZone icalTz( startSpec.timeZone() ); mTimeZones->add( icalTz ); } } if ( endSpec.type() == KDateTime::TimeZone ) { const KTimeZone systemTz = KSystemTimeZones::zone( endSpec.timeZone().name() ); if ( !systemTz.isValid() ) { const KCalCore::ICalTimeZone icalTz( endSpec.timeZone() ); mTimeZones->add( icalTz ); } } mUi->mTimeZoneComboStart->setAdditionalTimeZones( mTimeZones ); mUi->mTimeZoneComboEnd->setAdditionalTimeZones( mTimeZones ); if ( start.isValid() ) { mUi->mStartDateEdit->setDate( start.date() ); mUi->mStartTimeEdit->setTime( start.time() ); mUi->mTimeZoneComboStart->selectTimeSpec( start.timeSpec() ); } else { KDateTime dt( QDate::currentDate(), QTime::currentTime() ); mUi->mStartDateEdit->setDate( dt.date() ); mUi->mStartTimeEdit->setTime( dt.time() ); mUi->mTimeZoneComboStart->selectTimeSpec( dt.timeSpec() ); } if ( end.isValid() ) { mUi->mEndDateEdit->setDate( end.date() ); mUi->mEndTimeEdit->setTime( end.time() ); mUi->mTimeZoneComboEnd->selectTimeSpec( end.timeSpec() ); } else { KDateTime dt( QDate::currentDate(), QTime::currentTime().addSecs( 60 * 60 ) ); mUi->mEndDateEdit->setDate( dt.date() ); mUi->mEndTimeEdit->setTime( dt.time() ); mUi->mTimeZoneComboEnd->selectTimeSpec( dt.timeSpec() ); } mCurrentStartDateTime = currentStartDateTime(); emit startDateChanged( start.date() ); emit startTimeChanged( start.time() ); emit endDateChanged( end.date() ); emit endTimeChanged( end.time() ); updateStartToolTips(); updateEndToolTips(); } void IncidenceDateTime::updateStartToolTips() { if ( mUi->mStartCheck->isChecked() ) { QString datetimeStr = KCalUtils::IncidenceFormatter::dateTimeToString( currentStartDateTime(), mUi->mWholeDayCheck->isChecked(), false, KSystemTimeZones::local() ); mUi->mStartDateEdit->setToolTip( i18n( "Starts: %1", datetimeStr ) ); mUi->mStartTimeEdit->setToolTip( i18n( "Starts: %1", datetimeStr ) ); } else { mUi->mStartDateEdit->setToolTip( i18n( "Starting Date" ) ); mUi->mStartTimeEdit->setToolTip( i18n( "Starting Time" ) ); } } void IncidenceDateTime::updateEndToolTips() { if ( mUi->mStartCheck->isChecked() ) { QString datetimeStr = KCalUtils::IncidenceFormatter::dateTimeToString( currentEndDateTime(), mUi->mWholeDayCheck->isChecked(), false, KSystemTimeZones::local() ); if ( mLoadedIncidence->type() == KCalCore::Incidence::TypeTodo ) { mUi->mEndDateEdit->setToolTip( i18n( "Due on: %1", datetimeStr ) ); mUi->mEndTimeEdit->setToolTip( i18n( "Due on: %1", datetimeStr ) ); } else { mUi->mEndDateEdit->setToolTip( i18n( "Ends: %1", datetimeStr ) ); mUi->mEndTimeEdit->setToolTip( i18n( "Ends: %1", datetimeStr ) ); } } else { if ( mLoadedIncidence->type() == KCalCore::Incidence::TypeTodo ) { mUi->mEndDateEdit->setToolTip( i18n( "Due Date" ) ); mUi->mEndTimeEdit->setToolTip( i18n( "Due Time" ) ); } else { mUi->mEndDateEdit->setToolTip( i18n( "Ending Date" ) ); mUi->mEndTimeEdit->setToolTip( i18n( "Ending Time" ) ); } } } void IncidenceDateTime::setTimes( const KDateTime &start, const KDateTime &end ) { // like setDateTimes(), but it set only the start/end time, not the date // it is used while applying a template to an event. mUi->mStartTimeEdit->blockSignals( true ); mUi->mStartTimeEdit->setTime( start.time() ); mUi->mStartTimeEdit->blockSignals( false ); mUi->mEndTimeEdit->setTime( end.time() ); mUi->mTimeZoneComboStart->selectTimeSpec( start.timeSpec() ); mUi->mTimeZoneComboEnd->selectTimeSpec( end.timeSpec() ); // emitDateTimeStr(); } void IncidenceDateTime::setStartDate( const QDate &newDate ) { mUi->mStartDateEdit->setDate( newDate ); updateStartDate( newDate ); } void IncidenceDateTime::setStartTime( const QTime &newTime ) { mUi->mStartTimeEdit->setTime( newTime ); updateStartTime( newTime ); } bool IncidenceDateTime::startDateTimeEnabled() const { return mUi->mStartCheck->isChecked(); } bool IncidenceDateTime::endDateTimeEnabled() const { return mUi->mEndCheck->isChecked(); } bool IncidenceDateTime::isValid() const { if ( startDateTimeEnabled() && !currentStartDateTime().isValid() ) { //TODO: Add strings qWarning() << "Start date is invalid"; return false; } if ( endDateTimeEnabled() && !currentEndDateTime().isValid() ) { //TODO: Add strings qWarning() << "End date is invalid"; return false; } if ( startDateTimeEnabled() && endDateTimeEnabled() && currentStartDateTime() > currentEndDateTime() ) { if ( mLoadedIncidence->type() == KCalCore::Incidence::TypeEvent ) { mLastErrorString = i18nc( "@info", "The event ends before it starts.\n" "Please correct dates and times." ); } else if ( mLoadedIncidence->type() == KCalCore::Incidence::TypeTodo ) { mLastErrorString = i18nc( "@info", "The to-do is due before it starts.\n" "Please correct dates and times." ); } else if ( mLoadedIncidence->type() == KCalCore::Incidence::TypeJournal ) { return true; } kDebug() << mLastErrorString; return false; } else { mLastErrorString.clear(); return true; } } static QString timespecToString( const KDateTime::Spec &spec ) { QString str = QLatin1String( "type=" ) + QString::number( spec.type() ) + QLatin1String( "; timezone=" ) + spec.timeZone().name(); return str; } void IncidenceDateTime::printDebugInfo() const { qDebug() << "startDateTimeEnabled() : " << startDateTimeEnabled(); qDebug() << "endDateTimeEnabled() : " << endDateTimeEnabled(); qDebug() << "currentStartDateTime().isValid(): " << currentStartDateTime().isValid(); qDebug() << "currentEndDateTime().isValid() : " << currentEndDateTime().isValid(); qDebug() << "currentStartDateTime() : " << currentStartDateTime().toString(); qDebug() << "currentEndDateTime() : " << currentEndDateTime().toString(); qDebug() << "Incidence type : " << mLoadedIncidence->type(); qDebug() << "allday : " << mLoadedIncidence->allDay(); qDebug() << "mInitialStartDT : " << mInitialStartDT.toString(); qDebug() << "mInitialEndDT : " << mInitialEndDT.toString(); qDebug() << "currentStartDateTime().timeSpec(): " << timespecToString( currentStartDateTime().timeSpec() ); qDebug() << "currentEndDateTime().timeSpec() : " << timespecToString( currentStartDateTime().timeSpec() ); qDebug() << "mInitialStartDT.timeSpec() : " << timespecToString( mInitialStartDT.timeSpec() ); qDebug() << "mInitialEndDT.timeSpec() : " << timespecToString( mInitialEndDT.timeSpec() ); qDebug() << "dirty test1: " << ( mLoadedIncidence->allDay() != mUi->mWholeDayCheck->isChecked() ); if ( mLoadedIncidence->type() == KCalCore::Incidence::TypeEvent ) { KCalCore::Event::Ptr event = mLoadedIncidence.staticCast(); qDebug() << "dirty test2: " << ( mUi->mFreeBusyCheck->isChecked() && event->transparency() != KCalCore::Event::Opaque ); qDebug() << "dirty test3: " << ( !mUi->mFreeBusyCheck->isChecked() && event->transparency() != KCalCore::Event::Transparent ) ; } if ( mLoadedIncidence->allDay() ) { qDebug() << "dirty test4: " << ( mUi->mStartDateEdit->date() != mInitialStartDT.date() || mUi->mEndDateEdit->date() != mInitialEndDT.date() ); } else { qDebug() << "dirty test4.1: " << ( currentStartDateTime() != mInitialStartDT ); qDebug() << "dirty test4.2: " << ( currentEndDateTime() != mInitialEndDT ); qDebug() << "dirty test4.3: " << ( currentStartDateTime().timeSpec() != mInitialStartDT.timeSpec() ); qDebug() << "dirty test4.4: " << ( currentEndDateTime().timeSpec() != mInitialEndDT.timeSpec() ); } } void IncidenceDateTime::setTimeZoneLabelEnabled( bool enable ) { #ifndef KDEPIM_MOBILE_UI mUi->mTimeZoneLabel->setVisible( enable ); #else Q_UNUSED( enable ); #endif }