kde-playground/kdepim/korgac/alarmdialog.cpp

1039 lines
32 KiB
C++
Raw Normal View History

/*
This file is part of the KDE reminder agent.
Copyright (c) 2000,2003 Cornelius Schumacher <schumacher@kde.org>
Copyright (c) 2008-2009 Allen Winter <winter@kde.org>
Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
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 "alarmdialog.h"
#include "korganizer_interface.h"
#include "mailclient.h"
#include <calendarsupport/next/incidenceviewer.h>
#include <calendarsupport/kcalprefs.h>
#include <calendarsupport/identitymanager.h>
#include <calendarsupport/utils.h>
#include <incidenceeditor-ng/incidencedialog.h>
#include <incidenceeditor-ng/incidencedialogfactory.h>
#include <KCalCore/Event>
#include <KCalCore/Todo>
#include <KCalUtils/IncidenceFormatter>
#include <KPIMIdentities/Identity>
#include <KPIMIdentities/IdentityManager>
#include <Akonadi/Item>
#include <Mailtransport/TransportManager>
#include <KComboBox>
#include <KDebug>
#include <KHBox>
#include <KLocale>
#include <KMessageBox>
#include <KNotification>
#include <KSharedConfig>
#include <KSystemTimeZone>
#include <KToolInvocation>
#include <KWindowSystem>
#include <phonon/mediaobject.h>
#include <QLabel>
#include <QKeyEvent>
#include <QSpinBox>
#include <QTreeWidget>
#include <QVBoxLayout>
using namespace KPIMIdentities;
using namespace KCalCore;
using namespace KCalUtils;
static int defSuspendVal = 5;
static int defSuspendUnit = 0; // 0=>minutes, 1=>hours, 2=>days, 3=>weeks
class ReminderTree : public QTreeWidget
{
public:
ReminderTree( QWidget *parent ) : QTreeWidget( parent )
{
}
/*reimp*/
void mouseReleaseEvent( QMouseEvent *event );
};
void ReminderTree::mouseReleaseEvent( QMouseEvent *event )
{
QTreeWidgetItem *item = itemAt( event->pos() );
if ( item ) {
if ( event->button() == Qt::LeftButton ) {
emit itemActivated( item, 0 );
} else if ( event->button() == Qt::RightButton ) {
emit customContextMenuRequested( event->pos() );
}
}
}
class ReminderTreeItem : public QTreeWidgetItem
{
public:
ReminderTreeItem( const Akonadi::Item &incidence, QTreeWidget *parent )
: QTreeWidgetItem( parent ), mIncidence( incidence ), mNotified( false )
{
}
bool operator<( const QTreeWidgetItem & other ) const;
QString mDisplayText;
const Akonadi::Item mIncidence;
QDateTime mRemindAt;
KDateTime mTrigger;
KDateTime mHappening;
bool mNotified;
};
struct ConfItem {
QString uid;
KUrl akonadiUrl;
QDateTime remindAt;
};
bool ReminderTreeItem::operator<( const QTreeWidgetItem &other ) const
{
switch( treeWidget()->sortColumn() ) {
case 1: // happening datetime
{
const ReminderTreeItem *item = static_cast<const ReminderTreeItem *>( &other );
return item->mHappening.secsTo( mHappening );
}
case 2: // trigger datetime
{
const ReminderTreeItem *item = static_cast<const ReminderTreeItem *>( &other );
return item->mTrigger.secsTo( mTrigger );
}
default:
return QTreeWidgetItem::operator < ( other );
}
}
typedef QList<ReminderTreeItem *> ReminderList;
AlarmDialog::AlarmDialog( const Akonadi::ETMCalendar::Ptr &calendar, QWidget *parent )
: KDialog( parent, Qt::WindowStaysOnTopHint ),
mCalendar( calendar ), mSuspendTimer( this )
{
// User1 => Edit...
// User2 => Dismiss All
// User3 => Dismiss Selected
// Ok => Suspend
connect( calendar.data(), SIGNAL(calendarChanged()), SLOT(slotCalendarChanged()) );
KIconLoader::global()->addAppDir( QLatin1String("korgac") );
KSharedConfig::Ptr config = KGlobal::config();
KConfigGroup generalConfig( config, "General" );
QPoint pos = generalConfig.readEntry( "Position", QPoint( 0, 0 ) );
QWidget *topBox = new QWidget( this );
if ( !pos.isNull() ) {
mPos = pos;
topBox->move( mPos );
}
setMainWidget( topBox );
setCaption( i18nc( "@title:window", "Reminders" ) );
setWindowIcon( KIcon( QLatin1String("korgac") ) );
setButtons( Ok | User1 | User2 | User3 );
setDefaultButton( NoDefault );
setButtonText( User3, i18nc( "@action:button", "Dismiss Reminder" ) );
setButtonToolTip( User3, i18nc( "@info:tooltip",
"Dismiss the reminders for the selected incidences" ) );
setButtonText( User2, i18nc( "@action:button", "Dismiss All" ) );
setButtonToolTip( User2, i18nc( "@info:tooltip",
"Dismiss the reminders for all listed incidences" ) );
setButtonText( User1, i18nc( "@action:button", "Edit..." ) );
setButtonToolTip( User1, i18nc( "@info:tooltip",
"Edit the selected incidence" ) );
setButtonText( Ok, i18nc( "@action:button", "Suspend" ) );
setButtonToolTip( Ok, i18nc( "@info:tooltip",
"Suspend the reminders for the selected incidences "
"by the specified interval" ) );
// Try to keep the dialog small and non-obtrusive.
setMinimumWidth( 575 );
setMinimumHeight( 300 );
QVBoxLayout *mTopLayout = new QVBoxLayout( topBox );
QLabel *label = new QLabel(
i18nc( "@label",
"Reminders: "
"Click on a title to toggle the details viewer for that item" ),
topBox );
mTopLayout->addWidget( label );
mIncidenceTree = new ReminderTree( topBox );
mIncidenceTree->setColumnCount( 3 );
mIncidenceTree->setSortingEnabled( true );
const QStringList headerLabels =
( QStringList( i18nc( "@title:column reminder title", "Title" ) )
<< i18nc( "@title:column happens at date/time", "Date Time" )
<< i18nc( "@title:column trigger date/time", "Trigger Time" ) );
mIncidenceTree->setHeaderLabels( headerLabels );
mIncidenceTree->headerItem()->setToolTip(
0,
i18nc( "@info:tooltip", "The event or to-do title" ) );
mIncidenceTree->headerItem()->setToolTip(
1,
i18nc( "@info:tooltip", "The reminder is set for this date/time" ) );
mIncidenceTree->headerItem()->setToolTip(
2,
i18nc( "@info:tooltip", "The date/time the reminder was triggered" ) );
mIncidenceTree->setWordWrap( true );
mIncidenceTree->setAllColumnsShowFocus( true );
mIncidenceTree->setSelectionMode( QAbstractItemView::ExtendedSelection );
mIncidenceTree->setRootIsDecorated( false );
mTopLayout->addWidget( mIncidenceTree );
connect( mIncidenceTree, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
SLOT(update()) );
connect( mIncidenceTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
SLOT(edit()) );
connect( mIncidenceTree, SIGNAL(customContextMenuRequested(QPoint)), SLOT(popupItemMenu(QPoint)) );
mDetailView = new CalendarSupport::IncidenceViewer( mCalendar.data(), topBox );
QString s;
s = i18nc( "@info default incidence details string",
"<emphasis>Select an event or to-do from the list above "
"to view its details here.</emphasis>" );
mDetailView->setDefaultMessage( s );
mTopLayout->addWidget( mDetailView );
mDetailView->hide();
mLastItem = 0;
KHBox *suspendBox = new KHBox( topBox );
suspendBox->setSpacing( spacingHint() );
mTopLayout->addWidget( suspendBox );
QLabel *l = new QLabel( i18nc( "@label:spinbox", "Suspend &duration:" ), suspendBox );
mSuspendSpin = new QSpinBox( suspendBox );
mSuspendSpin->setRange( 1, 9999 );
mSuspendSpin->setValue( defSuspendVal ); // default suspend duration
mSuspendSpin->setToolTip(
i18nc( "@info:tooltip",
"Suspend the reminders by this amount of time" ) );
mSuspendSpin->setWhatsThis(
i18nc( "@info:whatsthis",
"Each reminder for the selected incidences will be suspended "
"by this number of time units. You can choose the time units "
"(typically minutes) in the adjacent selector." ) );
l->setBuddy( mSuspendSpin );
mSuspendUnit = new KComboBox( suspendBox );
mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of minutes", "minute(s)" ) );
mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of hours", "hour(s)" ) );
mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of days", "day(s)" ) );
mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of weeks", "week(s)" ) );
mSuspendUnit->setToolTip(
i18nc( "@info:tooltip",
"Suspend the reminders using this time unit" ) );
mSuspendUnit->setWhatsThis(
i18nc( "@info:whatsthis",
"Each reminder for the selected incidences will be suspended "
"using this time unit. You can set the number of time units "
"in the adjacent number entry input." ) );
mSuspendUnit->setCurrentIndex( defSuspendUnit );
connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
connect( this, SIGNAL(okClicked()), this, SLOT(slotOk()) );
connect( this, SIGNAL(user1Clicked()), this, SLOT(slotUser1()) );
connect( this, SIGNAL(user2Clicked()), this, SLOT(slotUser2()) );
connect( this, SIGNAL(user3Clicked()), this, SLOT(slotUser3()) );
mIdentityManager = new CalendarSupport::IdentityManager;
}
AlarmDialog::~AlarmDialog()
{
mIncidenceTree->clear();
delete mIdentityManager;
}
ReminderTreeItem *AlarmDialog::searchByItem( const Akonadi::Item &incidence )
{
ReminderTreeItem *found = 0;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
if ( item->mIncidence == incidence ) {
found = item;
break;
}
++it;
}
return found;
}
static QString cleanSummary( const QString &summary )
{
static QString etc = i18nc( "@label an elipsis", "..." );
int maxLen = 30;
QString retStr = summary;
retStr.replace( QLatin1Char('\n'), QLatin1Char(' ') );
if ( retStr.length() > maxLen ) {
maxLen -= etc.length();
retStr = retStr.left( maxLen );
retStr += etc;
}
return retStr;
}
void AlarmDialog::addIncidence( const Akonadi::Item &incidenceitem,
const QDateTime &reminderAt,
const QString &displayText )
{
Incidence::Ptr incidence = CalendarSupport::incidence( incidenceitem );
ReminderTreeItem *item = searchByItem( incidenceitem );
if ( !item ) {
item = new ReminderTreeItem( incidenceitem, mIncidenceTree );
}
item->mNotified = false;
item->mHappening = KDateTime();
item->mRemindAt = reminderAt;
item->mTrigger = KDateTime::currentLocalDateTime();
item->mDisplayText = displayText;
item->setText( 0, cleanSummary( incidence->summary() ) );
Event::Ptr event;
Todo::Ptr todo;
QString displayStr;
const KDateTime dateTime = triggerDateForIncidence( incidence, reminderAt,
displayStr );
if ( incidence->type() == Incidence::TypeEvent ) {
item->setIcon( 0, SmallIcon( QLatin1String("view-calendar-day") ) );
} else if ( incidence->type() == Incidence::TypeTodo ) {
item->setIcon( 0, SmallIcon( QLatin1String("view-calendar-tasks")) );
}
item->mHappening = dateTime;
item->setText( 1, displayStr );
item->setText( 2, IncidenceFormatter::dateTimeToString(
item->mTrigger, false, true, KDateTime::Spec::LocalZone() ) );
QString tip =
IncidenceFormatter::toolTipStr(
CalendarSupport::displayName( mCalendar.data(), incidenceitem.parentCollection() ),
incidence,
item->mRemindAt.date(), true,
KDateTime::Spec::LocalZone() );
if ( !item->mDisplayText.isEmpty() ) {
tip += QLatin1String("<br>") + item->mDisplayText;
}
item->setToolTip( 0, tip );
item->setToolTip( 1, tip );
item->setToolTip( 2, tip );
item->setData( 0, QTreeWidgetItem::UserType, false );
mIncidenceTree->setCurrentItem( item );
showDetails( item );
slotSave();
}
void AlarmDialog::slotOk()
{
suspend();
}
void AlarmDialog::slotUser1()
{
edit();
}
void AlarmDialog::slotUser2()
{
dismissAll();
}
void AlarmDialog::slotUser3()
{
dismissCurrent();
}
void AlarmDialog::dismissCurrent()
{
dismiss( selectedItems() );
if ( activeCount() == 0 ) {
accept();
} else {
update();
}
emit reminderCount( activeCount() );
}
void AlarmDialog::dismissAll()
{
ReminderList selections;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
if ( !(*it)->isDisabled() ) { //do not disable suspended reminders
selections.append( static_cast<ReminderTreeItem *>( *it ) );
}
++it;
}
dismiss( selections );
setTimer();
accept();
emit reminderCount( activeCount() );
}
void AlarmDialog::dismiss( ReminderList selections )
{
QList<Akonadi::Item::Id> ids;
for ( ReminderList::Iterator it = selections.begin(); it != selections.end(); ++it ) {
kDebug() << "removing " << CalendarSupport::incidence( (*it)->mIncidence )->summary();
if ( mIncidenceTree->itemBelow( *it ) ) {
mIncidenceTree->setCurrentItem( mIncidenceTree->itemBelow( *it ) );
} else if ( mIncidenceTree->itemAbove( *it ) ) {
mIncidenceTree->setCurrentItem( mIncidenceTree->itemAbove( *it ) );
}
mIncidenceTree->removeItemWidget( *it, 0 );
ids.append( (*it)->mIncidence.id() );
delete *it;
}
removeFromConfig( ids );
}
void AlarmDialog::edit()
{
ReminderList selection = selectedItems();
if ( selection.count() != 1 ) {
return;
}
Incidence::Ptr incidence = CalendarSupport::incidence( selection.first()->mIncidence );
if ( !mCalendar->hasRight( selection.first()->mIncidence, Akonadi::Collection::CanChangeItem ) ) {
KMessageBox::sorry(
this,
i18nc( "@info",
"\"%1\" is a read-only item so modifications are not possible.",
cleanSummary( incidence->summary() ) ) );
return;
}
#if !defined(KDEPIM_MOBILE_UI)
openIncidenceEditorNG( selection.first()->mIncidence );
#else
openIncidenceEditorThroughKOrganizer( incidence );
#endif
}
void AlarmDialog::suspend()
{
if ( !isVisible() ) { //do nothing if the dialog is hidden
return;
}
int unit = 1;
switch ( mSuspendUnit->currentIndex() ) {
case 3: // weeks
unit *= 7;
case 2: // days
unit *= 24;
case 1: // hours
unit *= 60;
case 0: // minutes
unit *= 60;
default:
break;
}
ReminderTreeItem *selitem = 0;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
if ( (*it)->isSelected() && !(*it)->isDisabled() ) { //suspend selected, non-suspended reminders
(*it)->setSelected( false );
(*it)->setDisabled( true );
ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
item->mHappening = KDateTime( item->mRemindAt, KDateTime::Spec::LocalZone() );
item->mNotified = false;
(*it)->setText( 1, KGlobal::locale()->formatDateTime( item->mHappening ) );
selitem = item;
}
++it;
}
if ( selitem ) {
if ( mIncidenceTree->itemBelow( selitem ) ) {
mIncidenceTree->setCurrentItem( mIncidenceTree->itemBelow( selitem ) );
} else if ( mIncidenceTree->itemAbove( selitem ) ) {
mIncidenceTree->setCurrentItem( mIncidenceTree->itemAbove( selitem ) );
}
}
// save suspended alarms too so they can be restored on restart
// kolab/issue4108
slotSave();
setTimer();
if ( activeCount() == 0 ) {
accept();
} else {
update();
}
emit reminderCount( activeCount() );
}
void AlarmDialog::setTimer()
{
int nextReminderAt = -1;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
if ( item->mRemindAt > QDateTime::currentDateTime() ) {
const int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
nextReminderAt = nextReminderAt <= 0 ? secs : qMin( nextReminderAt, secs );
}
++it;
}
if ( nextReminderAt >= 0 ) {
mSuspendTimer.stop();
mSuspendTimer.start( 1000 * ( nextReminderAt + 1 ) );
mSuspendTimer.setSingleShot( true );
}
}
void AlarmDialog::show()
{
mIncidenceTree->resizeColumnToContents( 0 );
mIncidenceTree->resizeColumnToContents( 1 );
mIncidenceTree->resizeColumnToContents( 2 );
mIncidenceTree->sortItems( 1, Qt::AscendingOrder );
// select the first item that hasn't already been notified
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
if ( !item->mNotified ) {
(*it)->setSelected( true );
break;
}
++it;
}
// reset the default suspend time
// Allen: commented-out the following lines on 17 Sept 2013
// mSuspendSpin->setValue( defSuspendVal );
// mSuspendUnit->setCurrentIndex( defSuspendUnit );
KDialog::show();
if ( !mPos.isNull() ) {
KDialog::move( mPos );
}
KWindowSystem::unminimizeWindow( winId(), false );
KWindowSystem::setState( winId(), NET::KeepAbove | NET::DemandsAttention );
KWindowSystem::setOnAllDesktops( winId(), true );
KWindowSystem::activateWindow( winId() );
// Audio, Procedure, and EMail alarms
eventNotification();
}
void AlarmDialog::suspendAll()
{
mIncidenceTree->clearSelection();
QTreeWidgetItemIterator it( mIncidenceTree );
// first, select all non-suspended reminders
while ( *it ) {
if ( !(*it)->isDisabled() ) { //do not suspend suspended reminders
(*it)->setSelected( true );
}
++it;
}
//suspend all selected reminders
suspend();
}
void AlarmDialog::eventNotification()
{
bool beeped = false;
bool found = false;
ReminderList list;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
++it;
if ( item->isDisabled() || item->mNotified ) {
//skip suspended reminders or reminders that have been notified
continue;
}
found = true;
item->mNotified = true;
Incidence::Ptr incidence = CalendarSupport::incidence( item->mIncidence );
Alarm::List alarms = incidence->alarms();
Alarm::List::ConstIterator ait;
for ( ait = alarms.constBegin(); ait != alarms.constEnd(); ++ait ) {
Alarm::Ptr alarm = *ait;
// FIXME: Check whether this should be done for all multiple alarms
if ( alarm->type() == Alarm::Procedure ) {
// FIXME: Add a message box asking whether the procedure should really be executed
kDebug() << "Starting program: '" << alarm->programFile() << "'";
QString program = alarm->programFile();
// if the program name contains spaces escape it
if ( program.contains( QLatin1Char(' ') ) &&
!( program.startsWith( QLatin1Char('\"') ) &&
program.endsWith( QLatin1Char('\"') ) ) ) {
program = QLatin1Char('\"') + program + QLatin1Char('\"');
}
QProcess::startDetached( program + QLatin1Char(' ') + alarm->programArguments() );
} else if ( alarm->type() == Alarm::Audio ) {
beeped = true;
Phonon::MediaObject *player =
Phonon::createPlayer( Phonon::NotificationCategory,
KUrl( alarm->audioFile() ) );
player->setParent( this );
connect( player, SIGNAL(finished()), player, SLOT(deleteLater()) );
player->play();
} else if ( alarm->type() == Alarm::Email ) {
QString from = CalendarSupport::KCalPrefs::instance()->email();
Identity id = mIdentityManager->identityForAddress( from );
QString to;
if ( alarm->mailAddresses().isEmpty() ) {
to = from;
} else {
const Person::List addresses = alarm->mailAddresses();
QStringList add;
for ( Person::List::ConstIterator it = addresses.constBegin();
it != addresses.constEnd(); ++it ) {
add << (*it)->fullName();
}
to = add.join( QLatin1String(", ") );
}
QString subject;
Akonadi::Item parentItem = mCalendar->item( alarm->parentUid() );
Incidence::Ptr parent = CalendarSupport::incidence( parentItem );
if ( alarm->mailSubject().isEmpty() ) {
if ( parent->summary().isEmpty() ) {
subject = i18nc( "@title", "Reminder" );
} else {
subject = i18nc( "@title", "Reminder: %1", cleanSummary( parent->summary() ) );
}
} else {
subject = i18nc( "@title", "Reminder: %1", alarm->mailSubject() );
}
QString body =
IncidenceFormatter::mailBodyStr(
parent.staticCast<IncidenceBase>(), KSystemTimeZones::local() );
if ( !alarm->mailText().isEmpty() ) {
body += QLatin1Char('\n') + alarm->mailText();
}
//TODO: support attachments
KOrg::MailClient mailer;
mailer.send( id, from, to, QString(), subject, body, true, false, QString(),
MailTransport::TransportManager::self()->defaultTransportName() );
}
}
}
if ( !beeped && found ) {
KNotification::beep();
}
}
void AlarmDialog::wakeUp()
{
bool activeReminders = false;
QTreeWidgetItemIterator it( mIncidenceTree );
QTreeWidgetItem *firstItem = 0;
while ( *it ) {
if ( !firstItem ) {
firstItem = *it;
}
ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
Incidence::Ptr incidence = CalendarSupport::incidence( item->mIncidence );
if ( item->mRemindAt <= QDateTime::currentDateTime() ) {
if ( item->isDisabled() ) { //do not wakeup non-suspended reminders
item->setDisabled( false );
item->setSelected( false );
}
activeReminders = true;
} else {
item->setDisabled( true );
}
++it;
}
if ( activeReminders ) {
show();
}
setTimer();
showDetails( firstItem );
emit reminderCount( activeCount() );
}
void AlarmDialog::slotSave()
{
KSharedConfig::Ptr config = KGlobal::config();
KConfigGroup generalConfig( config, "General" );
int numReminders = 0;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
KConfigGroup incidenceConfig( config,
QString::fromLatin1( "Incidence-%1" ).arg( numReminders + 1 ) );
Incidence::Ptr incidence = CalendarSupport::incidence( item->mIncidence );
incidenceConfig.writeEntry( "AkonadiUrl", item->mIncidence.url() );
incidenceConfig.writeEntry( "RemindAt", item->mRemindAt );
++numReminders;
++it;
}
generalConfig.writeEntry( "Reminders", numReminders );
generalConfig.writeEntry( "Position", pos() );
config->sync();
}
ReminderList AlarmDialog::selectedItems() const
{
ReminderList list;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
if ( (*it)->isSelected() ) {
list.append( static_cast<ReminderTreeItem *>( *it ) );
}
++it;
}
return list;
}
int AlarmDialog::activeCount()
{
int count = 0;
QTreeWidgetItemIterator it( mIncidenceTree );
while ( *it ) {
if ( !(*it)->isDisabled() ) { //suspended reminders are non-active
++count;
}
++it;
}
kDebug() << "computed " << count << " active reminders";
return count;
}
void AlarmDialog::closeEvent( QCloseEvent * )
{
slotSave();
accept();
}
void AlarmDialog::updateButtons()
{
const int count = selectedItems().count();
kDebug() << "selected items=" << count;
enableButton( User3, count > 0 ); // enable Dismiss, if >1 selected
enableButton( User1, count == 1 ); // enable Edit, if only 1 selected
enableButton( Ok, count > 0 ); // enable Suspend, if >1 selected
}
void AlarmDialog::toggleDetails( QTreeWidgetItem *item )
{
if ( !item ) {
return;
}
if ( !mDetailView->isHidden() ) {
if ( mLastItem == item ) {
resize( size().width(), size().height() - mDetailView->height() - 50 );
mDetailView->hide();
} else {
showDetails( item );
}
} else {
resize( size().width(), size().height() + mDetailView->height() + 50 );
showDetails( item );
mDetailView->show();
}
mLastItem = item;
}
void AlarmDialog::showDetails( QTreeWidgetItem *item )
{
if ( !item ) {
return;
}
ReminderTreeItem *reminderItem = dynamic_cast<ReminderTreeItem *>( item );
if ( !reminderItem ) {
mDetailView->setIncidence( Akonadi::Item() );
} else {
if ( !reminderItem->mDisplayText.isEmpty() ) {
QString txt = QLatin1String("<qt><p><b>") + reminderItem->mDisplayText + QLatin1String("</b></p></qt>");
mDetailView->setHeaderText( txt );
} else {
mDetailView->setHeaderText( QString() );
}
Incidence::Ptr incidence = CalendarSupport::incidence( reminderItem->mIncidence );
mDetailView->setIncidence( reminderItem->mIncidence, reminderItem->mRemindAt.date() );
}
}
void AlarmDialog::update()
{
updateButtons();
if ( !mIncidenceTree->selectedItems().isEmpty() ) {
QTreeWidgetItem *item = mIncidenceTree->selectedItems().first();
toggleDetails( item );
}
}
void AlarmDialog::popupItemMenu( const QPoint &point )
{
QTreeWidgetItem *item = mIncidenceTree->itemAt( point );
if ( !item ) {
return;
}
ReminderTreeItem *reminderItem = dynamic_cast<ReminderTreeItem *>( item );
if ( reminderItem ) {
Incidence::Ptr incidence = CalendarSupport::incidence( reminderItem->mIncidence );
}
}
void AlarmDialog::accept()
{
if ( activeCount() == 0 ) {
mPos = pos();
hide();
}
}
/** static */
KDateTime AlarmDialog::triggerDateForIncidence( const Incidence::Ptr &incidence,
const QDateTime &reminderAt,
QString &displayStr )
{
KDateTime result;
if ( incidence->alarms().isEmpty() ) {
return result;
}
Alarm::Ptr alarm = incidence->alarms().first();
if ( incidence->recurs() ) {
result = incidence->recurrence()->getNextDateTime(
KDateTime( reminderAt, KDateTime::Spec::LocalZone( ) ) );
displayStr = KGlobal::locale()->formatDateTime( result.toLocalZone() );
}
if ( !result.isValid() ) {
result = incidence->dateTime( Incidence::RoleAlarm );
displayStr = IncidenceFormatter::dateTimeToString( result, false,
true,
KDateTime::Spec::LocalZone() );
}
return result;
}
void AlarmDialog::slotCalendarChanged()
{
KCalCore::Incidence::List incidences = mCalendar->incidences();
Akonadi::Item::List items = mCalendar->itemList( incidences );
for ( Akonadi::Item::List::ConstIterator it = items.constBegin();
it != items.constEnd(); ++it ) {
ReminderTreeItem *item = searchByItem( *it );
if ( item ) {
Incidence::Ptr incidence = CalendarSupport::incidence( *it );
QString displayStr;
// Yes, alarms can be empty, if someone edited the incidence and removed all alarms
if ( !incidence->alarms().isEmpty() ) {
const KDateTime dateTime = triggerDateForIncidence( incidence,
item->mRemindAt,
displayStr );
const QString summary = cleanSummary( incidence->summary() );
if ( displayStr != item->text( 1 ) || summary != item->text( 0 ) ) {
item->setText( 1, displayStr );
item->setText( 0, summary );
}
}
}
}
}
void AlarmDialog::keyPressEvent( QKeyEvent *e )
{
const int key = e->key() | e->modifiers();
if ( key == Qt::Key_Enter || key == Qt::Key_Return ) {
e->ignore();
return;
}
KDialog::keyPressEvent( e );
}
bool AlarmDialog::openIncidenceEditorThroughKOrganizer( const Incidence::Ptr &incidence )
{
if ( !QDBusConnection::sessionBus().interface()->isServiceRegistered( QLatin1String("org.kde.korganizer") ) ) {
if ( KToolInvocation::startServiceByDesktopName( QLatin1String("korganizer"), QString() ) ) {
KMessageBox::error(
this,
i18nc( "@info",
"Could not start KOrganizer so editing is not possible." ) );
return false;
}
}
org::kde::korganizer::Korganizer korganizer(
QLatin1String("org.kde.korganizer"), QLatin1String("/Korganizer"), QDBusConnection::sessionBus() );
kDebug() << "editing incidence " << incidence->summary();
if ( !korganizer.editIncidence( incidence->uid() ) ) {
KMessageBox::error(
this,
i18nc( "@info",
"An internal KOrganizer error occurred attempting to modify \"%1\"",
cleanSummary( incidence->summary() ) ) );
}
// get desktop # where korganizer (or kontact) runs
QString object =
QDBusConnection::sessionBus().interface()->isServiceRegistered( QLatin1String("org.kde.kontact") ) ?
QLatin1String("kontact/MainWindow_1") : QLatin1String("korganizer/MainWindow_1");
QDBusInterface korganizerObj( QLatin1String("org.kde.korganizer"), QLatin1Char('/') + object );
#ifdef Q_WS_X11
QDBusReply<int> reply = korganizerObj.call( QLatin1String("winId") );
if ( reply.isValid() ) {
int window = reply;
int desktop = KWindowSystem::windowInfo( window, NET::WMDesktop ).desktop();
if ( KWindowSystem::currentDesktop() == desktop ) {
KWindowSystem::minimizeWindow( winId(), false );
} else {
KWindowSystem::setCurrentDesktop( desktop );
}
KWindowSystem::activateWindow( KWindowSystem::transientFor( window ) );
}
#elif defined(Q_WS_WIN)
// WId is a typedef to a void* on windows
QDBusReply<qlonglong> reply = korganizerObj.call( QLatin1String("winId") );
if ( reply.isValid() ) {
qlonglong window = reply;
KWindowSystem::minimizeWindow( winId(), false );
KWindowSystem::allowExternalProcessWindowActivation();
KWindowSystem::activateWindow( reinterpret_cast<WId>(window) );
}
#else
// TODO (mac)
#endif
return true;
}
bool AlarmDialog::openIncidenceEditorNG( const Akonadi::Item &item )
{
Incidence::Ptr incidence = CalendarSupport::incidence( item );
IncidenceEditorNG::IncidenceDialog *dialog =
IncidenceEditorNG::IncidenceDialogFactory::create(
false, /*doesn't need initial saving*/
incidence->type(), 0, this );
dialog->load( item );
return true;
}
void AlarmDialog::removeFromConfig( const QList<Akonadi::Item::Id> &ids )
{
KSharedConfig::Ptr config = KGlobal::config();
KConfigGroup genGroup( config, "General" );
const int oldNumReminders = genGroup.readEntry( "Reminders", 0 );
QList<ConfItem> newReminders;
// Delete everything
for ( int i = 1; i <= oldNumReminders; ++i ) {
const QString group( QString::fromLatin1( "Incidence-%1" ).arg( i ) );
KConfigGroup incGroup( config, group );
const QString uid = incGroup.readEntry( "UID" );
const QDateTime remindAtDate = incGroup.readEntry( "RemindAt", QDateTime() );
const KUrl akonadiUrl = incGroup.readEntry( "AkonadiUrl" );
const Akonadi::Item::Id id = Akonadi::Item::fromUrl( akonadiUrl ).id();
if ( !ids.contains( id ) ) {
ConfItem ci;
ci.akonadiUrl = akonadiUrl;
ci.remindAt = remindAtDate;
ci.uid = uid;
newReminders.append( ci );
}
config->deleteGroup( group );
}
genGroup.writeEntry( "Reminders", newReminders.count() );
//Write everything except those which have an uid we don't want
for ( int i = 0; i < newReminders.count(); ++i ) {
const QString group( QString::fromLatin1( "Incidence-%1" ).arg( i + 1 ) );
KConfigGroup incGroup( config, group );
incGroup.writeEntry( "UID", newReminders[i].uid );
incGroup.writeEntry( "RemindAt", newReminders[i].remindAt );
incGroup.writeEntry( "AkonadiUrl", newReminders[i].akonadiUrl );
incGroup.sync();
}
genGroup.sync();
}