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"
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>
53 #include <KViewStateMaintainer>
55 #include <QApplication>
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;
68 void EventView::setGlobalCollectionSelection( CalendarSupport::CollectionSelection
*s
)
70 EventViewPrivate::sGlobalCollectionSelection
= s
;
73 EventView::EventView( 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
*)) );
93 EventView::~EventView()
98 void EventView::defaultAction( const Akonadi::Item
&aitem
)
101 const Incidence::Ptr incidence
= CalendarSupport::incidence( aitem
);
106 kDebug() << " type:" << int( incidence
->type() );
108 if ( incidence
->isReadOnly() ) {
109 emit
showIncidenceSignal(aitem
);
111 emit
editIncidenceSignal(aitem
);
115 void EventView::setHolidayRegion( const KHolidays::HolidayRegionPtr
&holidayRegion
)
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 );
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 );
166 return KCalUtils::RecurrenceActions::NoOccurrence
;
169 void EventView::setCalendar( const Akonadi::ETMCalendar::Ptr
&calendar
)
172 if ( d
->calendar
!= calendar
) {
174 disconnect(d
->calendar
.data());
176 d
->calendar
= 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
);
193 void EventView::setPreferences( const PrefsPtr
&preferences
)
196 if ( d
->mPrefs
!= preferences
) {
198 d
->mPrefs
= preferences
;
200 d
->mPrefs
= PrefsPtr( new Prefs() );
206 void EventView::setKCalPreferences( const KCalPrefsPtr
&preferences
)
209 if ( d
->mKCalPrefs
!= preferences
) {
211 d
->mKCalPrefs
= preferences
;
213 d
->mKCalPrefs
= KCalPrefsPtr( new CalendarSupport::KCalPrefs() );
219 PrefsPtr
EventView::preferences() const
221 Q_D( const EventView
);
225 KCalPrefsPtr
EventView::kcalPreferences() const
227 Q_D( const EventView
);
228 return d
->mKCalPrefs
;
231 void EventView::dayPassed( const QDate
& )
236 void EventView::setIncidenceChanger( Akonadi::IncidenceChanger
*changer
)
239 d
->mChanger
= changer
;
242 void EventView::flushView()
246 EventView
*EventView::viewAt( const QPoint
& )
251 void EventView::updateConfig()
255 QDateTime
EventView::selectionStart() const
260 QDateTime
EventView::selectionEnd() const
265 bool EventView::dateRangeSelectionEnabled() const
267 Q_D( const EventView
);
268 return d
->mDateRangeSelectionEnabled
;
271 void EventView::setDateRangeSelectionEnabled( bool enable
)
274 d
->mDateRangeSelectionEnabled
= enable
;
277 bool EventView::supportsZoom() const
282 bool EventView::hasConfigurationDialog() const
287 void EventView::setDateRange( const KDateTime
&start
, const KDateTime
&end
,
288 const QDate
&preferredMonth
)
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
)
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;
341 d
->mReturnPressed
= false;
346 // Ignore all input that does not produce any output
347 if ( ke
->text().isEmpty() || ( ke
->modifiers() & Qt::ControlModifier
) ) {
351 if ( ke
->type() == QEvent::KeyPress
) {
352 switch ( ke
->key() ) {
357 case Qt::Key_Backtab
:
362 case Qt::Key_Backspace
:
365 case Qt::Key_PageDown
:
368 case Qt::Key_Control
:
373 d
->mTypeAheadEvents
.append(
374 new QKeyEvent( ke
->type(),
379 static_cast<ushort
>( ke
->count() ) ) );
380 if ( !d
->mTypeAhead
) {
381 d
->mTypeAhead
= true;
382 emit
newEventSignal();
390 void EventView::setTypeAheadReceiver( QObject
*o
)
393 d
->mTypeAheadReceiver
= o
;
396 void EventView::focusChanged( QWidget
*, QWidget
*now
)
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
)
413 if ( d
->collectionSelectionModel
== model
) {
417 delete d
->collectionSelectionModel
;
418 d
->collectionSelectionModel
= model
;
422 KCheckableProxyModel
*EventView::customCollectionSelectionProxyModel() const
424 Q_D( const EventView
);
425 return d
->collectionSelectionModel
;
428 KCheckableProxyModel
*EventView::takeCustomCollectionSelectionProxyModel()
431 KCheckableProxyModel
*m
= d
->collectionSelectionModel
;
432 d
->collectionSelectionModel
= 0;
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
455 Akonadi::IncidenceChanger
*EventView::changer() const
457 Q_D( const EventView
);
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
)
515 d
->identifier
= identifier
;
518 void EventView::setChanges( Changes changes
)
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
);
534 void EventView::restoreConfig( const KConfigGroup
&configGroup
)
537 const bool useCustom
= configGroup
.readEntry( "UseCustomCollectionSelection", false );
538 if ( !d
->collectionSelectionModel
&& !useCustom
) {
539 delete d
->collectionSelectionModel
;
540 d
->collectionSelectionModel
= 0;
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
);
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
);
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
)
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
)
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
611 // Must be marked busy (TRANSP: OPAQUE)
612 // You must be attendee or organizer
614 if ( incidence
->type() != KCalCore::Incidence::TypeEvent
|| !incidence
->allDay() ) {
618 KCalCore::Event::Ptr ev
= incidence
.staticCast
<KCalCore::Event
>();
620 if ( ev
->transparency() != KCalCore::Event::Opaque
) {
624 // Last check: must be organizer or attendee:
626 if ( kcalPreferences()->thatIsMe( ev
->organizer()->email() ) ) {
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() ) ) {
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 );
653 QString
EventView::iconForItem( const Akonadi::Item
&item
)
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();
669 void EventView::onCollectionChanged(const Akonadi::Collection
&collection
,
670 const QSet
<QByteArray
> &changedAttributes
)
672 if (changedAttributes
.contains("AccessRights")) {
673 setChanges(changes() | EventViews::EventView::ResourcesChanged
);
678 #include "eventview.moc"
679 // kate: space-indent on; indent-width 2; replace-tabs on;