diff options
Diffstat (limited to 'korganizer/calendarview.cpp')
-rw-r--r-- | korganizer/calendarview.cpp | 2291 |
1 files changed, 2291 insertions, 0 deletions
diff --git a/korganizer/calendarview.cpp b/korganizer/calendarview.cpp new file mode 100644 index 000000000..75e7ee434 --- /dev/null +++ b/korganizer/calendarview.cpp @@ -0,0 +1,2291 @@ +/* + This file is part of KOrganizer. + + Copyright (c) 1997, 1998, 1999 + Preston Brown ([email protected]) + Fester Zigterman ([email protected]) + Ian Dawes ([email protected]) + Laszlo Boloni ([email protected]) + + Copyright (c) 2000, 2001, 2002, 2003, 2004 + Cornelius Schumacher <[email protected]> + Copyright (C) 2003-2004 Reinhold Kainhofer <[email protected]> + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "calendarview.h" + +#ifndef KORG_NOPRINTER +#include "calprinter.h" +#endif +#include "koeventeditor.h" +#include "kotodoeditor.h" +#include "kojournaleditor.h" +#include "koprefs.h" +#include "koeventviewerdialog.h" +#include "publishdialog.h" +#include "koglobals.h" +#include "koviewmanager.h" +#include "koagendaview.h" +#include "kodialogmanager.h" +#include "statusdialog.h" +#include "datenavigatorcontainer.h" +#include "kotodoview.h" +#include "datenavigator.h" +#include "resourceview.h" +#include "navigatorbar.h" +#include "history.h" +#include "kogroupware.h" +#include "freebusymanager.h" +#include "komonthview.h" +#include "datechecker.h" +#include "komessagebox.h" +#include "exportwebdialog.h" +#include "kocorehelper.h" +#include "incidencechanger.h" +#include "kholidays.h" +#include "mailscheduler.h" +#include "komailclient.h" +#include "multiagendaview.h" + +#include <libkcal/vcaldrag.h> +#include <libkcal/icaldrag.h> +#include <libkcal/icalformat.h> +#include <libkcal/vcalformat.h> +#include <libkcal/scheduler.h> +#include <libkcal/calendarlocal.h> +#include <libkcal/journal.h> +#include <libkcal/calfilter.h> +#include <libkcal/attendee.h> +#include <libkcal/dndfactory.h> +#include <libkcal/freebusy.h> +#include <libkcal/filestorage.h> +#include <libkcal/calendarresources.h> +#include <libkcal/calendarnull.h> +#include <libkcal/htmlexportsettings.h> + +#include <kglobal.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kfiledialog.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <kconfig.h> +#include <krun.h> +#include <kdirwatch.h> + +#include <qapplication.h> +#include <qclipboard.h> +#include <qcursor.h> +#include <qmultilineedit.h> +#include <qtimer.h> +#include <qwidgetstack.h> +#include <qptrlist.h> +#include <qfile.h> +#include <qlayout.h> +#ifndef KORG_NOSPLITTER +#include <qsplitter.h> +#endif +#include <qvbox.h> +#include <qwhatsthis.h> + +#include <stdlib.h> +#include <assert.h> + +using namespace KOrg; + +CalendarView::CalendarView( QWidget *parent, const char *name ) + : CalendarViewBase( parent, name ), + mHistory( 0 ), + mCalendar( CalendarNull::self() ), + mChanger( 0 ) +{ + kdDebug(5850) << "CalendarView::CalendarView( Calendar )" << endl; + + mViewManager = new KOViewManager( this ); + mDialogManager = new KODialogManager( this ); + + mModified = false; + mReadOnly = false; + mSelectedIncidence = 0; + + mFilters.setAutoDelete( true ); + + mExtensions.setAutoDelete( true ); + + mNavigator = new DateNavigator( this ); + mDateChecker = new DateChecker( this ); + + QBoxLayout *topLayout = new QVBoxLayout( this ); + +#ifndef KORG_NOSPLITTER + // create the main layout frames. + mPanner = new QSplitter( QSplitter::Horizontal, this, + "CalendarView::Panner" ); + topLayout->addWidget( mPanner ); + + mLeftSplitter = new QSplitter( QSplitter::Vertical, mPanner, + "CalendarView::LeftFrame" ); +// mPanner->setResizeMode( mLeftSplitter, QSplitter::Stretch ); + + mDateNavigator = new DateNavigatorContainer( mLeftSplitter, + "CalendarView::DateNavigator" ); + +// mLeftSplitter->setResizeMode( mDateNavigator, QSplitter::Stretch ); + mLeftSplitter->setCollapsible( mDateNavigator, true ); + mTodoList = new KOTodoView( CalendarNull::self(), mLeftSplitter, "todolist" ); + + mEventViewer = new KOEventViewer( mLeftSplitter,"EventViewer" ); + + QVBox *rightBox = new QVBox( mPanner ); + mNavigatorBar = new NavigatorBar( rightBox ); + mRightFrame = new QWidgetStack( rightBox ); + rightBox->setStretchFactor( mRightFrame, 1 ); + + mLeftFrame = mLeftSplitter; +#else + QWidget *mainBox; + QWidget *leftFrame; + + if ( KOPrefs::instance()->mVerticalScreen ) { + mainBox = new QVBox( this ); + leftFrame = new QHBox( mainBox ); + } else { + mainBox = new QHBox( this ); + leftFrame = new QVBox( mainBox ); + } + + topLayout->addWidget( mainBox ); + + mDateNavigator = new KDateNavigator( leftFrame, true, + "CalendarView::DateNavigator", + QDate::currentDate() ); + mTodoList = new KOTodoView( CalendarNull::self(), leftFrame, "todolist" ); + + mEventViewer = new KOEventViewer ( leftFrame, "EventViewer" ); + + QWidget *rightBox = new QWidget( mainBox ); + QBoxLayout *rightLayout = new QVBoxLayout( rightBox ); + + mNavigatorBar = new NavigatorBar( QDate::currentDate(), rightBox ); + rightLayout->addWidget( mNavigatorBar ); + + mRightFrame = new QWidgetStack( rightBox ); + rightLayout->addWidget( mRightFrame ); + + mLeftFrame = leftFrame; + + if ( KOPrefs::instance()->mVerticalScreen ) { +// mTodoList->setFixedHeight( 60 ); + mTodoList->setFixedHeight( mDateNavigator->sizeHint().height() ); + } +#endif + + connect( mNavigator, SIGNAL( datesSelected( const KCal::DateList & ) ), + SLOT( showDates( const KCal::DateList & ) ) ); + connect( mNavigator, SIGNAL( datesSelected( const KCal::DateList & ) ), + mDateNavigator, SLOT( selectDates( const KCal::DateList & ) ) ); + + connect( mNavigatorBar, SIGNAL( goPrevYear() ), + mNavigator, SLOT( selectPreviousYear() ) ); + connect( mNavigatorBar, SIGNAL( goNextYear() ), + mNavigator, SLOT( selectNextYear() ) ); + connect( mNavigatorBar, SIGNAL( goPrevMonth() ), + mNavigator, SLOT( selectPreviousMonth() ) ); + connect( mNavigatorBar, SIGNAL( goNextMonth() ), + mNavigator, SLOT( selectNextMonth() ) ); + connect( mNavigatorBar, SIGNAL( goMonth(int) ), + mNavigator, SLOT( selectMonth(int) ) ); + + connect( mNavigator, SIGNAL( datesSelected( const KCal::DateList & ) ), + mNavigatorBar, SLOT( selectDates( const KCal::DateList & ) ) ); + + connect( mDateNavigator, SIGNAL( weekClicked( const QDate & ) ), + mNavigator, SLOT( selectWeek( const QDate & ) ) ); + + connect( mDateNavigator, SIGNAL( goPrevYear() ), + mNavigator, SLOT( selectPreviousYear() ) ); + connect( mDateNavigator, SIGNAL( goNextYear() ), + mNavigator, SLOT( selectNextYear() ) ); + connect( mDateNavigator, SIGNAL( goPrevMonth() ), + mNavigator, SLOT( selectPreviousMonth() ) ); + connect( mDateNavigator, SIGNAL( goNextMonth() ), + mNavigator, SLOT( selectNextMonth() ) ); + connect( mDateNavigator, SIGNAL( goMonth(int) ), + mNavigator, SLOT( selectMonth(int) ) ); + + connect( mDateNavigator, SIGNAL( goPrevious() ), + mNavigator, SLOT( selectPrevious() ) ); + connect( mDateNavigator, SIGNAL( goNext() ), + mNavigator, SLOT( selectNext() ) ); + + connect( mDateNavigator, SIGNAL( datesSelected( const KCal::DateList & ) ), + mNavigator, SLOT( selectDates( const KCal::DateList & ) ) ); + + connect( mDateNavigator, SIGNAL(incidenceDropped(Incidence*, const QDate&)), + SLOT( addIncidenceOn( Incidence *, const QDate & ) ) ); + connect( mDateNavigator, SIGNAL(incidenceDroppedMove(Incidence*,const QDate&)), + SLOT( moveIncidenceTo( Incidence *, const QDate & ) ) ); + + connect( mDateChecker, SIGNAL( dayPassed( const QDate & ) ), + mTodoList, SLOT( dayPassed( const QDate & ) ) ); + connect( mDateChecker, SIGNAL( dayPassed( const QDate & ) ), + SIGNAL( dayPassed( const QDate & ) ) ); + connect( mDateChecker, SIGNAL( dayPassed( const QDate & ) ), + mDateNavigator, SLOT( updateToday() ) ); + + connect( this, SIGNAL( configChanged() ), + mDateNavigator, SLOT( updateConfig() ) ); + + connect( this, SIGNAL( incidenceSelected(Incidence *) ), + mEventViewer, SLOT ( setIncidence (Incidence *) ) ); + + //TODO: do a pretty Summary, + QString s; + s = i18n( "<p><em>No Item Selected</em></p>" + "<p>Select an event, to-do or journal entry to view its details " + "here.</p>"); + + mEventViewer->setDefaultText( s ); + QWhatsThis::add( mEventViewer, + i18n( "View the details of events, journal entries or to-dos " + "selected in KOrganizer's main view here." ) ); + mEventViewer->setIncidence( 0 ); + + mViewManager->connectTodoView( mTodoList ); + mViewManager->connectView( mTodoList ); + + KOGlobals::self()-> + setHolidays( new KHolidays( KOPrefs::instance()->mHolidays ) ); + + connect( QApplication::clipboard(), SIGNAL( dataChanged() ), + SLOT( checkClipboard() ) ); + + connect( mTodoList, SIGNAL( incidenceSelected( Incidence * ) ), + SLOT( processTodoListSelection( Incidence * ) ) ); + disconnect( mTodoList, SIGNAL( incidenceSelected( Incidence * ) ), + this, SLOT( processMainViewSelection( Incidence * ) ) ); + + kdDebug(5850) << "CalendarView::CalendarView() done" << endl; +} + +CalendarView::~CalendarView() +{ + kdDebug(5850) << "~CalendarView()" << endl; + + mCalendar->unregisterObserver( this ); + + delete mDialogManager; + delete mViewManager; + delete mEventViewer; + kdDebug(5850) << "~CalendarView() done" << endl; +} + +void CalendarView::setCalendar( Calendar *cal ) +{ + kdDebug(5850)<<"CalendarView::setCalendar"<<endl; + mCalendar = cal; + + delete mHistory; + mHistory = new History( mCalendar ); + connect( mHistory, SIGNAL( undone() ), SLOT( updateView() ) ); + connect( mHistory, SIGNAL( redone() ), SLOT( updateView() ) ); + + if ( mChanger ) delete mChanger; + setIncidenceChanger( new IncidenceChanger( mCalendar, this ) ); + + mCalendar->registerObserver( this ); + + mDateNavigator->setCalendar( mCalendar ); + + mTodoList->setCalendar( mCalendar ); +} + +void CalendarView::setIncidenceChanger( IncidenceChangerBase *changer ) +{ + mChanger = changer; + emit newIncidenceChanger( mChanger ); + connect( mChanger, SIGNAL( incidenceAdded( Incidence* ) ), + this, SLOT( incidenceAdded( Incidence* ) ) ); + connect( mChanger, SIGNAL( incidenceChanged( Incidence*, Incidence*, int ) ), + this, SLOT( incidenceChanged( Incidence*, Incidence*, int ) ) ); + connect( mChanger, SIGNAL( incidenceChanged( Incidence*, Incidence* ) ), + this, SLOT( incidenceChanged( Incidence*, Incidence* ) ) ); + connect( mChanger, SIGNAL( incidenceToBeDeleted( Incidence * ) ), + this, SLOT( incidenceToBeDeleted( Incidence * ) ) ); + connect( mChanger, SIGNAL( incidenceDeleted( Incidence * ) ), + this, SLOT( incidenceDeleted( Incidence * ) ) ); + + connect( mChanger, SIGNAL( schedule( Scheduler::Method, Incidence*) ), + this, SLOT( schedule( Scheduler::Method, Incidence*) ) ); + + + connect( this, SIGNAL( cancelAttendees( Incidence * ) ), + mChanger, SLOT( cancelAttendees( Incidence * ) ) ); +} + +Calendar *CalendarView::calendar() +{ + if ( mCalendar ) return mCalendar; + else return CalendarNull::self(); +} + +KOIncidenceEditor *CalendarView::editorDialog( Incidence *incidence ) const +{ + if (mDialogList.find(incidence) != mDialogList.end ()) + return mDialogList[incidence]; + else return 0; +} + +QDate CalendarView::startDate() +{ + DateList dates = mNavigator->selectedDates(); + + return dates.first(); +} + +QDate CalendarView::endDate() +{ + DateList dates = mNavigator->selectedDates(); + + return dates.last(); +} + + +bool CalendarView::openCalendar(const QString& filename, bool merge) +{ + kdDebug(5850) << "CalendarView::openCalendar(): " << filename << endl; + + if (filename.isEmpty()) { + kdDebug(5850) << "CalendarView::openCalendar(): Error! Empty filename." << endl; + return false; + } + + if (!QFile::exists(filename)) { + kdDebug(5850) << "CalendarView::openCalendar(): Error! File '" << filename + << "' doesn't exist." << endl; + } + + bool loadedSuccesfully = true; + if ( !merge ) { + mCalendar->close(); + CalendarLocal *cl = dynamic_cast<CalendarLocal*>( mCalendar ); + if ( cl ) { + loadedSuccesfully = cl->load( filename ); + } else { + CalendarResources *cr = dynamic_cast<CalendarResources*>( mCalendar ); + assert( cr ); // otherwise something is majorly wrong + // openCalendar called without merge and a filename, what should we do? + return false; + } + } else { + // merge in a file + FileStorage storage( mCalendar ); + storage.setFileName( filename ); + loadedSuccesfully = storage.load(); + } + + if ( loadedSuccesfully ) { + if ( merge ) + setModified( true ); + else { + setModified( false ); + mViewManager->setDocumentId( filename ); + mTodoList->setDocumentId( filename ); + } + updateCategories(); + updateView(); + return true; + } else { + // while failing to load, the calendar object could + // have become partially populated. Clear it out. + if ( !merge ) mCalendar->close(); + + KMessageBox::error(this,i18n("Could not load calendar '%1'.").arg(filename)); + + return false; + } +} + +bool CalendarView::saveCalendar( const QString& filename ) +{ + kdDebug(5850) << "CalendarView::saveCalendar(): " << filename << endl; + + // Store back all unsaved data into calendar object + mViewManager->currentView()->flushView(); + + FileStorage storage( mCalendar ); + storage.setFileName( filename ); + storage.setSaveFormat( new ICalFormat ); + + bool success = storage.save(); + + if ( !success ) { + return false; + } + + return true; +} + +void CalendarView::closeCalendar() +{ + kdDebug(5850) << "CalendarView::closeCalendar()" << endl; + + // child windows no longer valid + emit closingDown(); + + mCalendar->close(); + setModified( false ); + updateView(); +} + +void CalendarView::archiveCalendar() +{ + mDialogManager->showArchiveDialog(); +} + + +void CalendarView::readSettings() +{ +// kdDebug(5850) << "CalendarView::readSettings()" << endl; + + QString str; + + // read settings from the KConfig, supplying reasonable + // defaults where none are to be found + + KConfig *config = KOGlobals::self()->config(); + +#ifndef KORG_NOSPLITTER + config->setGroup( "KOrganizer Geometry" ); + + QValueList<int> sizes = config->readIntListEntry( "Separator1" ); + if ( sizes.count() != 2 ) { + sizes << mDateNavigator->minimumSizeHint().width(); + sizes << 300; + } + mPanner->setSizes( sizes ); + + sizes = config->readIntListEntry( "Separator2" ); + mLeftSplitter->setSizes( sizes ); +#endif + + mEventViewer->readSettings( config ); + + mViewManager->readSettings( config ); + mTodoList->restoreLayout( config, QString( "Todo Layout" ) ); + + readFilterSettings( config ); + + config->setGroup( "Views" ); + int dateCount = config->readNumEntry( "ShownDatesCount", 7 ); + if ( dateCount == 7 ) mNavigator->selectWeek(); + else mNavigator->selectDates( mNavigator->selectedDates().first(), dateCount ); +} + + +void CalendarView::writeSettings() +{ +// kdDebug(5850) << "CalendarView::writeSettings" << endl; + + KConfig *config = KOGlobals::self()->config(); + +#ifndef KORG_NOSPLITTER + config->setGroup( "KOrganizer Geometry" ); + + QValueList<int> list = mPanner->sizes(); + config->writeEntry( "Separator1", list ); + + list = mLeftSplitter->sizes(); + config->writeEntry( "Separator2", list ); +#endif + mEventViewer->writeSettings( config ); + mViewManager->writeSettings( config ); + mTodoList->saveLayout( config, QString( "Todo Layout" ) ); + + KOPrefs::instance()->writeConfig(); + + writeFilterSettings( config ); + + config->setGroup( "Views" ); + config->writeEntry( "ShownDatesCount", mNavigator->selectedDates().count() ); + + config->sync(); +} + +void CalendarView::readFilterSettings( KConfig *config ) +{ +// kdDebug(5850) << "CalendarView::readFilterSettings()" << endl; + + mFilters.clear(); + + config->setGroup( "General" ); + // FIXME: Move the filter loading and saving to the CalFilter class in libkcal + QStringList filterList = config->readListEntry ("CalendarFilters" ); + QString currentFilter = config->readEntry( "Current Filter" ); + + QStringList::ConstIterator it = filterList.begin(); + QStringList::ConstIterator end = filterList.end(); + while( it != end ) { +// kdDebug(5850) << " filter: " << (*it) << endl; + CalFilter *filter; + filter = new CalFilter( *it ); + config->setGroup( "Filter_" + (*it) ); + filter->setCriteria( config->readNumEntry( "Criteria", 0 ) ); + filter->setCategoryList( config->readListEntry( "CategoryList" ) ); + if ( filter->criteria() & KCal::CalFilter::HideTodosWithoutAttendeeInEmailList ) + filter->setEmailList( KOPrefs::instance()->allEmails() ); + filter->setCompletedTimeSpan( config->readNumEntry( "HideTodoDays", 0 ) ); + mFilters.append( filter ); + + ++it; + } + + config->setGroup( "General" ); + int pos = filterList.findIndex( currentFilter ); + mCurrentFilter = 0; + if ( pos>=0 ) { + mCurrentFilter = mFilters.at( pos ); + } + updateFilter(); +} + +void CalendarView::writeFilterSettings( KConfig *config ) +{ +// kdDebug(5850) << "CalendarView::writeFilterSettings()" << endl; + + QStringList filterList; + + CalFilter *filter = mFilters.first(); + while( filter ) { +// kdDebug(5850) << " fn: " << filter->name() << endl; + filterList << filter->name(); + config->setGroup( "Filter_" + filter->name() ); + config->writeEntry( "Criteria", filter->criteria() ); + config->writeEntry( "CategoryList", filter->categoryList() ); + config->writeEntry( "HideTodoDays", filter->completedTimeSpan() ); + filter = mFilters.next(); + } + config->setGroup( "General" ); + config->writeEntry( "CalendarFilters", filterList ); + if ( mCurrentFilter ) { + config->writeEntry( "Current Filter", mCurrentFilter->name() ); + } else { + config->writeEntry( "Current Filter", QString::null ); + } +} + + +void CalendarView::goDate( const QDate& date ) +{ + mNavigator->selectDate( date ); +} + +void CalendarView::showDate(const QDate & date) +{ + int dateCount = mNavigator->datesCount(); + if ( dateCount == 7 ) + mNavigator->selectWeek( date ); + else + mNavigator->selectDates( date, dateCount ); +} + +void CalendarView::goToday() +{ + mNavigator->selectToday(); +} + +void CalendarView::goNext() +{ + if ( dynamic_cast<KOMonthView*>( mViewManager->currentView() ) ) + mNavigator->selectNextMonth(); + else + mNavigator->selectNext(); +} + +void CalendarView::goPrevious() +{ + if ( dynamic_cast<KOMonthView*>( mViewManager->currentView() ) ) + mNavigator->selectPreviousMonth(); + else + mNavigator->selectPrevious(); +} + +void CalendarView::updateConfig( const QCString& receiver) +{ + if ( receiver != "korganizer" ) return; + kdDebug(5850) << "CalendarView::updateConfig()" << endl; + + KOGlobals::self()-> + setHolidays( new KHolidays( KOPrefs::instance()->mHolidays ) ); + + QString tz( mCalendar->timeZoneId() ); + // Only set a new time zone if it changed. This prevents the window + // from being modified on start + if ( tz != KOPrefs::instance()->mTimeZoneId ) { + + const QString question( i18n("The timezone setting was changed. Do you want to keep the absolute time of " + "the items in your calendar, which will show them to be at a different time than " + "before, or move them to be at the old time also in the new timezone?") ); + int rc = KMessageBox::questionYesNo( this, question, + i18n("Keep Absolute Times?"), + KGuiItem(i18n("Keep Times")), + KGuiItem(i18n("Move Times")), + "calendarKeepAbsoluteTimes"); + if ( rc == KMessageBox::Yes ) { + // user wants us to shift + mCalendar->setTimeZoneIdViewOnly( KOPrefs::instance()->mTimeZoneId ); + } else { + // only set the new timezone, wihtout shifting events, they will be + // interpreted as being in the new timezone now + mCalendar->setTimeZoneId( KOPrefs::instance()->mTimeZoneId ); + } + } + emit configChanged(); + + // force reload and handle agenda view type switch + const bool showMerged = KOPrefs::instance()->agendaViewCalendarDisplay() == KOPrefs::CalendarsMerged; + const bool showSideBySide = KOPrefs::instance()->agendaViewCalendarDisplay() == KOPrefs::CalendarsSideBySide; + KOrg::BaseView *view = mViewManager->currentView(); + mViewManager->showAgendaView(); + if ( view == mViewManager->agendaView() && showSideBySide ) + view = mViewManager->multiAgendaView(); + else if ( view == mViewManager->multiAgendaView() && showMerged ) + view = mViewManager->agendaView(); + mViewManager->showView( view ); + + // To make the "fill window" configurations work + mViewManager->raiseCurrentView(); +} + + +void CalendarView::incidenceAdded( Incidence *incidence ) +{ + setModified( true ); + history()->recordAdd( incidence ); + changeIncidenceDisplay( incidence, KOGlobals::INCIDENCEADDED ); + updateUnmanagedViews(); + checkForFilteredChange( incidence ); +} + +void CalendarView::incidenceChanged( Incidence *oldIncidence, + Incidence *newIncidence ) +{ + incidenceChanged( oldIncidence, newIncidence, KOGlobals::UNKNOWN_MODIFIED ); +} + +void CalendarView::incidenceChanged( Incidence *oldIncidence, + Incidence *newIncidence, int what ) +{ + // FIXME: Make use of the what flag, which indicates which parts of the incidence have changed! + KOIncidenceEditor *tmp = editorDialog( newIncidence ); + if ( tmp ) { + kdDebug(5850) << "Incidence modified and open" << endl; + tmp->modified( what ); + } + setModified( true ); + history()->recordEdit( oldIncidence, newIncidence ); + + // Record completed todos in journals, if enabled. we should to this here in + // favour of the todolist. users can mark a task as completed in an editor + // as well. + if ( newIncidence->type() == "Todo" + && KOPrefs::instance()->recordTodosInJournals() + && ( what == KOGlobals::COMPLETION_MODIFIED + || what == KOGlobals::COMPLETION_MODIFIED_WITH_RECURRENCE ) ) { + + Todo *todo = static_cast<Todo *>(newIncidence); + if ( todo->isCompleted() + || what == KOGlobals::COMPLETION_MODIFIED_WITH_RECURRENCE ) { + QString timeStr = KGlobal::locale()->formatTime( QTime::currentTime() ); + QString description = i18n( "To-do completed: %1 (%2)" ).arg( + newIncidence->summary() ).arg( timeStr ); + + Journal::List journals = calendar()->journals( QDate::currentDate() ); + Journal *journal; + + if ( journals.isEmpty() ) { + journal = new Journal(); + journal->setDtStart( QDateTime::currentDateTime() ); + + QString dateStr = KGlobal::locale()->formatDate( QDate::currentDate() ); + journal->setSummary( i18n("Journal of %1").arg( dateStr ) ); + journal->setDescription( description ); + + if ( !mChanger->addIncidence( journal, this ) ) { + KODialogManager::errorSaveIncidence( this, journal ); + delete journal; + return; + } + + } else { // journal list is not empty + journal = *(journals.at(0)); + Journal *oldJournal = journal->clone(); + journal->setDescription( journal->description().append( "\n" + description ) ); + + if ( !mChanger->changeIncidence( oldJournal, journal ) ) { + KODialogManager::errorSaveIncidence( this, journal ); + delete journal; + return; + } + } + } + } + + changeIncidenceDisplay( newIncidence, KOGlobals::INCIDENCEEDITED ); + updateUnmanagedViews(); + checkForFilteredChange( newIncidence ); +} + +void CalendarView::incidenceToBeDeleted( Incidence *incidence ) +{ + KOIncidenceEditor *tmp = editorDialog( incidence ); + if (tmp) { + kdDebug(5850) << "Incidence to be deleted and open in editor" << endl; + tmp->delayedDestruct(); + } + setModified( true ); + history()->recordDelete( incidence ); +// changeIncidenceDisplay( incidence, KOGlobals::INCIDENCEDELETED ); + updateUnmanagedViews(); +} + +void CalendarView::incidenceDeleted( Incidence *incidence ) +{ + changeIncidenceDisplay( incidence, KOGlobals::INCIDENCEDELETED ); + updateUnmanagedViews(); +} + +void CalendarView::checkForFilteredChange( Incidence *incidence ) +{ + CalFilter *filter = calendar()->filter(); + if ( filter && !filter->filterIncidence( incidence ) ) { + // Incidence is filtered and thus not shown in the view, tell the + // user so that he isn't surprised if his new event doesn't show up + KMessageBox::information( this, i18n("The item \"%1\" is filtered by " + "your current filter rules, so it will be hidden and not " + "appear in the view.").arg( incidence->summary() ), + i18n("Filter Applied"), "ChangedIncidenceFiltered" ); + } +} + +void CalendarView::startMultiModify( const QString &text ) +{ + history()->startMultiModify( text ); +} + +void CalendarView::endMultiModify() +{ + history()->endMultiModify(); +} + + +void CalendarView::changeIncidenceDisplay( Incidence *incidence, int action ) +{ + mDateNavigator->updateView(); + mDialogManager->updateSearchDialog(); + + if ( incidence ) { + // If there is an event view visible update the display + mViewManager->currentView()->changeIncidenceDisplay( incidence, action ); + if ( mTodoList ) mTodoList->changeIncidenceDisplay( incidence, action ); + mEventViewer->changeIncidenceDisplay( incidence, action ); + } else { + mViewManager->currentView()->updateView(); + if ( mTodoList ) mTodoList->updateView(); + } +} + + +void CalendarView::updateView(const QDate &start, const QDate &end) +{ + mTodoList->updateView(); + mViewManager->updateView(start, end); + mDateNavigator->updateView(); +} + +void CalendarView::updateView() +{ + DateList tmpList = mNavigator->selectedDates(); + + // We assume that the navigator only selects consecutive days. + updateView( tmpList.first(), tmpList.last() ); +} + +void CalendarView::updateUnmanagedViews() +{ + mDateNavigator->updateDayMatrix(); +} + +int CalendarView::msgItemDelete( Incidence *incidence ) +{ + return KMessageBox::warningContinueCancel(this, + i18n("The item \"%1\" will be permanently deleted.").arg( incidence->summary() ), + i18n("KOrganizer Confirmation"), KGuiItem(i18n("&Delete"),"editdelete")); +} + + +void CalendarView::edit_cut() +{ + Incidence *incidence = selectedIncidence(); + + if ( !incidence || !mChanger ) { + KNotifyClient::beep(); + return; + } + mChanger->cutIncidence( incidence ); +} + +void CalendarView::edit_copy() +{ + Incidence *incidence = selectedIncidence(); + + if (!incidence) { + KNotifyClient::beep(); + return; + } + DndFactory factory( mCalendar ); + if ( !factory.copyIncidence( incidence ) ) { + KNotifyClient::beep(); + } +} + +void CalendarView::edit_paste() +{ +// If in agenda view, use the selected time and date from there. +// In all other cases, paste the event on the first day of the +// selection in the day matrix on the left + + QDate date; + // create an invalid time to check if we got a new time for the eevent + QTime time(-1,-1); + QDateTime startDT, endDT; + bool useEndTime = false; + + KOAgendaView *aView = mViewManager->agendaView(); + if (aView && aView->selectionStart().isValid()) { + date = aView->selectionStart().date(); + startDT = aView->selectionStart(); + endDT = aView->selectionEnd(); + useEndTime = !aView->selectedIsSingleCell(); + if (!aView->selectedIsAllDay()) { + time = aView->selectionStart().time(); + } + + } else { + date = mNavigator->selectedDates().first(); + } + + DndFactory factory( mCalendar ); + Incidence *pastedIncidence; + if (time.isValid()) + pastedIncidence = factory.pasteIncidence( date, &time ); + else + pastedIncidence = factory.pasteIncidence( date ); + if ( !pastedIncidence ) return; + + // FIXME: use a visitor here + if (pastedIncidence->type() == "Event" ) { + + Event* pastedEvent = static_cast<Event*>(pastedIncidence); + // only use selected area if event is of the same type (all-day or non-all-day + // as the current selection is + if ( aView && endDT.isValid() && useEndTime ) { + if ( (pastedEvent->doesFloat() && aView->selectedIsAllDay()) || + (!pastedEvent->doesFloat() && ! aView->selectedIsAllDay()) ) { + pastedEvent->setDtEnd(endDT); + } + } + mChanger->addIncidence( pastedEvent, this ); + + } else if ( pastedIncidence->type() == "Todo" ) { + Todo* pastedTodo = static_cast<Todo*>(pastedIncidence); + Todo* _selectedTodo = selectedTodo(); + if ( _selectedTodo ) + pastedTodo->setRelatedTo( _selectedTodo ); + mChanger->addIncidence( pastedTodo, this ); + } +} + +void CalendarView::edit_options() +{ + mDialogManager->showOptionsDialog(); +} + +void CalendarView::dateTimesForNewEvent( QDateTime &startDt, QDateTime &endDt, bool &allDay ) +{ + if ( !startDt.isValid() ) { + // Default start is the first selected date with the preferred time as set + // in the config dlg. + if ( !startDt.date().isValid() ) { + startDt.setDate( mNavigator->selectedDates().first() ); + } + if ( !startDt.time().isValid() ) { + startDt.setTime( KOPrefs::instance()->mStartTime.time() ); + } + } + if ( !endDt.isValid() ) { + int addSecs = ( KOPrefs::instance()->mDefaultDuration.time().hour()*3600 ) + + ( KOPrefs::instance()->mDefaultDuration.time().minute()*60 ); + endDt = startDt.addSecs( addSecs ); + } + mViewManager->currentView()->eventDurationHint( startDt, endDt, allDay ); +} + +KOEventEditor *CalendarView::newEventEditor( const QDateTime &startDtParam, + const QDateTime &endDtParam, bool allDayParam) +{ + // let the current view change the default start/end datetime + bool allDay = allDayParam; + QDateTime startDt( startDtParam ), endDt( endDtParam ); + // Adjust the start/end date times (i.e. replace invalid values by defaults, + // and let the view adjust the type. + dateTimesForNewEvent( startDt, endDt, allDay ); + + KOEventEditor *eventEditor = mDialogManager->getEventEditor(); + eventEditor->newEvent(); + connectIncidenceEditor( eventEditor ); + eventEditor->setDates( startDt, endDt, allDay ); + mDialogManager->connectTypeAhead( eventEditor, dynamic_cast<KOrg::AgendaView*>(viewManager()->currentView()) ); + return eventEditor; +} + + + + +void CalendarView::newEvent() +{ + kdDebug(5850) << "CalendarView::newEvent()" << endl; + newEvent( QDateTime(), QDateTime() ); +} + +void CalendarView::newEvent( const QDate &dt ) +{ + QDateTime startDt( dt, KOPrefs::instance()->mStartTime.time() ); + return newEvent( QDateTime( dt ), QDateTime() ); +} + +void CalendarView::newEvent( const QDateTime &startDt ) +{ + return newEvent( startDt, QDateTime() ); +} + +void CalendarView::newEvent( const QDateTime &startDt, const QDateTime &endDt, + bool allDay ) +{ + KOEventEditor *eventEditor = newEventEditor( startDt, endDt, allDay ); + eventEditor->show(); +} + +void CalendarView::newEvent( const QString &summary, const QString &description, + const QStringList &attachments, const QStringList &attendees, + const QStringList &attachmentMimetypes, bool inlineAttachment ) +{ + KOEventEditor *eventEditor = newEventEditor(); + eventEditor->setTexts( summary, description ); + // if attach or attendee list is empty, these methods don't do anything, so + // it's save to call them in every case + eventEditor->addAttachments( attachments, attachmentMimetypes, inlineAttachment ); + eventEditor->addAttendees( attendees ); + eventEditor->show(); +} + +void CalendarView::newTodo( const QString &summary, const QString &description, + const QStringList &attachments, const QStringList &attendees, + const QStringList &attachmentMimetypes, bool inlineAttachment ) +{ + kdDebug(5850) << k_funcinfo << endl; + KOTodoEditor *todoEditor = mDialogManager->getTodoEditor(); + connectIncidenceEditor( todoEditor ); + todoEditor->newTodo(); + todoEditor->setTexts( summary, description ); + todoEditor->addAttachments( attachments, attachmentMimetypes, inlineAttachment ); + todoEditor->addAttendees( attendees ); + todoEditor->show(); +} + +void CalendarView::newTodo() +{ + kdDebug(5850) << k_funcinfo << endl; + QDateTime dtDue; + bool allday = true; + KOTodoEditor *todoEditor = mDialogManager->getTodoEditor(); + connectIncidenceEditor( todoEditor ); + todoEditor->newTodo(); + if ( mViewManager->currentView()->isEventView() ) { + dtDue.setDate( mNavigator->selectedDates().first() ); + QDateTime dtDummy = QDateTime::currentDateTime(); + mViewManager->currentView()-> + eventDurationHint( dtDue, dtDummy, allday ); + todoEditor->setDates( dtDue, allday ); + } + todoEditor->show(); +} + +void CalendarView::newTodo( const QDate &date ) +{ + KOTodoEditor *todoEditor = mDialogManager->getTodoEditor(); + connectIncidenceEditor( todoEditor ); + todoEditor->newTodo(); + todoEditor->setDates( QDateTime( date, QTime::currentTime() ), true ); + todoEditor->show(); +} + +void CalendarView::newJournal() +{ + kdDebug(5850) << "CalendarView::newJournal()" << endl; + newJournal( QString::null, QDate() ); +} + +void CalendarView::newJournal( const QDate &date) +{ + newJournal( QString::null, date ); +} + +void CalendarView::newJournal( const QString &text, const QDate &date ) +{ + KOJournalEditor *journalEditor = mDialogManager->getJournalEditor(); + connectIncidenceEditor( journalEditor ); + journalEditor->newJournal(); + journalEditor->setTexts( text ); + if ( !date.isValid() ) { + journalEditor->setDate( mNavigator->selectedDates().first() ); + } else { + journalEditor->setDate( date ); + } + journalEditor->show(); +} + +void CalendarView::newSubTodo() +{ + Todo *todo = selectedTodo(); + if ( todo ) newSubTodo( todo ); +} + +void CalendarView::newSubTodo(Todo *parentEvent) +{ + KOTodoEditor *todoEditor = mDialogManager->getTodoEditor(); + connectIncidenceEditor( todoEditor ); + todoEditor->newTodo(); + todoEditor->setDates( QDateTime(), false, parentEvent ); + todoEditor->show(); +} + +void CalendarView::newFloatingEvent() +{ + DateList tmpList = mNavigator->selectedDates(); + QDate date = tmpList.first(); + + newEvent( QDateTime( date, QTime( 12, 0, 0 ) ), + QDateTime( date, QTime( 12, 0, 0 ) ), true ); +} + +bool CalendarView::addIncidence( const QString &ical ) +{ + kdDebug(5850) << "CalendarView::addIncidence:\n" << ical << endl; + ICalFormat format; + format.setTimeZone( mCalendar->timeZoneId(), true ); + Incidence *incidence = format.fromString( ical ); + if ( !incidence ) return false; + if ( !mChanger->addIncidence( incidence, this ) ) { + delete incidence; + return false; + } + return true; +} + +void CalendarView::appointment_show() +{ + Incidence *incidence = selectedIncidence(); + if (incidence) + showIncidence( incidence ); + else + KNotifyClient::beep(); +} + +void CalendarView::appointment_edit() +{ + Incidence *incidence = selectedIncidence(); + if (incidence) + editIncidence( incidence ); + else + KNotifyClient::beep(); +} + +void CalendarView::appointment_delete() +{ + Incidence *incidence = selectedIncidence(); + if (incidence) + deleteIncidence( incidence ); + else + KNotifyClient::beep(); +} + +void CalendarView::todo_unsub() +{ + Todo *anTodo = selectedTodo(); + if( todo_unsub (anTodo ) ) { + updateView(); + } +} + +bool CalendarView::todo_unsub( Todo *todo ) +{ + bool status= false; + if ( !todo || !todo->relatedTo() ) return false; + + if ( mChanger->beginChange( todo ) ) { + Todo *oldTodo = todo->clone(); + todo->setRelatedTo(0); + mChanger->changeIncidence( oldTodo, todo, KOGlobals::RELATION_MODIFIED ); + mChanger->endChange( todo ); + delete oldTodo; + setModified(true); + status = true; + } + if ( ! status ) { + KMessageBox::sorry( this, i18n("Unable to turn sub-to-do into a top-level " + "to-do, because it cannot be locked.") ); + } + + return status; +} + +bool CalendarView::makeSubTodosIndependents ( ) +{ + bool status = false; + Todo *anTodo = selectedTodo(); + + if( makeSubTodosIndependents( anTodo ) ) { + updateView(); + status = true; + } + return status; +} + +bool CalendarView::makeSubTodosIndependents ( Todo *todo ) +{ + if( !todo || todo->relations().isEmpty() ) return false; + + startMultiModify ( i18n( "Make sub-to-dos independent" ) ); + Incidence::List subTodos( todo->relations() ); + Incidence::List::Iterator it; + Incidence *aIncidence; + Todo *aTodo; + + for ( it= subTodos.begin(); it != subTodos.end(); ++it ) { + aIncidence = *it; + if( aIncidence && aIncidence->type() == "Todo" ) { + aTodo = static_cast<Todo*>( aIncidence ); + todo_unsub ( aTodo ); + } + } + endMultiModify(); + return true; +} + +bool CalendarView::deleteIncidence( const QString &uid, bool force ) +{ + Incidence *inc = mCalendar->incidence( uid ); + if ( inc ) { + deleteIncidence( inc, force ); + return true; + } else { + return false; + } +} + +void CalendarView::toggleAlarm( Incidence *incidence ) +{ + if ( !incidence || !mChanger ) { + kdDebug(5850) << "CalendarView::toggleAlarm() called without having a clicked item" << endl; + return; + } + Incidence*oldincidence = incidence->clone(); + if ( !mChanger->beginChange( incidence ) ) { + kdDebug(5850) << "Unable to lock incidence " << endl; + delete oldincidence; + return; + } + + Alarm::List alarms = incidence->alarms(); + Alarm::List::ConstIterator it; + for( it = alarms.begin(); it != alarms.end(); ++it ) + (*it)->toggleAlarm(); + if (alarms.isEmpty()) { + // Add an alarm if it didn't have one + Alarm*alm = incidence->newAlarm(); + alm->setEnabled(true); + } + mChanger->changeIncidence( oldincidence, incidence, KOGlobals::ALARM_MODIFIED ); + mChanger->endChange( incidence ); + delete oldincidence; + +// mClickedItem->updateIcons(); +} + +void CalendarView::dissociateOccurrence( Incidence *incidence, const QDate &date ) +{ + if ( !incidence || !mChanger ) { + kdDebug(5850) << "CalendarView::toggleAlarm() called without having a clicked item" << endl; + return; + } + if ( !mChanger->beginChange( incidence ) ) { + kdDebug(5850) << "Unable to lock incidence " << endl; + return; + } + startMultiModify( i18n("Dissociate occurrence") ); + Incidence*oldincidence = incidence->clone(); + + Incidence* newInc = mCalendar->dissociateOccurrence( incidence, date, true ); + + if ( newInc ) { + // TODO: Use the same resource instead of asking again! + mChanger->changeIncidence( oldincidence, incidence ); + mChanger->addIncidence( newInc, this ); + } else { + KMessageBox::sorry( this, i18n("Dissociating the occurrence failed."), + i18n("Dissociating Failed") ); + } + mChanger->endChange( incidence ); + endMultiModify(); + delete oldincidence; +} + +void CalendarView::dissociateFutureOccurrence( Incidence *incidence, const QDate &date ) +{ + if ( !incidence || !mChanger ) { + kdDebug(5850) << "CalendarView::toggleAlarm() called without having a clicked item" << endl; + return; + } + if ( !mChanger->beginChange( incidence ) ) { + kdDebug(5850) << "Unable to lock incidence " << endl; + return; + } + startMultiModify( i18n("Dissociate future occurrences") ); + Incidence*oldincidence = incidence->clone(); + + Incidence* newInc = mCalendar->dissociateOccurrence( incidence, date, true ); + if ( newInc ) { + // TODO: Use the same resource instead of asking again! + mChanger->changeIncidence( oldincidence, incidence ); + mChanger->addIncidence( newInc, this ); + } else { + KMessageBox::sorry( this, i18n("Dissociating the future occurrences failed."), + i18n("Dissociating Failed") ); + } + endMultiModify(); + mChanger->endChange( incidence ); + delete oldincidence; +} + + +/*****************************************************************************/ + + +void CalendarView::schedule_publish(Incidence *incidence) +{ + if (incidence == 0) + incidence = selectedIncidence(); + + if (!incidence) { + KMessageBox::information( this, i18n("No item selected."), + "PublishNoEventSelected" ); + return; + } + + PublishDialog *publishdlg = new PublishDialog(); + if (incidence->attendeeCount()>0) { + Attendee::List attendees = incidence->attendees(); + Attendee::List::ConstIterator it; + for( it = attendees.begin(); it != attendees.end(); ++it ) { + publishdlg->addAttendee( *it ); + } + } + if ( publishdlg->exec() == QDialog::Accepted ) { + Incidence *inc = incidence->clone(); + inc->registerObserver( 0 ); + inc->clearAttendees(); + + // Send the mail + KCal::MailScheduler scheduler( mCalendar ); + if ( scheduler.publish( incidence, publishdlg->addresses() ) ) { + KMessageBox::information( this, i18n("The item information was successfully sent."), + i18n("Publishing"), "IncidencePublishSuccess" ); + } else { + KMessageBox::error( this, i18n("Unable to publish the item '%1'").arg( incidence->summary() ) ); + } + } + delete publishdlg; +} + +void CalendarView::schedule_request(Incidence *incidence) +{ + schedule(Scheduler::Request,incidence); +} + +void CalendarView::schedule_refresh(Incidence *incidence) +{ + schedule(Scheduler::Refresh,incidence); +} + +void CalendarView::schedule_cancel(Incidence *incidence) +{ + schedule(Scheduler::Cancel,incidence); +} + +void CalendarView::schedule_add(Incidence *incidence) +{ + schedule(Scheduler::Add,incidence); +} + +void CalendarView::schedule_reply(Incidence *incidence) +{ + schedule(Scheduler::Reply,incidence); +} + +void CalendarView::schedule_counter(Incidence *incidence) +{ + schedule(Scheduler::Counter,incidence); +} + +void CalendarView::schedule_declinecounter(Incidence *incidence) +{ + schedule(Scheduler::Declinecounter,incidence); +} + +void CalendarView::schedule_forward(Incidence * incidence) +{ + if (incidence == 0) + incidence = selectedIncidence(); + + if (!incidence) { + KMessageBox::information( this, i18n("No item selected."), + "ForwardNoEventSelected" ); + return; + } + + PublishDialog publishdlg; + if ( publishdlg.exec() == QDialog::Accepted ) { + QString recipients = publishdlg.addresses(); + ICalFormat format; + QString messageText = format.createScheduleMessage( incidence, Scheduler::Request ); + KOMailClient mailer; + if ( mailer.mailTo( incidence, recipients, messageText ) ) { + + KMessageBox::information( this, i18n("The item information was successfully sent."), + i18n("Forwarding"), "IncidenceForwardSuccess" ); + } else { + KMessageBox::error( this, i18n("Unable to forward the item '%1'").arg( incidence->summary() ) ); + } + } +} + +void CalendarView::mailFreeBusy( int daysToPublish ) +{ + QDateTime start = QDateTime::currentDateTime(); + QDateTime end = start.addDays(daysToPublish); + + FreeBusy *freebusy = new FreeBusy(mCalendar, start, end); + freebusy->setOrganizer( Person( KOPrefs::instance()->fullName(), + KOPrefs::instance()->email() ) ); + + kdDebug(5850) << "calendarview: schedule_publish_freebusy: startDate: " + << KGlobal::locale()->formatDateTime( start ) << " End Date: " + << KGlobal::locale()->formatDateTime( end ) << endl; + + PublishDialog *publishdlg = new PublishDialog(); + if ( publishdlg->exec() == QDialog::Accepted ) { + // Send the mail + KCal::MailScheduler scheduler( mCalendar ); + if ( scheduler.publish( freebusy, publishdlg->addresses() ) ) { + KMessageBox::information( this, i18n("The free/busy information was successfully sent."), + i18n("Sending Free/Busy"), "FreeBusyPublishSuccess" ); + } else { + KMessageBox::error( this, i18n("Unable to publish the free/busy data.") ); + } + } + delete freebusy; + delete publishdlg; +} + +void CalendarView::uploadFreeBusy() +{ + KOGroupware::instance()->freeBusyManager()->publishFreeBusy(); +} + +void CalendarView::schedule(Scheduler::Method method, Incidence *incidence) +{ + if ( !incidence ) { + incidence = selectedIncidence(); + } + + if ( !incidence ) { + KMessageBox::sorry( this, i18n("No item selected."), + "ScheduleNoEventSelected" ); + return; + } + + if( incidence->attendeeCount() == 0 && method != Scheduler::Publish ) { + KMessageBox::information( this, i18n("The item has no attendees."), + "ScheduleNoIncidences" ); + return; + } + + Incidence *inc = incidence->clone(); + inc->registerObserver( 0 ); + inc->clearAttendees(); + + // Send the mail + KCal::MailScheduler scheduler( mCalendar ); + if ( !scheduler.performTransaction( incidence, method ) ) { + KMessageBox::information( this, i18n("The groupware message for item '%1'" + "was successfully sent.\nMethod: %2") + .arg( incidence->summary() ) + .arg( Scheduler::methodName( method ) ), + i18n("Sending Free/Busy"), + "FreeBusyPublishSuccess" ); + } else { + KMessageBox::error( this, i18n("Groupware message sending failed. " + "%2 is request/reply/add/cancel/counter/etc.", + "Unable to send the item '%1'.\nMethod: %2") + .arg( incidence->summary() ) + .arg( Scheduler::methodName( method ) ) ); + } +} + +void CalendarView::openAddressbook() +{ + KRun::runCommand("kaddressbook"); +} + +void CalendarView::setModified(bool modified) +{ + if (mModified != modified) { + mModified = modified; + emit modifiedChanged(mModified); + } +} + +bool CalendarView::isReadOnly() +{ + return mReadOnly; +} + +void CalendarView::setReadOnly(bool readOnly) +{ + if (mReadOnly != readOnly) { + mReadOnly = readOnly; + emit readOnlyChanged(mReadOnly); + } +} + +bool CalendarView::isModified() +{ + return mModified; +} + +void CalendarView::print() +{ +#ifndef KORG_NOPRINTER + KOCoreHelper helper; + CalPrinter printer( this, mCalendar, &helper ); + connect( this, SIGNAL(configChanged()), &printer, SLOT(updateConfig()) ); + + KOrg::BaseView *currentView = mViewManager->currentView(); + + CalPrinterBase::PrintType printType = CalPrinterBase::Month; + if ( currentView ) printType = currentView->printType(); + + DateList tmpDateList = mNavigator->selectedDates(); + Incidence::List selectedIncidences; + if ( mViewManager->currentView() ) { + selectedIncidences = mViewManager->currentView()->selectedIncidences(); + } + printer.print( printType, tmpDateList.first(), tmpDateList.last(), selectedIncidences ); +#endif +} + +void CalendarView::exportWeb() +{ + // FIXME: Get rid of the settings object. When can I delete it??? + HTMLExportSettings *settings = new HTMLExportSettings( "KOrganizer" ); + // Manually read in the config, because parametrized kconfigxt objects don't + // seem to load the config theirselves + if ( settings ) settings->readConfig(); + ExportWebDialog *dlg = new ExportWebDialog( settings, this ); + connect( dlg, SIGNAL( exportHTML( HTMLExportSettings* ) ), + this, SIGNAL( exportHTML( HTMLExportSettings* ) ) ); + dlg->show(); +} + +void CalendarView::exportICalendar() +{ + QString filename = KFileDialog::getSaveFileName("icalout.ics",i18n("*.ics|ICalendars"),this); + + // Force correct extension + if (filename.right(4) != ".ics") filename += ".ics"; + + FileStorage storage( mCalendar, filename, new ICalFormat ); + storage.save(); +} + +void CalendarView::exportVCalendar() +{ + if (mCalendar->journals().count() > 0) { + int result = KMessageBox::warningContinueCancel(this, + i18n("The journal entries can not be exported to a vCalendar file."), + i18n("Data Loss Warning"),i18n("Proceed"),"dontaskVCalExport", + true); + if (result != KMessageBox::Continue) return; + } + + QString filename = KFileDialog::getSaveFileName("vcalout.vcs",i18n("*.vcs|vCalendars"),this); + + // TODO: I don't like forcing extensions: + // Force correct extension + if (filename.right(4) != ".vcs") filename += ".vcs"; + + FileStorage storage( mCalendar, filename, new VCalFormat ); + storage.save(); +} + +void CalendarView::eventUpdated(Incidence *) +{ + setModified(); + // Don't call updateView here. The code, which has caused the update of the + // event is responsible for updating the view. +// updateView(); +} + +void CalendarView::adaptNavigationUnits() +{ + if (mViewManager->currentView()->isEventView()) { + int days = mViewManager->currentView()->currentDateCount(); + if (days == 1) { + emit changeNavStringPrev(i18n("&Previous Day")); + emit changeNavStringNext(i18n("&Next Day")); + } else { + emit changeNavStringPrev(i18n("&Previous Week")); + emit changeNavStringNext(i18n("&Next Week")); + } + } +} + +void CalendarView::processMainViewSelection( Incidence *incidence ) +{ + if ( incidence ) mTodoList->clearSelection(); + processIncidenceSelection( incidence ); +} + +void CalendarView::processTodoListSelection( Incidence *incidence ) +{ + if ( incidence && mViewManager->currentView() ) { + mViewManager->currentView()->clearSelection(); + } + processIncidenceSelection( incidence ); +} + +void CalendarView::processIncidenceSelection( Incidence *incidence ) +{ + if ( incidence == mSelectedIncidence ) return; + + mSelectedIncidence = incidence; + + emit incidenceSelected( mSelectedIncidence ); + bool organizerEvents = false; + bool groupEvents = false; + bool todo = false; + bool subtodo = false; + + if ( incidence ) { + organizerEvents = KOPrefs::instance()->thatIsMe( incidence->organizer().email() ); + groupEvents = incidence->attendeeByMails( KOPrefs::instance()->allEmails() ); + + if ( incidence && incidence->type() == "Todo" ) { + todo = true; + subtodo = ( incidence->relatedTo() != 0 ); + } + } + emit todoSelected( todo ); + emit subtodoSelected( subtodo ); + emit organizerEventsSelected( organizerEvents ); + emit groupEventsSelected( groupEvents ); +} + + +void CalendarView::checkClipboard() +{ +#ifndef KORG_NODND + if (ICalDrag::canDecode(QApplication::clipboard()->data())) { + kdDebug(5850) << "CalendarView::checkClipboard() true" << endl; + emit pasteEnabled(true); + } else { + kdDebug(5850) << "CalendarView::checkClipboard() false" << endl; + emit pasteEnabled(false); + } +#endif +} + +void CalendarView::showDates(const DateList &selectedDates) +{ +// kdDebug(5850) << "CalendarView::selectDates()" << endl; + + if ( mViewManager->currentView() ) { + updateView( selectedDates.first(), selectedDates.last() ); + } else { + mViewManager->showAgendaView(); + } +} + +void CalendarView::editFilters() +{ + kdDebug(5850) << "CalendarView::editFilters()" << endl; + + CalFilter *filter = mFilters.first(); + while(filter) { + kdDebug(5850) << " Filter: " << filter->name() << endl; + filter = mFilters.next(); + } + + mDialogManager->showFilterEditDialog(&mFilters); +} + +/** Filter configuration changed +*/ +void CalendarView::updateFilter() +{ + QStringList filters; + CalFilter *filter; + + int pos = mFilters.find( mCurrentFilter ); + if ( pos < 0 ) { + mCurrentFilter = 0; + } + + filters << i18n("No filter"); + for ( filter = mFilters.first(); filter; filter = mFilters.next() ) { + filters << filter->name(); + } + + emit newFilterListSignal( filters ); + // account for the additional "No filter" at the beginning! if the + // filter is not in the list, pos == -1... + emit selectFilterSignal( pos+1 ); + mCalendar->setFilter( mCurrentFilter ); + updateView(); +} + +/** A different filter was selected +*/ +void CalendarView::filterActivated( int filterNo ) +{ + CalFilter *newFilter = 0; + if ( filterNo > 0 && filterNo <= int(mFilters.count()) ) { + newFilter = mFilters.at( filterNo-1 ); + } + if ( newFilter != mCurrentFilter ) { + mCurrentFilter = newFilter; + mCalendar->setFilter( mCurrentFilter ); + updateView(); + } + emit filterChanged(); +} + +QString CalendarView::currentFilterName() const +{ + if ( mCurrentFilter) { + return mCurrentFilter->name(); + } else return i18n("No filter"); +} + +void CalendarView::takeOverEvent() +{ + Incidence *incidence = currentSelection(); + + if (!incidence) return; + + incidence->setOrganizer( Person( KOPrefs::instance()->fullName(), + KOPrefs::instance()->email() ) ); + incidence->recreate(); + incidence->setReadOnly(false); + + updateView(); +} + +void CalendarView::takeOverCalendar() +{ + Incidence::List incidences = mCalendar->rawIncidences(); + Incidence::List::Iterator it; + + for ( it = incidences.begin(); it != incidences.end(); ++it ) { + (*it)->setOrganizer( Person( KOPrefs::instance()->fullName(), + KOPrefs::instance()->email() ) ); + (*it)->recreate(); + (*it)->setReadOnly(false); + } + updateView(); +} + +void CalendarView::showIntro() +{ + kdDebug(5850) << "To be implemented." << endl; +} + +void CalendarView::showDateNavigator( bool show ) +{ + if( show ) + mDateNavigator->show(); + else + mDateNavigator->hide(); +} + +void CalendarView::showTodoView( bool show ) +{ + if( show ) + mTodoList->show(); + else + mTodoList->hide(); +} + +void CalendarView::showEventViewer( bool show ) +{ + if( show ) + mEventViewer->show(); + else + mEventViewer->hide(); +} + + +void CalendarView::addView(KOrg::BaseView *view) +{ + mViewManager->addView(view); +} + +void CalendarView::showView(KOrg::BaseView *view) +{ + mViewManager->showView(view); +} + +void CalendarView::addExtension( CalendarViewExtension::Factory *factory ) +{ + CalendarViewExtension *extension = factory->create( mLeftSplitter ); + + mExtensions.append( extension ); +} + +void CalendarView::toggleExpand() +{ + showLeftFrame( mLeftFrame->isHidden() ); +} + +void CalendarView::showLeftFrame(bool show) +{ + if (show) { + mLeftFrame->show(); + emit calendarViewExpanded( false ); + } else { + mLeftFrame->hide(); + emit calendarViewExpanded( true ); + } +} + +void CalendarView::calendarModified( bool modified, Calendar * ) +{ + setModified( modified ); +} + +Todo *CalendarView::selectedTodo() +{ + Incidence *incidence = currentSelection(); + if ( incidence && incidence->type() == "Todo" ) { + return static_cast<Todo *>( incidence ); + } + incidence = 0; + + Incidence::List selectedIncidences = mTodoList->selectedIncidences(); + if ( !selectedIncidences.isEmpty() ) incidence = selectedIncidences.first(); + if ( incidence && incidence->type() == "Todo" ) { + return static_cast<Todo *>( incidence ); + } + + return 0; +} + +void CalendarView::dialogClosing( Incidence *in ) +{ + // FIXME: this doesn't work, because if it's a new incidence, it's not locked! + mChanger->endChange( in ); + mDialogList.remove( in ); +} + +Incidence *CalendarView::currentSelection() +{ + return mViewManager->currentSelection(); +} + +Incidence* CalendarView::selectedIncidence() +{ + Incidence *incidence = currentSelection(); + if ( !incidence ) { + Incidence::List selectedIncidences = mTodoList->selectedIncidences(); + if ( !selectedIncidences.isEmpty() ) + incidence = selectedIncidences.first(); + } + return incidence; +} + +void CalendarView::showIncidence() +{ + showIncidence( selectedIncidence() ); +} + +void CalendarView::editIncidence() +{ + editIncidence( selectedIncidence() ); +} + +bool CalendarView::editIncidence( const QString& uid ) +{ + kdDebug(5850) << "CalendarView::editIncidence()" << endl; + return editIncidence( mCalendar->incidence( uid ) ); +} + +void CalendarView::deleteIncidence() +{ + deleteIncidence( selectedIncidence() ); +} + +void CalendarView::cutIncidence(Incidence *) +{ + edit_cut(); +} + +void CalendarView::copyIncidence(Incidence *) +{ + edit_copy(); +} + +void CalendarView::pasteIncidence() +{ + edit_paste(); +} + +void CalendarView::showIncidence( Incidence *incidence ) +{ + KOEventViewerDialog *eventViewer = new KOEventViewerDialog( this ); + eventViewer->setIncidence( incidence ); + eventViewer->show(); +} + +bool CalendarView::editIncidence( Incidence *incidence, bool isCounter ) +{ + kdDebug(5850) << "CalendarView::editEvent()" << endl; + + if ( !incidence || !mChanger ) { + KNotifyClient::beep(); + return false; + } + KOIncidenceEditor *tmp = editorDialog( incidence ); + if ( tmp ) { + kdDebug(5850) << "CalendarView::editIncidence() in List" << endl; + tmp->reload(); + tmp->raise(); + tmp->show(); + return true; + } + + if ( incidence->isReadOnly() ) { + showIncidence( incidence ); + return true; + } + + if ( !isCounter && !mChanger->beginChange( incidence ) ) { + warningChangeFailed( incidence ); + showIncidence( incidence ); + return false; + } + + kdDebug(5850) << "CalendarView::editIncidence() new IncidenceEditor" << endl; + KOIncidenceEditor *incidenceEditor = mDialogManager->getEditor( incidence ); + connectIncidenceEditor( incidenceEditor ); + + mDialogList.insert( incidence, incidenceEditor ); + incidenceEditor->editIncidence( incidence, mCalendar ); + incidenceEditor->show(); + return true; +} + +void CalendarView::deleteSubTodosIncidence ( Todo *todo ) +{ + if( !todo ) return; + + Incidence::List subTodos( todo->relations() ); + Incidence::List::Iterator it; + Incidence *aIncidence; + Todo *aTodo; + + for ( it= subTodos.begin(); it != subTodos.end(); ++it ) { + aIncidence = *it; + if( aIncidence && aIncidence->type() == "Todo" ) { + aTodo = static_cast<Todo*>( aIncidence ); + deleteSubTodosIncidence ( aTodo ); + } + } + mChanger->deleteIncidence ( todo ); +} + +void CalendarView::deleteTodoIncidence ( Todo *todo, bool force ) +{ + if ( !todo ) return ; + + // it a simple todo, ask and delete it. + if (todo->relations().isEmpty() ) { + bool doDelete = true; + if ( !force && KOPrefs::instance()->mConfirm ) { + doDelete = ( msgItemDelete( todo ) == KMessageBox::Continue ); + } + if ( doDelete ) + mChanger->deleteIncidence( todo ); + return; + } + + /* Ok, this to-do has sub-to-dos, ask what to do */ + int km = KMessageBox::No; + if ( !force ) { + km=KMessageBox::questionYesNoCancel( this, + i18n("The item \"%1\" has sub-to-dos. " + "Do you want to delete just this item and " + "make all its sub-to-dos independent, or " + "delete the to-do with all its sub-to-dos?" + ).arg( todo->summary() ), + i18n("KOrganizer Confirmation"), + i18n("Delete Only This"), + i18n("Delete All")); + } + startMultiModify( i18n("Deleting sub-to-dos" ) ); + // Delete only the father + if( km == KMessageBox::Yes ) { + + makeSubTodosIndependents ( todo ); + mChanger->deleteIncidence( todo ); + } else if ( km == KMessageBox::No ) { + // Delete all + // we have to hide the delete confirmation for each itemDate + deleteSubTodosIncidence ( todo ); + } + endMultiModify(); +} + +void CalendarView::deleteIncidence(Incidence *incidence, bool force) +{ + if ( !incidence || !mChanger ) { + if ( !force ) { + KNotifyClient::beep(); + } + return; + } + if ( incidence->isReadOnly() ) { + if ( !force ) { + KMessageBox::information( this, i18n("The item \"%1\" is marked read-only " + "and cannot be deleted; it probably belongs to " + "a read-only calendar resource.") + .arg(incidence->summary()), + i18n("Removing not possible"), + "deleteReadOnlyIncidence" ); + } + return; + } + + CanDeleteIncidenceVisitor v; + + // Let the visitor do special things for special incidence types. + // e.g. todos with children cannot be deleted, so act(..) returns false + if ( !v.act( incidence, this ) ) + return; + //If it is a todo, there are specific delete function + + if ( incidence && incidence->type()=="Todo" ) { + deleteTodoIncidence( static_cast<Todo*>(incidence), force ); + return; + } + + if ( incidence->doesRecur() ) { + QDate itemDate = mViewManager->currentSelectionDate(); + kdDebug(5850) << "Recurrence-Date: " << itemDate.toString() << endl; + int km = KMessageBox::Ok; + if ( !force ) { + if ( !itemDate.isValid() ) { + kdDebug(5850) << "Date Not Valid" << endl; + km = KMessageBox::warningContinueCancel(this, + i18n("The calendar item \"%1\" recurs over multiple dates; " + "are you sure you want to delete it " + "and all its recurrences?").arg( incidence->summary() ), + i18n("KOrganizer Confirmation"), i18n("Delete All") ); + } else { + km = KOMessageBox::fourBtnMsgBox( this, QMessageBox::Warning, + i18n("The calendar item \"%1\" recurs over multiple dates. " + "Do you want to delete only the current one on %2, only all " + "future recurrences, or all its recurrences?" ) + .arg( incidence->summary() ) + .arg( KGlobal::locale()->formatDate(itemDate)), + i18n("KOrganizer Confirmation"), i18n("Delete C&urrent"), + i18n("Delete &Future"), + i18n("Delete &All")); + } + } + switch(km) { + case KMessageBox::Ok: // Continue // all + case KMessageBox::Continue: + mChanger->deleteIncidence( incidence ); + break; + + case KMessageBox::Yes: // just this one + if ( mChanger->beginChange( incidence ) ) { + Incidence *oldIncidence = incidence->clone(); + incidence->recurrence()->addExDate( itemDate ); + mChanger->changeIncidence( oldIncidence, incidence ); + mChanger->endChange( incidence ); + delete oldIncidence; + } + break; + case KMessageBox::No: // all future items + if ( mChanger->beginChange( incidence ) ) { + Incidence *oldIncidence = incidence->clone(); + Recurrence *recur = incidence->recurrence(); + recur->setEndDate( itemDate.addDays(-1) ); + mChanger->changeIncidence( oldIncidence, incidence ); + mChanger->endChange( incidence ); + delete oldIncidence; + } + break; + } + } else { + bool doDelete = true; + if ( !force && KOPrefs::instance()->mConfirm ) { + doDelete = ( msgItemDelete( incidence ) == KMessageBox::Continue ); + } + if ( doDelete ) { + mChanger->deleteIncidence( incidence ); + processIncidenceSelection( 0 ); + } + } +} + +void CalendarView::connectIncidenceEditor( KOIncidenceEditor *editor ) +{ + connect( this, SIGNAL( newIncidenceChanger( IncidenceChangerBase* ) ), + editor, SLOT( setIncidenceChanger( IncidenceChangerBase* ) ) ); + editor->setIncidenceChanger( mChanger ); +} + +bool CalendarView::purgeCompletedSubTodos( Todo* todo, bool &allPurged ) +{ + if ( !todo ) return true; + bool deleteThisTodo = true; + Incidence::List subTodos( todo->relations() ); + Incidence *aIncidence; + Todo *aTodo; + Incidence::List::Iterator it; + for ( it = subTodos.begin(); it != subTodos.end(); ++it ) { + aIncidence = *it; + if ( aIncidence && aIncidence->type()=="Todo" ) { + aTodo = static_cast<Todo*>( aIncidence ); + deleteThisTodo &= purgeCompletedSubTodos( aTodo, allPurged ); + } + } + + if ( deleteThisTodo ) { + if ( todo->isCompleted() ) { + if ( !mChanger->deleteIncidence( todo ) ) + allPurged = false; + } else { + deleteThisTodo = false; + } + } else { + if ( todo->isCompleted() ) { + allPurged = false; + } + } + return deleteThisTodo; +} + +void CalendarView::purgeCompleted() +{ + int result = KMessageBox::warningContinueCancel(this, + i18n("Delete all completed to-dos?"),i18n("Purge To-dos"),i18n("Purge")); + + if (result == KMessageBox::Continue) { + bool allDeleted = true; + startMultiModify( i18n("Purging completed to-dos") ); + Todo::List todos = calendar()->rawTodos(); + Todo::List rootTodos; + Todo::List::ConstIterator it; + for ( it = todos.begin(); it != todos.end(); ++it ) { + Todo *aTodo = *it; + if ( aTodo && !aTodo->relatedTo() ) + rootTodos.append( aTodo ); + } + // now that we have a list of all root todos, check them and their children + for ( it = rootTodos.begin(); it != rootTodos.end(); ++it ) { + purgeCompletedSubTodos( *it, allDeleted ); + } + endMultiModify(); + if ( !allDeleted ) { + KMessageBox::information( this, i18n("Unable to purge to-dos with " + "uncompleted children."), i18n("Delete To-do"), + "UncompletedChildrenPurgeTodos" ); + } + } +} + +void CalendarView::slotCalendarChanged() +{ + kdDebug(5850) << "CalendarView::slotCalendarChanged()" << endl; + + updateView(); +} + +void CalendarView::warningChangeFailed( Incidence * ) +{ + KMessageBox::sorry( this, i18n("Unable to edit item: " + "it is locked by another process.") ); +} + +void CalendarView::editCanceled( Incidence *i ) +{ + mCalendar->endChange( i ); +} + +void CalendarView::showErrorMessage( const QString &msg ) +{ + KMessageBox::error( this, msg ); +} + +void CalendarView::updateCategories() +{ + QStringList allCats( calendar()->categories() ); + allCats.sort(); + QStringList categories( KOPrefs::instance()->mCustomCategories ); + for ( QStringList::ConstIterator si = allCats.constBegin(); si != allCats.constEnd(); ++si ) { + if ( categories.find( *si ) == categories.end() ) { + categories.append( *si ); + } + } + KOPrefs::instance()->mCustomCategories = categories; + KOPrefs::instance()->writeConfig(); + // Make the category editor update the list! + emit categoriesChanged(); +} + +void CalendarView::addIncidenceOn( Incidence *incadd, const QDate &dt ) +{ + if ( !incadd || !mChanger ) { + KMessageBox::sorry(this, i18n("Unable to copy the item to %1.") + .arg( dt.toString() ), i18n("Copying Failed") ); + return; + } + Incidence *incidence = mCalendar->incidence( incadd->uid() ); + if ( !incidence ) incidence = incadd; + // Create a copy of the incidence, since the incadd doesn't belong to us. + incidence = incidence->clone(); + incidence->recreate(); + + if ( incidence->type() == "Event" ) { + Event *event = static_cast<Event*>(incidence); + + // Adjust date + QDateTime start = event->dtStart(); + QDateTime end = event->dtEnd(); + + int duration = start.daysTo( end ); + start.setDate( dt ); + end.setDate( dt.addDays( duration ) ); + + event->setDtStart( start ); + event->setDtEnd( end ); + + } else if ( incidence->type() == "Todo" ) { + Todo *todo = static_cast<Todo*>(incidence); + QDateTime due = todo->dtDue(); + due.setDate( dt ); + + todo->setDtDue( due ); + todo->setHasDueDate( true ); + } + + if ( !mChanger->addIncidence( incidence, this ) ) { + KODialogManager::errorSaveIncidence( this, incidence ); + delete incidence; + } +} + +void CalendarView::moveIncidenceTo( Incidence *incmove, const QDate &dt ) +{ + if ( !incmove || !mChanger ) { + KMessageBox::sorry( this, i18n("Unable to move the item to %1.") + .arg( dt.toString() ), i18n("Moving Failed") ); + return; + } + Incidence *incidence = mCalendar->incidence( incmove->uid() ); + if ( !incidence ) { + addIncidenceOn( incidence, dt ); + return; + } + Incidence *oldIncidence = incidence->clone(); + if ( !mChanger->beginChange( incidence ) ) { + delete oldIncidence; + return; + } + + if ( incidence->type() == "Event" ) { + Event *event = static_cast<Event*>(incidence); + + // Adjust date + QDateTime start = event->dtStart(); + QDateTime end = event->dtEnd(); + + int duration = start.daysTo( end ); + start.setDate( dt ); + end.setDate( dt.addDays( duration ) ); + + event->setDtStart( start ); + event->setDtEnd( end ); + + } else if ( incidence->type() == "Todo" ) { + Todo *todo = static_cast<Todo*>(incidence); + QDateTime due = todo->dtDue(); + due.setDate( dt ); + + todo->setDtDue( due ); + todo->setHasDueDate( true ); + } + mChanger->changeIncidence( oldIncidence, incidence ); + mChanger->endChange( incidence ); + delete oldIncidence; +} + +void CalendarView::resourcesChanged() +{ + mViewManager->resourcesChanged(); + updateView(); +} + +#include "calendarview.moc" |