You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/korganizer/calendarview.cpp

2292 lines
67 KiB

/*
This file is part of KOrganizer.
Copyright (c) 1997, 1998, 1999
Preston Brown (preston.brown@yale.edu)
Fester Zigterman (F.J.F.ZigtermanRustenburg@student.utwente.nl)
Ian Dawes (iadawes@globalserve.net)
Laszlo Boloni (boloni@cs.purdue.edu)
Copyright (c) 2000, 2001, 2002, 2003, 2004
Cornelius Schumacher <schumacher@kde.org>
Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
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"