kde-playground/kdepim/incidenceeditor-ng/incidencedatetime.cpp
2015-04-14 21:49:29 +00:00

1015 lines
36 KiB
C++

/*
Copyright (c) 2010 Bertjan Broeksema <broeksema@kde.org>
Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
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 <calendarsupport/kcalprefs.h>
#include <KCalCore/ICalTimeZones>
#include <KCalUtils/IncidenceFormatter>
#include <KDebug>
#include <KSystemTimeZone>
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<QLineEdit*> lineEdits;
lineEdits << mUi->mStartDateEdit->lineEdit() << mUi->mEndDateEdit->lineEdit()
<< mUi->mStartTimeEdit->lineEdit() << mUi->mEndTimeEdit->lineEdit();
foreach ( QLineEdit *lineEdit, lineEdits ) {
KLineEdit *klineEdit = qobject_cast<KLineEdit*>( 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<KCalCore::Todo>() ) {
load( todo, isTemplate, templateOverridesTimes );
} else if ( KCalCore::Event::Ptr event = IncidenceDateTime::incidence<KCalCore::Event>() ) {
load( event, isTemplate, templateOverridesTimes );
} else if ( KCalCore::Journal::Ptr journal = IncidenceDateTime::incidence<KCalCore::Journal>() ) {
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<KCalCore::Todo>( incidence ) ) {
save( todo );
} else if ( KCalCore::Event::Ptr event =
IncidenceDateTime::incidence<KCalCore::Event>( incidence ) ) {
save( event );
} else if ( KCalCore::Journal::Ptr journal =
IncidenceDateTime::incidence<KCalCore::Journal>( 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<KCalCore::Todo>() ) {
return isDirty( todo );
} else if ( KCalCore::Event::Ptr event = IncidenceDateTime::incidence<KCalCore::Event>() ) {
return isDirty( event );
} else if ( KCalCore::Journal::Ptr journal = IncidenceDateTime::incidence<KCalCore::Journal>() ) {
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( "<a href=\"hide\"><font color='blue'>&lt;&lt; %1</font></a>" );
if ( visible ) {
placeholder = placeholder.arg( tz );
} else {
placeholder = QString( "<a href=\"show\"><font color='blue'>%1 &gt;&gt;</font></a>" );
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( "&lt;&lt;" );
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<KCalCore::Event>();
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
}