remove superfluous 'and'
[kdepim.git] / calendarviews / eventview.cpp
blob6ca5e933f9e8ca4dedee32b4de05265f13f9e543
1 /*
2 Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
3 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
4 Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
5 Author: Kevin Krammer, krake@kdab.com
6 Author: Sergio Martins, sergio.martins@kdab.com
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 As a special exception, permission is given to link this program
23 with any edition of Qt, and distribute the resulting executable,
24 without including the source code for Qt in the source distribution.
27 #include "eventview_p.h"
28 #include "prefs.h"
30 #include <Akonadi/EntityTreeModel>
32 #include <calendarsupport/collectionselection.h>
33 #include <calendarsupport/kcalprefs.h>
34 #include <calendarsupport/utils.h>
36 #include <akonadi_next/kcolumnfilterproxymodel.h>
37 using namespace Future;
39 #include <Akonadi/EntityDisplayAttribute>
40 #include <Akonadi/ETMViewStateSaver>
42 #include <KCalCore/Todo>
43 #include <KCalCore/CalFilter>
45 #include <KCalUtils/RecurrenceActions>
47 #include <KHolidays/Holidays>
49 #include <KCheckableProxyModel>
50 #include <KGuiItem>
51 #include <KGlobal>
52 #include <KLocale>
53 #include <KViewStateMaintainer>
55 #include <QApplication>
56 #include <QKeyEvent>
57 #include <QSortFilterProxyModel>
59 using namespace KCalCore;
60 using namespace EventViews;
61 using namespace Akonadi;
63 static const KCatalogLoader loaderCatalog( "libeventviews" );
65 CalendarSupport::CollectionSelection *EventViewPrivate::sGlobalCollectionSelection = 0;
67 /* static */
68 void EventView::setGlobalCollectionSelection( CalendarSupport::CollectionSelection *s )
70 EventViewPrivate::sGlobalCollectionSelection = s;
73 EventView::EventView( QWidget *parent )
74 : QWidget( parent ),
75 d_ptr( new EventViewPrivate( this ) )
77 QByteArray cname = metaObject()->className();
78 cname.replace( ':', '_' );
79 d_ptr->identifier = cname + '_' + KRandom::randomString( 8 ).toLatin1();
81 //AKONADI_PORT review: the FocusLineEdit in the editor emits focusReceivedSignal(),
82 //which triggered finishTypeAhead. But the global focus widget in QApplication is
83 //changed later, thus subsequent keyevents still went to this view, triggering another
84 //editor, for each keypress.
85 //Thus, listen to the global focusChanged() signal (seen in Qt 4.6-stable-patched 20091112 -Frank)
86 connect( qobject_cast<QApplication*>( QApplication::instance() ),
87 SIGNAL(focusChanged(QWidget*,QWidget*)),
88 this, SLOT(focusChanged(QWidget*,QWidget*)) );
90 d_ptr->setUpModels();
93 EventView::~EventView()
95 delete d_ptr;
98 void EventView::defaultAction( const Akonadi::Item &aitem )
100 kDebug();
101 const Incidence::Ptr incidence = CalendarSupport::incidence( aitem );
102 if ( !incidence ) {
103 return;
106 kDebug() << " type:" << int( incidence->type() );
108 if ( incidence->isReadOnly() ) {
109 emit showIncidenceSignal(aitem);
110 } else {
111 emit editIncidenceSignal(aitem);
115 void EventView::setHolidayRegion( const KHolidays::HolidayRegionPtr &holidayRegion )
117 Q_D( EventView );
118 d->mHolidayRegion = holidayRegion;
121 int EventView::showMoveRecurDialog( const Incidence::Ptr &inc, const QDate &date )
123 KDateTime dateTime( date, preferences()->timeSpec() );
125 int availableOccurrences = KCalUtils::RecurrenceActions::availableOccurrences( inc, dateTime );
127 const QString caption = i18nc( "@title:window", "Changing Recurring Item" );
128 KGuiItem itemFuture( i18n( "Also &Future Items" ) );
129 KGuiItem itemSelected( i18n( "Only &This Item" ) );
130 KGuiItem itemAll( i18n( "&All Occurrences" ) );
132 switch ( availableOccurrences ) {
133 case KCalUtils::RecurrenceActions::NoOccurrence:
134 return KCalUtils::RecurrenceActions::NoOccurrence;
136 case KCalUtils::RecurrenceActions::SelectedOccurrence:
137 return KCalUtils::RecurrenceActions::SelectedOccurrence;
139 case KCalUtils::RecurrenceActions::AllOccurrences:
141 Q_ASSERT( availableOccurrences & KCalUtils::RecurrenceActions::SelectedOccurrence );
143 // if there are all kinds of ooccurrences (i.e. past present and future) the user might
144 // want the option only apply to current and future occurrences, leaving the past ones
145 // provide a third choice for that ("Also future")
146 if ( availableOccurrences == KCalUtils::RecurrenceActions::AllOccurrences ) {
147 const QString message = i18n( "The item you are trying to change is a recurring item. "
148 "Should the changes be applied only to this single occurrence, "
149 "also to future items, or to all items in the recurrence?" );
150 return KCalUtils::RecurrenceActions::questionSelectedFutureAllCancel(
151 message, caption, itemSelected, itemFuture, itemAll, this );
155 default:
156 Q_ASSERT( availableOccurrences & KCalUtils::RecurrenceActions::SelectedOccurrence );
157 // selected occurrence and either past or future occurrences
158 const QString message = i18n( "The item you are trying to change is a recurring item. "
159 "Should the changes be applied only to this single occurrence "
160 "or to all items in the recurrence?" );
161 return KCalUtils::RecurrenceActions::questionSelectedAllCancel(
162 message, caption, itemSelected, itemAll, this );
163 break;
166 return KCalUtils::RecurrenceActions::NoOccurrence;
169 void EventView::setCalendar( const Akonadi::ETMCalendar::Ptr &calendar )
171 Q_D( EventView );
172 if ( d->calendar != calendar ) {
173 if (d->calendar)
174 disconnect(d->calendar.data());
176 d->calendar = calendar;
177 if ( calendar ) {
178 if ( d->collectionSelectionModel )
179 d->collectionSelectionModel->setSourceModel( calendar->model() );
181 connect( calendar.data(), SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)),
182 SLOT(onCollectionChanged(Akonadi::Collection,QSet<QByteArray>)) );
187 Akonadi::ETMCalendar::Ptr EventView::calendar() const
189 Q_D( const EventView );
190 return d->calendar;
193 void EventView::setPreferences( const PrefsPtr &preferences )
195 Q_D( EventView );
196 if ( d->mPrefs != preferences ) {
197 if ( preferences ) {
198 d->mPrefs = preferences;
199 } else {
200 d->mPrefs = PrefsPtr( new Prefs() );
202 updateConfig();
206 void EventView::setKCalPreferences( const KCalPrefsPtr &preferences )
208 Q_D( EventView );
209 if ( d->mKCalPrefs != preferences ) {
210 if ( preferences ) {
211 d->mKCalPrefs = preferences;
212 } else {
213 d->mKCalPrefs = KCalPrefsPtr( new CalendarSupport::KCalPrefs() );
215 updateConfig();
219 PrefsPtr EventView::preferences() const
221 Q_D( const EventView );
222 return d->mPrefs;
225 KCalPrefsPtr EventView::kcalPreferences() const
227 Q_D( const EventView );
228 return d->mKCalPrefs;
231 void EventView::dayPassed( const QDate & )
233 updateView();
236 void EventView::setIncidenceChanger( Akonadi::IncidenceChanger *changer )
238 Q_D( EventView );
239 d->mChanger = changer;
242 void EventView::flushView()
246 EventView *EventView::viewAt( const QPoint & )
248 return this;
251 void EventView::updateConfig()
255 QDateTime EventView::selectionStart() const
257 return QDateTime();
260 QDateTime EventView::selectionEnd() const
262 return QDateTime();
265 bool EventView::dateRangeSelectionEnabled() const
267 Q_D( const EventView );
268 return d->mDateRangeSelectionEnabled;
271 void EventView::setDateRangeSelectionEnabled( bool enable )
273 Q_D( EventView );
274 d->mDateRangeSelectionEnabled = enable;
277 bool EventView::supportsZoom() const
279 return false;
282 bool EventView::hasConfigurationDialog() const
284 return false;
287 void EventView::setDateRange( const KDateTime &start, const KDateTime &end,
288 const QDate &preferredMonth )
290 Q_D( EventView );
292 d->startDateTime = start;
293 d->endDateTime = end;
294 showDates( start.date(), end.date(), preferredMonth );
295 const QPair<KDateTime,KDateTime> adjusted = actualDateRange( start, end, preferredMonth );
296 d->actualStartDateTime = adjusted.first;
297 d->actualEndDateTime = adjusted.second;
300 KDateTime EventView::startDateTime() const
302 Q_D( const EventView );
303 return d->startDateTime;
306 KDateTime EventView::endDateTime() const
308 Q_D( const EventView );
309 return d->endDateTime;
312 KDateTime EventView::actualStartDateTime() const
314 Q_D( const EventView );
315 return d->actualStartDateTime;
318 KDateTime EventView::actualEndDateTime() const
320 Q_D( const EventView );
321 return d->actualEndDateTime;
324 void EventView::showConfigurationDialog( QWidget * )
328 bool EventView::processKeyEvent( QKeyEvent *ke )
330 Q_D( EventView );
331 // If Return is pressed bring up an editor for the current selected time span.
332 if ( ke->key() == Qt::Key_Return ) {
333 if ( ke->type() == QEvent::KeyPress ) {
334 d->mReturnPressed = true;
335 } else if ( ke->type() == QEvent::KeyRelease ) {
336 if ( d->mReturnPressed ) {
337 emit newEventSignal();
338 d->mReturnPressed = false;
339 return true;
340 } else {
341 d->mReturnPressed = false;
346 // Ignore all input that does not produce any output
347 if ( ke->text().isEmpty() || ( ke->modifiers() & Qt::ControlModifier ) ) {
348 return false;
351 if ( ke->type() == QEvent::KeyPress ) {
352 switch ( ke->key() ) {
353 case Qt::Key_Escape:
354 case Qt::Key_Return:
355 case Qt::Key_Enter:
356 case Qt::Key_Tab:
357 case Qt::Key_Backtab:
358 case Qt::Key_Left:
359 case Qt::Key_Right:
360 case Qt::Key_Up:
361 case Qt::Key_Down:
362 case Qt::Key_Backspace:
363 case Qt::Key_Delete:
364 case Qt::Key_PageUp:
365 case Qt::Key_PageDown:
366 case Qt::Key_Home:
367 case Qt::Key_End:
368 case Qt::Key_Control:
369 case Qt::Key_Meta:
370 case Qt::Key_Alt:
371 break;
372 default:
373 d->mTypeAheadEvents.append(
374 new QKeyEvent( ke->type(),
375 ke->key(),
376 ke->modifiers(),
377 ke->text(),
378 ke->isAutoRepeat(),
379 static_cast<ushort>( ke->count() ) ) );
380 if ( !d->mTypeAhead ) {
381 d->mTypeAhead = true;
382 emit newEventSignal();
384 return true;
387 return false;
390 void EventView::setTypeAheadReceiver( QObject *o )
392 Q_D( EventView );
393 d->mTypeAheadReceiver = o;
396 void EventView::focusChanged( QWidget *, QWidget *now )
398 Q_D( EventView );
399 if ( d->mTypeAhead && now && now == d->mTypeAheadReceiver ) {
400 d->finishTypeAhead();
404 CalendarSupport::CollectionSelection *EventView::collectionSelection() const
406 Q_D( const EventView );
407 return d->customCollectionSelection ? d->customCollectionSelection : globalCollectionSelection();
410 void EventView::setCustomCollectionSelectionProxyModel( KCheckableProxyModel *model )
412 Q_D( EventView );
413 if ( d->collectionSelectionModel == model ) {
414 return;
417 delete d->collectionSelectionModel;
418 d->collectionSelectionModel = model;
419 d->setUpModels();
422 KCheckableProxyModel *EventView::customCollectionSelectionProxyModel() const
424 Q_D( const EventView );
425 return d->collectionSelectionModel;
428 KCheckableProxyModel *EventView::takeCustomCollectionSelectionProxyModel()
430 Q_D( EventView );
431 KCheckableProxyModel *m = d->collectionSelectionModel;
432 d->collectionSelectionModel = 0;
433 d->setUpModels();
434 return m;
437 CalendarSupport::CollectionSelection *EventView::customCollectionSelection() const
439 Q_D( const EventView );
440 return d->customCollectionSelection;
443 void EventView::clearSelection()
447 bool EventView::eventDurationHint( QDateTime &startDt, QDateTime &endDt, bool &allDay ) const
449 Q_UNUSED( startDt );
450 Q_UNUSED( endDt );
451 Q_UNUSED( allDay );
452 return false;
455 Akonadi::IncidenceChanger *EventView::changer() const
457 Q_D( const EventView );
458 return d->mChanger;
461 void EventView::doRestoreConfig( const KConfigGroup & )
465 void EventView::doSaveConfig( KConfigGroup & )
469 QPair<KDateTime,KDateTime> EventView::actualDateRange( const KDateTime &start,
470 const KDateTime &end,
471 const QDate &preferredMonth ) const
473 Q_UNUSED( preferredMonth );
474 return qMakePair( start, end );
478 void EventView::incidencesAdded( const Akonadi::Item::List & )
483 void EventView::incidencesAboutToBeRemoved( const Akonadi::Item::List & )
487 void EventView::incidencesChanged( const Akonadi::Item::List & )
492 void EventView::handleBackendError( const QString &errorString )
494 kError() << errorString;
497 void EventView::calendarReset()
501 CalendarSupport::CollectionSelection *EventView::globalCollectionSelection()
503 return EventViewPrivate::sGlobalCollectionSelection;
506 QByteArray EventView::identifier() const
508 Q_D( const EventView );
509 return d->identifier;
512 void EventView::setIdentifier( const QByteArray &identifier )
514 Q_D( EventView );
515 d->identifier = identifier;
518 void EventView::setChanges( Changes changes )
520 Q_D( EventView );
521 if ( d->mChanges == NothingChanged ) {
522 QMetaObject::invokeMethod( this, "updateView", Qt::QueuedConnection );
525 d->mChanges = changes;
528 EventView::Changes EventView::changes() const
530 Q_D( const EventView );
531 return d->mChanges;
534 void EventView::restoreConfig( const KConfigGroup &configGroup )
536 Q_D( EventView );
537 const bool useCustom = configGroup.readEntry( "UseCustomCollectionSelection", false );
538 if ( !d->collectionSelectionModel && !useCustom ) {
539 delete d->collectionSelectionModel;
540 d->collectionSelectionModel = 0;
541 d->setUpModels();
542 } else if ( useCustom ) {
544 if ( !d->collectionSelectionModel ) {
545 // Sort the calendar model on calendar name
546 QSortFilterProxyModel *sortProxy = new QSortFilterProxyModel( this );
547 sortProxy->setDynamicSortFilter( true );
548 sortProxy->setSortCaseSensitivity( Qt::CaseInsensitive );
550 if ( d->calendar ) {
551 sortProxy->setSourceModel( d->calendar->entityTreeModel() );
554 // Only show the first column.
555 KColumnFilterProxyModel *columnFilterProxy = new KColumnFilterProxyModel( this );
556 columnFilterProxy->setVisibleColumn( Akonadi::ETMCalendar::CollectionTitle );
557 columnFilterProxy->setSourceModel( sortProxy );
559 // Make the calendar model checkable.
560 d->collectionSelectionModel = new KCheckableProxyModel( this );
561 d->collectionSelectionModel->setSourceModel( columnFilterProxy );
563 d->setUpModels();
565 const KConfigGroup selectionGroup =
566 configGroup.config()->group( configGroup.name() + QLatin1String( "_selectionSetup" ) );
568 KViewStateMaintainer<ETMViewStateSaver> maintainer( selectionGroup );
569 maintainer.setSelectionModel( d->collectionSelectionModel->selectionModel() );
570 maintainer.restoreState();
573 doRestoreConfig( configGroup );
576 void EventView::saveConfig( KConfigGroup &configGroup )
578 Q_D( EventView );
579 configGroup.writeEntry( "UseCustomCollectionSelection", d->collectionSelectionModel != 0 );
581 if ( d->collectionSelectionModel ) {
582 KConfigGroup selectionGroup =
583 configGroup.config()->group( configGroup.name() + QLatin1String( "_selectionSetup" ) );
585 KViewStateMaintainer<ETMViewStateSaver> maintainer( selectionGroup );
586 maintainer.setSelectionModel( d->collectionSelectionModel->selectionModel() );
587 maintainer.saveState();
590 doSaveConfig( configGroup );
593 void EventView::setCollectionId( Akonadi::Collection::Id id )
595 Q_D( EventView );
596 if ( d->mCollectionId != id ) {
597 d->mCollectionId = id;
601 Akonadi::Collection::Id EventView::collectionId() const
603 Q_D( const EventView );
604 return d->mCollectionId;
607 bool EventView::makesWholeDayBusy( const KCalCore::Incidence::Ptr &incidence ) const
609 // Must be event
610 // Must be all day
611 // Must be marked busy (TRANSP: OPAQUE)
612 // You must be attendee or organizer
614 if ( incidence->type() != KCalCore::Incidence::TypeEvent || !incidence->allDay() ) {
615 return false;
618 KCalCore::Event::Ptr ev = incidence.staticCast<KCalCore::Event>();
620 if ( ev->transparency() != KCalCore::Event::Opaque ) {
621 return false;
624 // Last check: must be organizer or attendee:
626 if ( kcalPreferences()->thatIsMe( ev->organizer()->email() ) ) {
627 return true;
630 KCalCore::Attendee::List attendees = ev->attendees();
631 KCalCore::Attendee::List::ConstIterator it;
632 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
633 if ( kcalPreferences()->thatIsMe( (*it)->email() ) ) {
634 return true;
638 return false;
641 /*static*/
642 QColor EventView::itemFrameColor( const QColor &color, bool selected )
644 if ( color.isValid() ) {
645 return selected ? QColor( 85 + color.red() * 2.0 / 3,
646 85 + color.green() * 2.0 / 3,
647 85 + color.blue() * 2.0 / 3 ) : color.dark( 115 );
648 } else {
649 return Qt::black;
653 QString EventView::iconForItem( const Akonadi::Item &item )
655 QString iconName;
656 Akonadi::Collection collection = item.parentCollection();
657 while ( collection.parentCollection().isValid() &&
658 collection.parentCollection() != Akonadi::Collection::root() ) {
659 collection = calendar()->collection( collection.parentCollection().id() );
662 if ( collection.isValid() && collection.hasAttribute<Akonadi::EntityDisplayAttribute>() ) {
663 iconName = collection.attribute<Akonadi::EntityDisplayAttribute>()->iconName();
666 return iconName;
669 void EventView::onCollectionChanged(const Akonadi::Collection &collection,
670 const QSet<QByteArray> &changedAttributes)
672 if (changedAttributes.contains("AccessRights")) {
673 setChanges(changes() | EventViews::EventView::ResourcesChanged);
674 updateView();
678 #include "eventview.moc"
679 // kate: space-indent on; indent-width 2; replace-tabs on;