/*
* kalarmapp . cpp - the KAlarm application object
* Program : kalarm
* Copyright © 2001 - 2009 by David Jarvie < djarvie @ kde . org >
*
* 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 .
*/
# include "kalarm.h"
# include <stdlib.h>
# include <ctype.h>
# include <iostream>
# include <tqobjectlist.h>
# include <tqtimer.h>
# include <tqregexp.h>
# include <tqfile.h>
# include <tdecmdlineargs.h>
# include <tdelocale.h>
# include <kstandarddirs.h>
# include <tdeconfig.h>
# include <tdeaboutdata.h>
# include <dcopclient.h>
# include <kprocess.h>
# include <tdetempfile.h>
# include <tdefileitem.h>
# include <kstdguiitem.h>
# include <ktrader.h>
# include <kstaticdeleter.h>
# include <kdebug.h>
# include <libkcal/calformat.h>
# include <kalarmd/clientinfo.h>
# include "alarmcalendar.h"
# include "alarmlistview.h"
# include "birthdaydlg.h"
# include "editdlg.h"
# include "daemon.h"
# include "dcophandler.h"
# include "functions.h"
# include "kamail.h"
# include "karecurrence.h"
# include "mainwindow.h"
# include "messagebox.h"
# include "messagewin.h"
# include "preferences.h"
# include "prefdlg.h"
# include "shellprocess.h"
# include "traywindow.h"
# include "kalarmapp.moc"
# include <netwm.h>
static bool convWakeTime ( const TQCString & timeParam , TQDateTime & , bool & noTime ) ;
static bool convInterval ( const TQCString & timeParam , KARecurrence : : Type & , int & timeInterval , bool allowMonthYear = false ) ;
/******************************************************************************
* Find the maximum number of seconds late which a late - cancel alarm is allowed
* to be . This is calculated as the alarm daemon ' s check interval , plus a few
* seconds leeway to cater for any timing irregularities .
*/
static inline int maxLateness ( int lateCancel )
{
static const int LATENESS_LEEWAY = 5 ;
int lc = ( lateCancel > = 1 ) ? ( lateCancel - 1 ) * 60 : 0 ;
return Daemon : : maxTimeSinceCheck ( ) + LATENESS_LEEWAY + lc ;
}
KAlarmApp * KAlarmApp : : theInstance = 0 ;
int KAlarmApp : : mActiveCount = 0 ;
int KAlarmApp : : mFatalError = 0 ;
TQString KAlarmApp : : mFatalMessage ;
/******************************************************************************
* Construct the application .
*/
KAlarmApp : : KAlarmApp ( )
: KUniqueApplication ( ) ,
mInitialised ( false ) ,
mDcopHandler ( new DcopHandler ( ) ) ,
# ifdef OLD_DCOP
mDcopHandlerOld ( new DcopHandlerOld ( ) ) ,
# endif
mTrayWindow ( 0 ) ,
mPendingQuit ( false ) ,
mProcessingQueue ( false ) ,
mCheckingSystemTray ( false ) ,
mSessionClosingDown ( false ) ,
mRefreshExpiredAlarms ( false ) ,
mSpeechEnabled ( false )
{
Preferences : : initialise ( ) ;
Preferences : : connect ( TQT_SIGNAL ( preferencesChanged ( ) ) , this , TQT_SLOT ( slotPreferencesChanged ( ) ) ) ;
KCal : : CalFormat : : setApplication ( aboutData ( ) - > programName ( ) , AlarmCalendar : : icalProductId ( ) ) ;
KARecurrence : : setDefaultFeb29Type ( Preferences : : defaultFeb29Type ( ) ) ;
// Check if the system tray is supported by this window manager
mHaveSystemTray = true ; // assume yes in lieu of a test which works
if ( AlarmCalendar : : initialiseCalendars ( ) )
{
connect ( AlarmCalendar : : expiredCalendar ( ) , TQT_SIGNAL ( purged ( ) ) , TQT_SLOT ( slotExpiredPurged ( ) ) ) ;
TDEConfig * config = kapp - > config ( ) ;
config - > setGroup ( TQString : : fromLatin1 ( " General " ) ) ;
mNoSystemTray = config - > readBoolEntry ( TQString : : fromLatin1 ( " NoSystemTray " ) , false ) ;
mSavedNoSystemTray = mNoSystemTray ;
mOldRunInSystemTray = wantRunInSystemTray ( ) ;
mDisableAlarmsIfStopped = mOldRunInSystemTray & & ! mNoSystemTray & & Preferences : : disableAlarmsIfStopped ( ) ;
mStartOfDay = Preferences : : startOfDay ( ) ;
if ( Preferences : : hasStartOfDayChanged ( ) )
mStartOfDay . setHMS ( 100 , 0 , 0 ) ; // start of day time has changed: flag it as invalid
DateTime : : setStartOfDay ( mStartOfDay ) ;
mPrefsExpiredColour = Preferences : : expiredColour ( ) ;
mPrefsExpiredKeepDays = Preferences : : expiredKeepDays ( ) ;
}
// Check if the speech synthesis daemon is installed
mSpeechEnabled = ( TDETrader : : self ( ) - > query ( " DCOP/Text-to-Speech " , " Name == 'KTTSD' " ) . count ( ) > 0 ) ;
if ( ! mSpeechEnabled )
kdDebug ( 5950 ) < < " KAlarmApp::KAlarmApp(): speech synthesis disabled (KTTSD not found) " < < endl ;
// Check if KOrganizer is installed
TQString korg = TQString : : fromLatin1 ( " korganizer " ) ;
mKOrganizerEnabled = ! locate ( " exe " , korg ) . isNull ( ) | | ! TDEStandardDirs : : findExe ( korg ) . isNull ( ) ;
if ( ! mKOrganizerEnabled )
kdDebug ( 5950 ) < < " KAlarmApp::KAlarmApp(): KOrganizer options disabled (KOrganizer not found) " < < endl ;
}
/******************************************************************************
*/
KAlarmApp : : ~ KAlarmApp ( )
{
while ( ! mCommandProcesses . isEmpty ( ) )
{
ProcData * pd = mCommandProcesses . first ( ) ;
mCommandProcesses . pop_front ( ) ;
delete pd ;
}
AlarmCalendar : : terminateCalendars ( ) ;
}
/******************************************************************************
* Return the one and only KAlarmApp instance .
* If it doesn ' t already exist , it is created first .
*/
KAlarmApp * KAlarmApp : : getInstance ( )
{
if ( ! theInstance )
{
theInstance = new KAlarmApp ;
if ( mFatalError )
theInstance - > quitFatal ( ) ;
else
{
// This is here instead of in the constructor to avoid recursion
Daemon : : initialise ( ) ; // calendars must be initialised before calling this
}
}
return theInstance ;
}
/******************************************************************************
* Restore the saved session if required .
*/
bool KAlarmApp : : restoreSession ( )
{
if ( ! isRestored ( ) )
return false ;
if ( mFatalError )
{
quitFatal ( ) ;
return false ;
}
// Process is being restored by session management.
kdDebug ( 5950 ) < < " KAlarmApp::restoreSession(): Restoring \n " ;
+ + mActiveCount ;
if ( ! initCheck ( true ) ) // open the calendar file (needed for main windows)
{
- - mActiveCount ;
quitIf ( 1 , true ) ; // error opening the main calendar - quit
return true ;
}
MainWindow * trayParent = 0 ;
for ( int i = 1 ; TDEMainWindow : : canBeRestored ( i ) ; + + i )
{
TQString type = TDEMainWindow : : classNameOfToplevel ( i ) ;
if ( type = = TQString : : fromLatin1 ( " MainWindow " ) )
{
MainWindow * win = MainWindow : : create ( true ) ;
win - > restore ( i , false ) ;
if ( win - > isHiddenTrayParent ( ) )
trayParent = win ;
else
win - > show ( ) ;
}
else if ( type = = TQString : : fromLatin1 ( " MessageWin " ) )
{
MessageWin * win = new MessageWin ;
win - > restore ( i , false ) ;
if ( win - > isValid ( ) )
win - > show ( ) ;
else
delete win ;
}
}
initCheck ( ) ; // register with the alarm daemon
// Try to display the system tray icon if it is configured to be autostarted,
// or if we're in run-in-system-tray mode.
if ( Preferences : : autostartTrayIcon ( )
| | ( MainWindow : : count ( ) & & wantRunInSystemTray ( ) ) )
{
displayTrayIcon ( true , trayParent ) ;
// Occasionally for no obvious reason, the main main window is
// shown when it should be hidden, so hide it just to be sure.
if ( trayParent )
trayParent - > hide ( ) ;
}
- - mActiveCount ;
quitIf ( 0 ) ; // quit if no windows are open
return true ;
}
/******************************************************************************
* Called for a KUniqueApplication when a new instance of the application is
* started .
*/
int KAlarmApp : : newInstance ( )
{
kdDebug ( 5950 ) < < " KAlarmApp::newInstance() \n " ;
if ( mFatalError )
{
quitFatal ( ) ;
return 1 ;
}
+ + mActiveCount ;
int exitCode = 0 ; // default = success
static bool firstInstance = true ;
bool dontRedisplay = false ;
if ( ! firstInstance | | ! isRestored ( ) )
{
TQString usage ;
TDECmdLineArgs * args = TDECmdLineArgs : : parsedArgs ( ) ;
// Use a 'do' loop which is executed only once to allow easy error exits.
// Errors use 'break' to skip to the end of the function.
// Note that DCOP handling is only set up once the command line parameters
// have been checked, since we mustn't register with the alarm daemon only
// to quit immediately afterwards.
do
{
# define USAGE(message) { usage = message; break; }
if ( args - > isSet ( " stop " ) )
{
// Stop the alarm daemon
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): stop \n " ;
args - > clear ( ) ; // free up memory
if ( ! Daemon : : stop ( ) )
{
exitCode = 1 ;
break ;
}
dontRedisplay = true ; // exit program if no other instances running
}
else
if ( args - > isSet ( " reset " ) )
{
// Reset the alarm daemon, if it's running.
// (If it's not running, it will reset automatically when it eventually starts.)
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): reset \n " ;
args - > clear ( ) ; // free up memory
Daemon : : reset ( ) ;
dontRedisplay = true ; // exit program if no other instances running
}
else
if ( args - > isSet ( " tray " ) )
{
// Display only the system tray icon
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): tray \n " ;
args - > clear ( ) ; // free up memory
if ( ! mHaveSystemTray )
{
exitCode = 1 ;
break ;
}
if ( ! initCheck ( ) ) // open the calendar, register with daemon
{
exitCode = 1 ;
break ;
}
if ( ! displayTrayIcon ( true ) )
{
exitCode = 1 ;
break ;
}
}
else
if ( args - > isSet ( " handleEvent " ) | | args - > isSet ( " triggerEvent " ) | | args - > isSet ( " cancelEvent " ) | | args - > isSet ( " calendarURL " ) )
{
// Display or delete the event with the specified event ID
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): handle event \n " ;
EventFunc function = EVENT_HANDLE ;
int count = 0 ;
const char * option = 0 ;
if ( args - > isSet ( " handleEvent " ) ) { function = EVENT_HANDLE ; option = " handleEvent " ; + + count ; }
if ( args - > isSet ( " triggerEvent " ) ) { function = EVENT_TRIGGER ; option = " triggerEvent " ; + + count ; }
if ( args - > isSet ( " cancelEvent " ) ) { function = EVENT_CANCEL ; option = " cancelEvent " ; + + count ; }
if ( ! count )
USAGE ( i18n ( " %1 requires %2, %3 or %4 " ) . arg ( TQString : : fromLatin1 ( " --calendarURL " ) ) . arg ( TQString : : fromLatin1 ( " --handleEvent " ) ) . arg ( TQString : : fromLatin1 ( " --triggerEvent " ) ) . arg ( TQString : : fromLatin1 ( " --cancelEvent " ) ) )
if ( count > 1 )
USAGE ( i18n ( " %1, %2, %3 mutually exclusive " ) . arg ( TQString : : fromLatin1 ( " --handleEvent " ) ) . arg ( TQString : : fromLatin1 ( " --triggerEvent " ) ) . arg ( TQString : : fromLatin1 ( " --cancelEvent " ) ) ) ;
if ( ! initCheck ( true ) ) // open the calendar, don't register with daemon yet
{
exitCode = 1 ;
break ;
}
if ( args - > isSet ( " calendarURL " ) )
{
TQString calendarUrl = args - > getOption ( " calendarURL " ) ;
if ( KURL ( calendarUrl ) . url ( ) ! = AlarmCalendar : : activeCalendar ( ) - > urlString ( ) )
USAGE ( i18n ( " %1: wrong calendar file " ) . arg ( TQString : : fromLatin1 ( " --calendarURL " ) ) )
}
TQString eventID = args - > getOption ( option ) ;
args - > clear ( ) ; // free up memory
if ( eventID . startsWith ( TQString : : fromLatin1 ( " ad: " ) ) )
{
// It's a notification from the alarm deamon
eventID = eventID . mid ( 3 ) ;
Daemon : : queueEvent ( eventID ) ;
}
setUpDcop ( ) ; // start processing DCOP calls
if ( ! handleEvent ( eventID , function ) )
{
exitCode = 1 ;
break ;
}
}
else
if ( args - > isSet ( " edit " ) )
{
TQString eventID = args - > getOption ( " edit " ) ;
if ( ! initCheck ( ) )
{
exitCode = 1 ;
break ;
}
if ( ! KAlarm : : edit ( eventID ) )
{
USAGE ( i18n ( " %1: Event %2 not found, or not editable " ) . arg ( TQString : : fromLatin1 ( " --edit " ) ) . arg ( eventID ) )
exitCode = 1 ;
break ;
}
}
else
if ( args - > isSet ( " edit-new " ) | | args - > isSet ( " edit-new-preset " ) )
{
TQString templ ;
if ( args - > isSet ( " edit-new-preset " ) )
templ = args - > getOption ( " edit-new-preset " ) ;
if ( ! initCheck ( ) )
{
exitCode = 1 ;
break ;
}
KAlarm : : editNew ( templ ) ;
}
else
if ( args - > isSet ( " file " ) | | args - > isSet ( " exec " ) | | args - > isSet ( " mail " ) | | args - > count ( ) )
{
// Display a message or file, execute a command, or send an email
KAEvent : : Action action = KAEvent : : MESSAGE ;
TQCString alMessage ;
uint alFromID = 0 ;
EmailAddressList alAddresses ;
TQStringList alAttachments ;
TQCString alSubject ;
if ( args - > isSet ( " file " ) )
{
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): file \n " ;
if ( args - > isSet ( " exec " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --exec " ) ) . arg ( TQString : : fromLatin1 ( " --file " ) ) )
if ( args - > isSet ( " mail " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) . arg ( TQString : : fromLatin1 ( " --file " ) ) )
if ( args - > count ( ) )
USAGE ( i18n ( " message incompatible with %1 " ) . arg ( TQString : : fromLatin1 ( " --file " ) ) )
alMessage = args - > getOption ( " file " ) ;
action = KAEvent : : FILE ;
}
else if ( args - > isSet ( " exec " ) )
{
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): exec \n " ;
if ( args - > isSet ( " mail " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) . arg ( TQString : : fromLatin1 ( " --exec " ) ) )
alMessage = args - > getOption ( " exec " ) ;
int n = args - > count ( ) ;
for ( int i = 0 ; i < n ; + + i )
{
alMessage + = ' ' ;
alMessage + = args - > arg ( i ) ;
}
action = KAEvent : : COMMAND ;
}
else if ( args - > isSet ( " mail " ) )
{
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): mail \n " ;
if ( args - > isSet ( " subject " ) )
alSubject = args - > getOption ( " subject " ) ;
if ( args - > isSet ( " from-id " ) )
alFromID = KAMail : : identityUoid ( args - > getOption ( " from-id " ) ) ;
QCStringList params = args - > getOptionList ( " mail " ) ;
for ( QCStringList : : Iterator i = params . begin ( ) ; i ! = params . end ( ) ; + + i )
{
TQString addr = TQString : : fromLocal8Bit ( * i ) ;
if ( ! KAMail : : checkAddress ( addr ) )
USAGE ( i18n ( " %1: invalid email address " ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) )
alAddresses + = KCal : : Person ( TQString ( ) , addr ) ;
}
params = args - > getOptionList ( " attach " ) ;
for ( QCStringList : : Iterator i = params . begin ( ) ; i ! = params . end ( ) ; + + i )
alAttachments + = TQString : : fromLocal8Bit ( * i ) ;
alMessage = args - > arg ( 0 ) ;
action = KAEvent : : EMAIL ;
}
else
{
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): message \n " ;
alMessage = args - > arg ( 0 ) ;
}
if ( action ! = KAEvent : : EMAIL )
{
if ( args - > isSet ( " subject " ) )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --subject " ) ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) )
if ( args - > isSet ( " from-id " ) )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --from-id " ) ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) )
if ( args - > isSet ( " attach " ) )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --attach " ) ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) )
if ( args - > isSet ( " bcc " ) )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --bcc " ) ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) )
}
bool alarmNoTime = false ;
TQDateTime alarmTime , endTime ;
TQColor bgColour = Preferences : : defaultBgColour ( ) ;
TQColor fgColour = Preferences : : defaultFgColour ( ) ;
KARecurrence recurrence ;
int repeatCount = 0 ;
int repeatInterval = 0 ;
if ( args - > isSet ( " color " ) )
{
// Background colour is specified
TQCString colourText = args - > getOption ( " color " ) ;
if ( static_cast < const char * > ( colourText ) [ 0 ] = = ' 0 '
& & tolower ( static_cast < const char * > ( colourText ) [ 1 ] ) = = ' x ' )
colourText . replace ( 0 , 2 , " # " ) ;
bgColour . setNamedColor ( colourText ) ;
if ( ! bgColour . isValid ( ) )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " --color " ) ) )
}
if ( args - > isSet ( " colorfg " ) )
{
// Foreground colour is specified
TQCString colourText = args - > getOption ( " colorfg " ) ;
if ( static_cast < const char * > ( colourText ) [ 0 ] = = ' 0 '
& & tolower ( static_cast < const char * > ( colourText ) [ 1 ] ) = = ' x ' )
colourText . replace ( 0 , 2 , " # " ) ;
fgColour . setNamedColor ( colourText ) ;
if ( ! fgColour . isValid ( ) )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " --colorfg " ) ) )
}
if ( args - > isSet ( " time " ) )
{
TQCString dateTime = args - > getOption ( " time " ) ;
if ( ! convWakeTime ( dateTime , alarmTime , alarmNoTime ) )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " --time " ) ) )
}
else
alarmTime = TQDateTime : : currentDateTime ( ) ;
bool haveRecurrence = args - > isSet ( " recurrence " ) ;
if ( haveRecurrence )
{
if ( args - > isSet ( " login " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --login " ) ) . arg ( TQString : : fromLatin1 ( " --recurrence " ) ) )
if ( args - > isSet ( " until " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --until " ) ) . arg ( TQString : : fromLatin1 ( " --recurrence " ) ) )
TQCString rule = args - > getOption ( " recurrence " ) ;
recurrence . set ( TQString : : fromLocal8Bit ( static_cast < const char * > ( rule ) ) ) ;
}
if ( args - > isSet ( " interval " ) )
{
// Repeat count is specified
int count ;
if ( args - > isSet ( " login " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --login " ) ) . arg ( TQString : : fromLatin1 ( " --interval " ) ) )
bool ok ;
if ( args - > isSet ( " repeat " ) )
{
count = args - > getOption ( " repeat " ) . toInt ( & ok ) ;
if ( ! ok | | ! count | | count < - 1 | | ( count < 0 & & haveRecurrence ) )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " --repeat " ) ) )
}
else if ( haveRecurrence )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --interval " ) ) . arg ( TQString : : fromLatin1 ( " --repeat " ) ) )
else if ( args - > isSet ( " until " ) )
{
count = 0 ;
TQCString dateTime = args - > getOption ( " until " ) ;
if ( ! convWakeTime ( dateTime , endTime , alarmNoTime ) )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " --until " ) ) )
if ( endTime < alarmTime )
USAGE ( i18n ( " %1 earlier than %2 " ) . arg ( TQString : : fromLatin1 ( " --until " ) ) . arg ( TQString : : fromLatin1 ( " --time " ) ) )
}
else
count = - 1 ;
// Get the recurrence interval
int interval ;
KARecurrence : : Type recurType ;
if ( ! convInterval ( args - > getOption ( " interval " ) , recurType , interval , ! haveRecurrence )
| | interval < 0 )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " --interval " ) ) )
if ( alarmNoTime & & recurType = = KARecurrence : : MINUTELY )
USAGE ( i18n ( " Invalid %1 parameter for date-only alarm " ) . arg ( TQString : : fromLatin1 ( " --interval " ) ) )
if ( haveRecurrence )
{
// There is a also a recurrence specified, so set up a sub-repetition
int longestInterval = recurrence . longestInterval ( ) ;
if ( count * interval > longestInterval )
USAGE ( i18n ( " Invalid %1 and %2 parameters: repetition is longer than %3 interval " ) . arg ( TQString : : fromLatin1 ( " --interval " ) ) . arg ( TQString : : fromLatin1 ( " --repeat " ) ) . arg ( TQString : : fromLatin1 ( " --recurrence " ) ) ) ;
repeatCount = count ;
repeatInterval = interval ;
}
else
{
// There is no other recurrence specified, so convert the repetition
// parameters into a KCal::Recurrence
recurrence . set ( recurType , interval , count , DateTime ( alarmTime , alarmNoTime ) , endTime ) ;
}
}
else
{
if ( args - > isSet ( " repeat " ) )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --repeat " ) ) . arg ( TQString : : fromLatin1 ( " --interval " ) ) )
if ( args - > isSet ( " until " ) )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --until " ) ) . arg ( TQString : : fromLatin1 ( " --interval " ) ) )
}
TQCString audioFile ;
float audioVolume = - 1 ;
# ifdef WITHOUT_ARTS
bool audioRepeat = false ;
# else
bool audioRepeat = args - > isSet ( " play-repeat " ) ;
# endif
if ( audioRepeat | | args - > isSet ( " play " ) )
{
// Play a sound with the alarm
if ( audioRepeat & & args - > isSet ( " play " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --play " ) ) . arg ( TQString : : fromLatin1 ( " --play-repeat " ) ) )
if ( args - > isSet ( " beep " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --beep " ) ) . arg ( TQString : : fromLatin1 ( audioRepeat ? " --play-repeat " : " --play " ) ) )
if ( args - > isSet ( " speak " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --speak " ) ) . arg ( TQString : : fromLatin1 ( audioRepeat ? " --play-repeat " : " --play " ) ) )
audioFile = args - > getOption ( audioRepeat ? " play-repeat " : " play " ) ;
# ifndef WITHOUT_ARTS
if ( args - > isSet ( " volume " ) )
{
bool ok ;
int volumepc = args - > getOption ( " volume " ) . toInt ( & ok ) ;
if ( ! ok | | volumepc < 0 | | volumepc > 100 )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " --volume " ) ) )
audioVolume = static_cast < float > ( volumepc ) / 100 ;
}
# endif
}
# ifndef WITHOUT_ARTS
else if ( args - > isSet ( " volume " ) )
USAGE ( i18n ( " %1 requires %2 or %3 " ) . arg ( TQString : : fromLatin1 ( " --volume " ) ) . arg ( TQString : : fromLatin1 ( " --play " ) ) . arg ( TQString : : fromLatin1 ( " --play-repeat " ) ) )
# endif
if ( args - > isSet ( " speak " ) )
{
if ( args - > isSet ( " beep " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --beep " ) ) . arg ( TQString : : fromLatin1 ( " --speak " ) ) )
if ( ! mSpeechEnabled )
USAGE ( i18n ( " %1 requires speech synthesis to be configured using KTTSD " ) . arg ( TQString : : fromLatin1 ( " --speak " ) ) )
}
int reminderMinutes = 0 ;
bool onceOnly = args - > isSet ( " reminder-once " ) ;
if ( args - > isSet ( " reminder " ) | | onceOnly )
{
// Issue a reminder alarm in advance of the main alarm
if ( onceOnly & & args - > isSet ( " reminder " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( TQString : : fromLatin1 ( " --reminder " ) ) . arg ( TQString : : fromLatin1 ( " --reminder-once " ) ) )
TQString opt = onceOnly ? TQString : : fromLatin1 ( " --reminder-once " ) : TQString : : fromLatin1 ( " --reminder " ) ;
if ( args - > isSet ( " exec " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( opt ) . arg ( TQString : : fromLatin1 ( " --exec " ) ) )
if ( args - > isSet ( " mail " ) )
USAGE ( i18n ( " %1 incompatible with %2 " ) . arg ( opt ) . arg ( TQString : : fromLatin1 ( " --mail " ) ) )
KARecurrence : : Type recurType ;
TQString optval = args - > getOption ( onceOnly ? " reminder-once " : " reminder " ) ;
if ( ! convInterval ( args - > getOption ( onceOnly ? " reminder-once " : " reminder " ) , recurType , reminderMinutes ) )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( opt ) )
if ( recurType = = KARecurrence : : MINUTELY & & alarmNoTime )
USAGE ( i18n ( " Invalid %1 parameter for date-only alarm " ) . arg ( opt ) )
}
int lateCancel = 0 ;
if ( args - > isSet ( " late-cancel " ) )
{
KARecurrence : : Type recurType ;
bool ok = convInterval ( args - > getOption ( " late-cancel " ) , recurType , lateCancel ) ;
if ( ! ok | | lateCancel < = 0 )
USAGE ( i18n ( " Invalid %1 parameter " ) . arg ( TQString : : fromLatin1 ( " late-cancel " ) ) )
}
else if ( args - > isSet ( " auto-close " ) )
USAGE ( i18n ( " %1 requires %2 " ) . arg ( TQString : : fromLatin1 ( " --auto-close " ) ) . arg ( TQString : : fromLatin1 ( " --late-cancel " ) ) )
int flags = KAEvent : : DEFAULT_FONT ;
if ( args - > isSet ( " ack-confirm " ) )
flags | = KAEvent : : CONFIRM_ACK ;
if ( args - > isSet ( " auto-close " ) )
flags | = KAEvent : : AUTO_CLOSE ;
if ( args - > isSet ( " beep " ) )
flags | = KAEvent : : BEEP ;
if ( args - > isSet ( " speak " ) )
flags | = KAEvent : : SPEAK ;
if ( args - > isSet ( " korganizer " ) )
flags | = KAEvent : : COPY_KORGANIZER ;
if ( args - > isSet ( " disable " ) )
flags | = KAEvent : : DISABLED ;
if ( audioRepeat )
flags | = KAEvent : : REPEAT_SOUND ;
if ( args - > isSet ( " login " ) )
flags | = KAEvent : : REPEAT_AT_LOGIN ;
if ( args - > isSet ( " bcc " ) )
flags | = KAEvent : : EMAIL_BCC ;
if ( alarmNoTime )
flags | = KAEvent : : ANY_TIME ;
args - > clear ( ) ; // free up memory
// Display or schedule the event
if ( ! initCheck ( ) )
{
exitCode = 1 ;
break ;
}
if ( ! scheduleEvent ( action , alMessage , alarmTime , lateCancel , flags , bgColour , fgColour , TQFont ( ) , audioFile ,
audioVolume , reminderMinutes , recurrence , repeatInterval , repeatCount ,
alFromID , alAddresses , alSubject , alAttachments ) )
{
exitCode = 1 ;
break ;
}
}
else
{
// No arguments - run interactively & display the main window
kdDebug ( 5950 ) < < " KAlarmApp::newInstance(): interactive \n " ;
if ( args - > isSet ( " ack-confirm " ) )
usage + = TQString : : fromLatin1 ( " --ack-confirm " ) ;
if ( args - > isSet ( " attach " ) )
usage + = TQString : : fromLatin1 ( " --attach " ) ;
if ( args - > isSet ( " auto-close " ) )
usage + = TQString : : fromLatin1 ( " --auto-close " ) ;
if ( args - > isSet ( " bcc " ) )
usage + = TQString : : fromLatin1 ( " --bcc " ) ;
if ( args - > isSet ( " beep " ) )
usage + = TQString : : fromLatin1 ( " --beep " ) ;
if ( args - > isSet ( " color " ) )
usage + = TQString : : fromLatin1 ( " --color " ) ;
if ( args - > isSet ( " colorfg " ) )
usage + = TQString : : fromLatin1 ( " --colorfg " ) ;
if ( args - > isSet ( " disable " ) )
usage + = TQString : : fromLatin1 ( " --disable " ) ;
if ( args - > isSet ( " from-id " ) )
usage + = TQString : : fromLatin1 ( " --from-id " ) ;
if ( args - > isSet ( " korganizer " ) )
usage + = TQString : : fromLatin1 ( " --korganizer " ) ;
if ( args - > isSet ( " late-cancel " ) )
usage + = TQString : : fromLatin1 ( " --late-cancel " ) ;
if ( args - > isSet ( " login " ) )
usage + = TQString : : fromLatin1 ( " --login " ) ;
if ( args - > isSet ( " play " ) )
usage + = TQString : : fromLatin1 ( " --play " ) ;
# ifndef WITHOUT_ARTS
if ( args - > isSet ( " play-repeat " ) )
usage + = TQString : : fromLatin1 ( " --play-repeat " ) ;
# endif
if ( args - > isSet ( " reminder " ) )
usage + = TQString : : fromLatin1 ( " --reminder " ) ;
if ( args - > isSet ( " reminder-once " ) )
usage + = TQString : : fromLatin1 ( " --reminder-once " ) ;
if ( args - > isSet ( " speak " ) )
usage + = TQString : : fromLatin1 ( " --speak " ) ;
if ( args - > isSet ( " subject " ) )
usage + = TQString : : fromLatin1 ( " --subject " ) ;
if ( args - > isSet ( " time " ) )
usage + = TQString : : fromLatin1 ( " --time " ) ;
# ifndef WITHOUT_ARTS
if ( args - > isSet ( " volume " ) )
usage + = TQString : : fromLatin1 ( " --volume " ) ;
# endif
if ( ! usage . isEmpty ( ) )
{
usage + = i18n ( " : option(s) only valid with a message/%1/%2 " ) . arg ( TQString : : fromLatin1 ( " --file " ) ) . arg ( TQString : : fromLatin1 ( " --exec " ) ) ;
break ;
}
args - > clear ( ) ; // free up memory
if ( ! initCheck ( ) )
{
exitCode = 1 ;
break ;
}
( MainWindow : : create ( ) ) - > show ( ) ;
}
} while ( 0 ) ; // only execute once
if ( ! usage . isEmpty ( ) )
{
// Note: we can't use args->usage() since that also quits any other
// running 'instances' of the program.
std : : cerr < < usage . local8Bit ( ) . data ( )
< < i18n ( " \n Use --help to get a list of available command line options. \n " ) . local8Bit ( ) . data ( ) ;
exitCode = 1 ;
}
}
if ( firstInstance & & ! dontRedisplay & & ! exitCode )
redisplayAlarms ( ) ;
- - mActiveCount ;
firstInstance = false ;
// Quit the application if this was the last/only running "instance" of the program.
// Executing 'return' doesn't work very well since the program continues to
// run if no windows were created.
quitIf ( exitCode ) ;
return exitCode ;
}
/******************************************************************************
* Quit the program , optionally only if there are no more " instances " running .
*/
void KAlarmApp : : quitIf ( int exitCode , bool force )
{
if ( force )
{
// Quit regardless, except for message windows
MainWindow : : closeAll ( ) ;
displayTrayIcon ( false ) ;
if ( MessageWin : : instanceCount ( ) )
return ;
}
else
{
// Quit only if there are no more "instances" running
mPendingQuit = false ;
if ( mActiveCount > 0 | | MessageWin : : instanceCount ( ) )
return ;
int mwcount = MainWindow : : count ( ) ;
MainWindow * mw = mwcount ? MainWindow : : firstWindow ( ) : 0 ;
if ( mwcount > 1 | | ( mwcount & & ( ! mw - > isHidden ( ) | | ! mw - > isTrayParent ( ) ) ) )
return ;
// There are no windows left except perhaps a main window which is a hidden tray icon parent
if ( mTrayWindow )
{
// There is a system tray icon.
// Don't exit unless the system tray doesn't seem to exist.
if ( checkSystemTray ( ) )
return ;
}
if ( ! mDcopQueue . isEmpty ( ) | | ! mCommandProcesses . isEmpty ( ) )
{
// Don't quit yet if there are outstanding actions on the DCOP queue
mPendingQuit = true ;
mPendingQuitCode = exitCode ;
return ;
}
}
// This was the last/only running "instance" of the program, so exit completely.
kdDebug ( 5950 ) < < " KAlarmApp::quitIf( " < < exitCode < < " ): quitting " < < endl ;
BirthdayDlg : : close ( ) ;
exit ( exitCode ) ;
}
/******************************************************************************
* Called when the Quit menu item is selected .
* Closes the system tray window and all main windows , but does not exit the
* program if other windows are still open .
*/
void KAlarmApp : : doQuit ( TQWidget * parent )
{
kdDebug ( 5950 ) < < " KAlarmApp::doQuit() \n " ;
if ( mDisableAlarmsIfStopped
& & MessageBox : : warningContinueCancel ( parent , KMessageBox : : Cancel ,
i18n ( " Quitting will disable alarms \n (once any alarm message windows are closed). " ) ,
TQString ( ) , KStdGuiItem : : quit ( ) , Preferences : : QUIT_WARN
) ! = KMessageBox : : Yes )
return ;
quitIf ( 0 , true ) ;
}
/******************************************************************************
* Called when the session manager is about to close down the application .
*/
void KAlarmApp : : commitData ( TQSessionManager & sm )
{
mSessionClosingDown = true ;
KUniqueApplication : : commitData ( sm ) ;
mSessionClosingDown = false ; // reset in case shutdown is cancelled
}
/******************************************************************************
* Display an error message for a fatal error . Prevent further actions since
* the program state is unsafe .
*/
void KAlarmApp : : displayFatalError ( const TQString & message )
{
if ( ! mFatalError )
{
mFatalError = 1 ;
mFatalMessage = message ;
if ( theInstance )
TQTimer : : singleShot ( 0 , theInstance , TQT_SLOT ( quitFatal ( ) ) ) ;
}
}
/******************************************************************************
* Quit the program , once the fatal error message has been acknowledged .
*/
void KAlarmApp : : quitFatal ( )
{
switch ( mFatalError )
{
case 0 :
case 2 :
return ;
case 1 :
mFatalError = 2 ;
KMessageBox : : error ( 0 , mFatalMessage ) ;
mFatalError = 3 ;
// fall through to '3'
case 3 :
if ( theInstance )
theInstance - > quitIf ( 1 , true ) ;
break ;
}
TQTimer : : singleShot ( 1000 , this , TQT_SLOT ( quitFatal ( ) ) ) ;
}
/******************************************************************************
* The main processing loop for KAlarm .
* All KAlarm operations involving opening or updating calendar files are called
* from this loop to ensure that only one operation is active at any one time .
* This precaution is necessary because KAlarm ' s activities are mostly
* asynchronous , being in response to DCOP calls from the alarm daemon ( or other
* programs ) or timer events , any of which can be received in the middle of
* performing another operation . If a calendar file is opened or updated while
* another calendar operation is in progress , the program has been observed to
* hang , or the first calendar call has failed with data loss - clearly
* unacceptable ! !
*/
void KAlarmApp : : processQueue ( )
{
if ( mInitialised & & ! mProcessingQueue )
{
kdDebug ( 5950 ) < < " KAlarmApp::processQueue() \n " ;
mProcessingQueue = true ;
// Reset the alarm daemon if it's been queued
KAlarm : : resetDaemonIfQueued ( ) ;
// Process DCOP calls
while ( ! mDcopQueue . isEmpty ( ) )
{
DcopTQEntry & entry = mDcopQueue . first ( ) ;
if ( entry . eventId . isEmpty ( ) )
{
// It's a new alarm
switch ( entry . function )
{
case EVENT_TRIGGER :
execAlarm ( entry . event , entry . event . firstAlarm ( ) , false ) ;
break ;
case EVENT_HANDLE :
KAlarm : : addEvent ( entry . event , 0 ) ;
break ;
case EVENT_CANCEL :
break ;
}
}
else
handleEvent ( entry . eventId , entry . function ) ;
mDcopQueue . pop_front ( ) ;
}
// Purge the expired alarms calendar if it's time to do so
AlarmCalendar : : expiredCalendar ( ) - > purgeIfQueued ( ) ;
// Now that the queue has been processed, quit if a quit was queued
if ( mPendingQuit )
quitIf ( mPendingQuitCode ) ;
mProcessingQueue = false ;
}
}
/******************************************************************************
* Redisplay alarms which were being shown when the program last exited .
* Normally , these alarms will have been displayed by session restoration , but
* if the program crashed or was killed , we can redisplay them here so that
* they won ' t be lost .
*/
void KAlarmApp : : redisplayAlarms ( )
{
AlarmCalendar * cal = AlarmCalendar : : displayCalendar ( ) ;
if ( cal - > isOpen ( ) )
{
KCal : : Event : : List events = cal - > events ( ) ;
for ( KCal : : Event : : List : : ConstIterator it = events . begin ( ) ; it ! = events . end ( ) ; + + it )
{
KCal : : Event * kcalEvent = * it ;
KAEvent event ( * kcalEvent ) ;
event . setUid ( KAEvent : : ACTIVE ) ;
if ( ! MessageWin : : findEvent ( event . id ( ) ) )
{
// This event should be displayed, but currently isn't being
kdDebug ( 5950 ) < < " KAlarmApp::redisplayAlarms(): " < < event . id ( ) < < endl ;
KAAlarm alarm = event . convertDisplayingAlarm ( ) ;
( new MessageWin ( event , alarm , false , ! alarm . repeatAtLogin ( ) ) ) - > show ( ) ;
}
}
}
}
/******************************************************************************
* Called when the system tray main window is closed .
*/
void KAlarmApp : : removeWindow ( TrayWindow * )
{
mTrayWindow = 0 ;
quitIf ( ) ;
}
/******************************************************************************
* Display or close the system tray icon .
*/
bool KAlarmApp : : displayTrayIcon ( bool show , MainWindow * parent )
{
static bool creating = false ;
if ( show )
{
if ( ! mTrayWindow & & ! creating )
{
if ( ! mHaveSystemTray )
return false ;
if ( ! MainWindow : : count ( ) & & wantRunInSystemTray ( ) )
{
creating = true ; // prevent main window constructor from creating an additional tray icon
parent = MainWindow : : create ( ) ;
creating = false ;
}
mTrayWindow = new TrayWindow ( parent ? parent : MainWindow : : firstWindow ( ) ) ;
connect ( mTrayWindow , TQT_SIGNAL ( deleted ( ) ) , TQT_SIGNAL ( trayIconToggled ( ) ) ) ;
mTrayWindow - > show ( ) ;
emit trayIconToggled ( ) ;
// Set up a timer so that we can check after all events in the window system's
// event queue have been processed, whether the system tray actually exists
mCheckingSystemTray = true ;
mSavedNoSystemTray = mNoSystemTray ;
mNoSystemTray = false ;
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( slotSystemTrayTimer ( ) ) ) ;
}
}
else if ( mTrayWindow )
{
delete mTrayWindow ;
mTrayWindow = 0 ;
}
return true ;
}
/******************************************************************************
* Called by a timer to check whether the system tray icon has been housed in
* the system tray . Because there is a delay between the system tray icon show
* event and the icon being reparented by the system tray , we have to use a
* timer to check whether the system tray has actually grabbed it , or whether
* the system tray probably doesn ' t exist .
*/
void KAlarmApp : : slotSystemTrayTimer ( )
{
mCheckingSystemTray = false ;
if ( ! checkSystemTray ( ) )
quitIf ( 0 ) ; // exit the application if there are no open windows
}
/******************************************************************************
* Check whether the system tray icon has been housed in the system tray .
* If the system tray doesn ' t seem to exist , tell the alarm daemon to notify us
* of alarms regardless of whether we ' re running .
*/
bool KAlarmApp : : checkSystemTray ( )
{
if ( mCheckingSystemTray | | ! mTrayWindow )
return true ;
if ( mTrayWindow - > inSystemTray ( ) ! = ! mSavedNoSystemTray )
{
kdDebug ( 5950 ) < < " KAlarmApp::checkSystemTray(): changed -> " < < mSavedNoSystemTray < < endl ;
mNoSystemTray = mSavedNoSystemTray = ! mSavedNoSystemTray ;
// Store the new setting in the config file, so that if KAlarm exits and is then
// next activated by the daemon to display a message, it will register with the
// daemon with the correct NOTIFY type. If that happened when there was no system
// tray and alarms are disabled when KAlarm is not running, registering with
// NO_START_NOTIFY could result in alarms never being seen.
TDEConfig * config = kapp - > config ( ) ;
config - > setGroup ( TQString : : fromLatin1 ( " General " ) ) ;
config - > writeEntry ( TQString : : fromLatin1 ( " NoSystemTray " ) , mNoSystemTray ) ;
config - > sync ( ) ;
// Update other settings and reregister with the alarm daemon
slotPreferencesChanged ( ) ;
}
else
{
kdDebug ( 5950 ) < < " KAlarmApp::checkSystemTray(): no change = " < < ! mSavedNoSystemTray < < endl ;
mNoSystemTray = mSavedNoSystemTray ;
}
return ! mNoSystemTray ;
}
/******************************************************************************
* Return the main window associated with the system tray icon .
*/
MainWindow * KAlarmApp : : trayMainWindow ( ) const
{
return mTrayWindow ? mTrayWindow - > assocMainWindow ( ) : 0 ;
}
/******************************************************************************
* Called when KAlarm preferences have changed .
*/
void KAlarmApp : : slotPreferencesChanged ( )
{
bool newRunInSysTray = wantRunInSystemTray ( ) ;
if ( newRunInSysTray ! = mOldRunInSystemTray )
{
// The system tray run mode has changed
+ + mActiveCount ; // prevent the application from quitting
MainWindow * win = mTrayWindow ? mTrayWindow - > assocMainWindow ( ) : 0 ;
delete mTrayWindow ; // remove the system tray icon if it is currently shown
mTrayWindow = 0 ;
mOldRunInSystemTray = newRunInSysTray ;
if ( ! newRunInSysTray )
{
if ( win & & win - > isHidden ( ) )
delete win ;
}
displayTrayIcon ( true ) ;
- - mActiveCount ;
}
bool newDisableIfStopped = wantRunInSystemTray ( ) & & ! mNoSystemTray & & Preferences : : disableAlarmsIfStopped ( ) ;
if ( newDisableIfStopped ! = mDisableAlarmsIfStopped )
{
mDisableAlarmsIfStopped = newDisableIfStopped ; // N.B. this setting is used by Daemon::reregister()
Preferences : : setQuitWarn ( true ) ; // since mode has changed, re-allow warning messages on Quit
Daemon : : reregister ( ) ; // re-register with the alarm daemon
}
// Change alarm times for date-only alarms if the start of day time has changed
if ( Preferences : : startOfDay ( ) ! = mStartOfDay )
changeStartOfDay ( ) ;
// In case the date for February 29th recurrences has changed
KARecurrence : : setDefaultFeb29Type ( Preferences : : defaultFeb29Type ( ) ) ;
if ( Preferences : : expiredColour ( ) ! = mPrefsExpiredColour )
{
// The expired alarms text colour has changed
mRefreshExpiredAlarms = true ;
mPrefsExpiredColour = Preferences : : expiredColour ( ) ;
}
if ( Preferences : : expiredKeepDays ( ) ! = mPrefsExpiredKeepDays )
{
// How long expired alarms are being kept has changed.
// N.B. This also adjusts for any change in start-of-day time.
mPrefsExpiredKeepDays = Preferences : : expiredKeepDays ( ) ;
AlarmCalendar : : expiredCalendar ( ) - > setPurgeDays ( mPrefsExpiredKeepDays ) ;
}
if ( mRefreshExpiredAlarms )
{
mRefreshExpiredAlarms = false ;
MainWindow : : updateExpired ( ) ;
}
}
/******************************************************************************
* Change alarm times for date - only alarms after the start of day time has changed .
*/
void KAlarmApp : : changeStartOfDay ( )
{
Daemon : : notifyTimeChanged ( ) ; // tell the alarm daemon the new time
TQTime sod = Preferences : : startOfDay ( ) ;
DateTime : : setStartOfDay ( sod ) ;
AlarmCalendar * cal = AlarmCalendar : : activeCalendar ( ) ;
if ( KAEvent : : adjustStartOfDay ( cal - > events ( ) ) )
cal - > save ( ) ;
Preferences : : updateStartOfDayCheck ( ) ; // now that calendar is updated, set OK flag in config file
mStartOfDay = sod ;
}
/******************************************************************************
* Called when the expired alarms calendar has been purged .
* Updates the alarm list in all main windows .
*/
void KAlarmApp : : slotExpiredPurged ( )
{
mRefreshExpiredAlarms = false ;
MainWindow : : updateExpired ( ) ;
}
/******************************************************************************
* Return whether the program is configured to be running in the system tray .
*/
bool KAlarmApp : : wantRunInSystemTray ( ) const
{
return Preferences : : runInSystemTray ( ) & & mHaveSystemTray ;
}
/******************************************************************************
* Called to schedule a new alarm , either in response to a DCOP notification or
* to command line options .
* Reply = true unless there was a parameter error or an error opening calendar file .
*/
bool KAlarmApp : : scheduleEvent ( KAEvent : : Action action , const TQString & text , const TQDateTime & dateTime ,
int lateCancel , int flags , const TQColor & bg , const TQColor & fg , const TQFont & font ,
const TQString & audioFile , float audioVolume , int reminderMinutes ,
const KARecurrence & recurrence , int repeatInterval , int repeatCount ,
uint mailFromID , const EmailAddressList & mailAddresses ,
const TQString & mailSubject , const TQStringList & mailAttachments )
{
kdDebug ( 5950 ) < < " KAlarmApp::scheduleEvent(): " < < text < < endl ;
if ( ! dateTime . isValid ( ) )
return false ;
TQDateTime now = TQDateTime : : currentDateTime ( ) ;
if ( lateCancel & & dateTime < now . addSecs ( - maxLateness ( lateCancel ) ) )
return true ; // alarm time was already expired too long ago
TQDateTime alarmTime = dateTime ;
// Round down to the nearest minute to avoid scheduling being messed up
alarmTime . setTime ( TQTime ( alarmTime . time ( ) . hour ( ) , alarmTime . time ( ) . minute ( ) , 0 ) ) ;
KAEvent event ( alarmTime , text , bg , fg , font , action , lateCancel , flags ) ;
if ( reminderMinutes )
{
bool onceOnly = ( reminderMinutes < 0 ) ;
event . setReminder ( ( onceOnly ? - reminderMinutes : reminderMinutes ) , onceOnly ) ;
}
if ( ! audioFile . isEmpty ( ) )
event . setAudioFile ( audioFile , audioVolume , - 1 , 0 ) ;
if ( ! mailAddresses . isEmpty ( ) )
event . setEmail ( mailFromID , mailAddresses , mailSubject , mailAttachments ) ;
event . setRecurrence ( recurrence ) ;
event . setFirstRecurrence ( ) ;
event . setRepetition ( repeatInterval , repeatCount - 1 ) ;
if ( alarmTime < = now )
{
// Alarm is due for display already.
// First execute it once without adding it to the calendar file.
if ( ! mInitialised )
mDcopQueue . append ( DcopTQEntry ( event , EVENT_TRIGGER ) ) ;
else
execAlarm ( event , event . firstAlarm ( ) , false ) ;
// If it's a recurring alarm, reschedule it for its next occurrence
if ( ! event . recurs ( )
| | event . setNextOccurrence ( now ) = = KAEvent : : NO_OCCURRENCE )
return true ;
// It has recurrences in the future
}
// Queue the alarm for insertion into the calendar file
mDcopQueue . append ( DcopTQEntry ( event ) ) ;
if ( mInitialised )
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( processQueue ( ) ) ) ;
return true ;
}
/******************************************************************************
* Called in response to a DCOP notification by the alarm daemon that an event
* should be handled , i . e . displayed or cancelled .
* Optionally display the event . Delete the event from the calendar file and
* from every main window instance .
*/
bool KAlarmApp : : handleEvent ( const TQString & urlString , const TQString & eventID , EventFunc function )
{
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(DCOP): " < < eventID < < endl ;
AlarmCalendar * cal = AlarmCalendar : : activeCalendar ( ) ; // this can be called before calendars have been initialised
if ( cal & & KURL ( urlString ) . url ( ) ! = cal - > urlString ( ) )
{
kdError ( 5950 ) < < " KAlarmApp::handleEvent(DCOP): wrong calendar file " < < urlString < < endl ;
Daemon : : eventHandled ( eventID , false ) ;
return false ;
}
mDcopQueue . append ( DcopTQEntry ( function , eventID ) ) ;
if ( mInitialised )
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( processQueue ( ) ) ) ;
return true ;
}
/******************************************************************************
* Either :
* a ) Display the event and then delete it if it has no outstanding repetitions .
* b ) Delete the event .
* c ) Reschedule the event for its next repetition . If none remain , delete it .
* If the event is deleted , it is removed from the calendar file and from every
* main window instance .
*/
bool KAlarmApp : : handleEvent ( const TQString & eventID , EventFunc function )
{
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(): " < < eventID < < " , " < < ( function = = EVENT_TRIGGER ? " TRIGGER " : function = = EVENT_CANCEL ? " CANCEL " : function = = EVENT_HANDLE ? " HANDLE " : " ? " ) < < endl ;
KCal : : Event * kcalEvent = AlarmCalendar : : activeCalendar ( ) - > event ( eventID ) ;
if ( ! kcalEvent )
{
kdError ( 5950 ) < < " KAlarmApp::handleEvent(): event ID not found: " < < eventID < < endl ;
Daemon : : eventHandled ( eventID , false ) ;
return false ;
}
KAEvent event ( * kcalEvent ) ;
switch ( function )
{
case EVENT_CANCEL :
KAlarm : : deleteEvent ( event , true ) ;
break ;
case EVENT_TRIGGER : // handle it if it's due, else execute it regardless
case EVENT_HANDLE : // handle it if it's due
{
TQDateTime now = TQDateTime : : currentDateTime ( ) ;
bool updateCalAndDisplay = false ;
bool alarmToExecuteValid = false ;
KAAlarm alarmToExecute ;
// Check all the alarms in turn.
// Note that the main alarm is fetched before any other alarms.
for ( KAAlarm alarm = event . firstAlarm ( ) ; alarm . valid ( ) ; alarm = event . nextAlarm ( alarm ) )
{
// Check if the alarm is due yet.
int secs = alarm . dateTime ( true ) . dateTime ( ) . secsTo ( now ) ;
if ( secs < 0 )
{
// This alarm is definitely not due yet
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(): alarm " < < alarm . type ( ) < < " : not due \n " ;
continue ;
}
if ( alarm . repeatAtLogin ( ) )
{
// Alarm is to be displayed at every login.
// Check if the alarm has only just been set up.
// (The alarm daemon will immediately notify that it is due
// since it is set up with a time in the past.)
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(): REPEAT_AT_LOGIN \n " ;
if ( secs < maxLateness ( 1 ) )
continue ;
// Check if the main alarm is already being displayed.
// (We don't want to display both at the same time.)
if ( alarmToExecute . valid ( ) )
continue ;
// Set the time to display if it's a display alarm
alarm . setTime ( now ) ;
}
if ( alarm . lateCancel ( ) )
{
// Alarm is due, and it is to be cancelled if too late.
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(): LATE_CANCEL \n " ;
bool late = false ;
bool cancel = false ;
if ( alarm . dateTime ( ) . isDateOnly ( ) )
{
// The alarm has no time, so cancel it if its date is too far past
int maxlate = alarm . lateCancel ( ) / 1440 ; // maximum lateness in days
TQDateTime limit ( alarm . date ( ) . addDays ( maxlate + 1 ) , Preferences : : startOfDay ( ) ) ;
if ( now > = limit )
{
// It's too late to display the scheduled occurrence.
// Find the last previous occurrence of the alarm.
DateTime next ;
KAEvent : : OccurType type = event . previousOccurrence ( now , next , true ) ;
switch ( type & ~ KAEvent : : OCCURRENCE_REPEAT )
{
case KAEvent : : FIRST_OR_ONLY_OCCURRENCE :
case KAEvent : : RECURRENCE_DATE :
case KAEvent : : RECURRENCE_DATE_TIME :
case KAEvent : : LAST_RECURRENCE :
limit . setDate ( next . date ( ) . addDays ( maxlate + 1 ) ) ;
limit . setTime ( Preferences : : startOfDay ( ) ) ;
if ( now > = limit )
{
if ( type = = KAEvent : : LAST_RECURRENCE
| | ( type = = KAEvent : : FIRST_OR_ONLY_OCCURRENCE & & ! event . recurs ( ) ) )
cancel = true ; // last occurrence (and there are no repetitions)
else
late = true ;
}
break ;
case KAEvent : : NO_OCCURRENCE :
default :
late = true ;
break ;
}
}
}
else
{
// The alarm is timed. Allow it to be the permitted amount late before cancelling it.
int maxlate = maxLateness ( alarm . lateCancel ( ) ) ;
if ( secs > maxlate )
{
// It's over the maximum interval late.
// Find the most recent occurrence of the alarm.
DateTime next ;
KAEvent : : OccurType type = event . previousOccurrence ( now , next , true ) ;
switch ( type & ~ KAEvent : : OCCURRENCE_REPEAT )
{
case KAEvent : : FIRST_OR_ONLY_OCCURRENCE :
case KAEvent : : RECURRENCE_DATE :
case KAEvent : : RECURRENCE_DATE_TIME :
case KAEvent : : LAST_RECURRENCE :
if ( next . dateTime ( ) . secsTo ( now ) > maxlate )
{
if ( type = = KAEvent : : LAST_RECURRENCE
| | ( type = = KAEvent : : FIRST_OR_ONLY_OCCURRENCE & & ! event . recurs ( ) ) )
cancel = true ; // last occurrence (and there are no repetitions)
else
late = true ;
}
break ;
case KAEvent : : NO_OCCURRENCE :
default :
late = true ;
break ;
}
}
}
if ( cancel )
{
// All recurrences are finished, so cancel the event
event . setArchive ( ) ;
cancelAlarm ( event , alarm . type ( ) , false ) ;
updateCalAndDisplay = true ;
continue ;
}
if ( late )
{
// The latest repetition was too long ago, so schedule the next one
rescheduleAlarm ( event , alarm , false ) ;
updateCalAndDisplay = true ;
continue ;
}
}
if ( ! alarmToExecuteValid )
{
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(): alarm " < < alarm . type ( ) < < " : execute \n " ;
alarmToExecute = alarm ; // note the alarm to be executed
alarmToExecuteValid = true ; // only trigger one alarm for the event
}
else
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(): alarm " < < alarm . type ( ) < < " : skip \n " ;
}
// If there is an alarm to execute, do this last after rescheduling/cancelling
// any others. This ensures that the updated event is only saved once to the calendar.
if ( alarmToExecute . valid ( ) )
execAlarm ( event , alarmToExecute , true , ! alarmToExecute . repeatAtLogin ( ) ) ;
else
{
if ( function = = EVENT_TRIGGER )
{
// The alarm is to be executed regardless of whether it's due.
// Only trigger one alarm from the event - we don't want multiple
// identical messages, for example.
KAAlarm alarm = event . firstAlarm ( ) ;
if ( alarm . valid ( ) )
execAlarm ( event , alarm , false ) ;
}
if ( updateCalAndDisplay )
KAlarm : : updateEvent ( event , 0 ) ; // update the window lists and calendar file
else if ( function ! = EVENT_TRIGGER )
{
kdDebug ( 5950 ) < < " KAlarmApp::handleEvent(): no action \n " ;
Daemon : : eventHandled ( eventID , false ) ;
}
}
break ;
}
}
return true ;
}
/******************************************************************************
* Called when an alarm is currently being displayed , to store a copy of the
* alarm in the displaying calendar , and to reschedule it for its next repetition .
* If no repetitions remain , cancel it .
*/
void KAlarmApp : : alarmShowing ( KAEvent & event , KAAlarm : : Type alarmType , const DateTime & alarmTime )
{
kdDebug ( 5950 ) < < " KAlarmApp::alarmShowing( " < < event . id ( ) < < " , " < < KAAlarm : : debugType ( alarmType ) < < " ) \n " ;
KCal : : Event * kcalEvent = AlarmCalendar : : activeCalendar ( ) - > event ( event . id ( ) ) ;
if ( ! kcalEvent )
kdError ( 5950 ) < < " KAlarmApp::alarmShowing(): event ID not found: " < < event . id ( ) < < endl ;
else
{
KAAlarm alarm = event . alarm ( alarmType ) ;
if ( ! alarm . valid ( ) )
kdError ( 5950 ) < < " KAlarmApp::alarmShowing(): alarm type not found: " < < event . id ( ) < < " : " < < alarmType < < endl ;
else
{
// Copy the alarm to the displaying calendar in case of a crash, etc.
KAEvent dispEvent ;
dispEvent . setDisplaying ( event , alarmType , alarmTime . dateTime ( ) ) ;
AlarmCalendar * cal = AlarmCalendar : : displayCalendarOpen ( ) ;
if ( cal )
{
cal - > deleteEvent ( dispEvent . id ( ) ) ; // in case it already exists
cal - > addEvent ( dispEvent ) ;
cal - > save ( ) ;
}
rescheduleAlarm ( event , alarm , true ) ;
return ;
}
}
Daemon : : eventHandled ( event . id ( ) , false ) ;
}
/******************************************************************************
* Called when an alarm action has completed , to perform any post - alarm actions .
*/
void KAlarmApp : : alarmCompleted ( const KAEvent & event )
{
if ( ! event . postAction ( ) . isEmpty ( ) & & ShellProcess : : authorised ( ) )
{
TQString command = event . postAction ( ) ;
kdDebug ( 5950 ) < < " KAlarmApp::alarmCompleted( " < < event . id ( ) < < " ): " < < command < < endl ;
doShellCommand ( command , event , 0 , ProcData : : POST_ACTION ) ;
}
}
/******************************************************************************
* Reschedule the alarm for its next recurrence . If none remain , delete it .
* If the alarm is deleted and it is the last alarm for its event , the event is
* removed from the calendar file and from every main window instance .
*/
void KAlarmApp : : rescheduleAlarm ( KAEvent & event , const KAAlarm & alarm , bool updateCalAndDisplay )
{
kdDebug ( 5950 ) < < " KAlarmApp::rescheduleAlarm() " < < endl ;
bool update = false ;
if ( alarm . reminder ( ) | | alarm . deferred ( ) )
{
// It's an advance warning alarm or an extra deferred alarm, so delete it
event . removeExpiredAlarm ( alarm . type ( ) ) ;
update = true ;
}
else if ( alarm . repeatAtLogin ( ) )
{
// Leave an alarm which repeats at every login until its main alarm is deleted
if ( updateCalAndDisplay & & event . updated ( ) )
update = true ;
}
else
{
// Reschedule the alarm for its next recurrence.
KAEvent : : OccurType type = event . setNextOccurrence ( TQDateTime : : currentDateTime ( ) ) ;
switch ( type )
{
case KAEvent : : NO_OCCURRENCE :
// All repetitions are finished, so cancel the event
cancelAlarm ( event , alarm . type ( ) , updateCalAndDisplay ) ;
break ;
default :
if ( ! ( type & KAEvent : : OCCURRENCE_REPEAT ) )
break ;
// Next occurrence is a repeat, so fall through to recurrence handling
case KAEvent : : RECURRENCE_DATE :
case KAEvent : : RECURRENCE_DATE_TIME :
case KAEvent : : LAST_RECURRENCE :
// The event is due by now and repetitions still remain, so rewrite the event
if ( updateCalAndDisplay )
update = true ;
else
{
event . cancelCancelledDeferral ( ) ;
event . setUpdated ( ) ; // note that the calendar file needs to be updated
}
break ;
case KAEvent : : FIRST_OR_ONLY_OCCURRENCE :
// The first occurrence is still due?!?, so don't do anything
break ;
}
if ( event . deferred ( ) )
{
// Just in case there's also a deferred alarm, ensure it's removed
event . removeExpiredAlarm ( KAAlarm : : DEFERRED_ALARM ) ;
update = true ;
}
}
if ( update )
{
event . cancelCancelledDeferral ( ) ;
KAlarm : : updateEvent ( event , 0 ) ; // update the window lists and calendar file
}
}
/******************************************************************************
* Delete the alarm . If it is the last alarm for its event , the event is removed
* from the calendar file and from every main window instance .
*/
void KAlarmApp : : cancelAlarm ( KAEvent & event , KAAlarm : : Type alarmType , bool updateCalAndDisplay )
{
kdDebug ( 5950 ) < < " KAlarmApp::cancelAlarm() " < < endl ;
event . cancelCancelledDeferral ( ) ;
if ( alarmType = = KAAlarm : : MAIN_ALARM & & ! event . displaying ( ) & & event . toBeArchived ( ) )
{
// The event is being deleted. Save it in the expired calendar file first.
TQString id = event . id ( ) ; // save event ID since KAlarm::addExpiredEvent() changes it
KAlarm : : addExpiredEvent ( event ) ;
event . setEventID ( id ) ; // restore event ID
}
event . removeExpiredAlarm ( alarmType ) ;
if ( ! event . alarmCount ( ) )
KAlarm : : deleteEvent ( event , false ) ;
else if ( updateCalAndDisplay )
KAlarm : : updateEvent ( event , 0 ) ; // update the window lists and calendar file
}
/******************************************************************************
* Execute an alarm by displaying its message or file , or executing its command .
* Reply = ShellProcess instance if a command alarm
* ! = 0 if successful
* = 0 if the alarm is disabled , or if an error message was output .
*/
void * KAlarmApp : : execAlarm ( KAEvent & event , const KAAlarm & alarm , bool reschedule , bool allowDefer , bool noPreAction )
{
if ( ! event . enabled ( ) )
{
// The event is disabled.
if ( reschedule )
rescheduleAlarm ( event , alarm , true ) ;
return 0 ;
}
void * result = ( void * ) 1 ;
event . setArchive ( ) ;
switch ( alarm . action ( ) )
{
case KAAlarm : : MESSAGE :
case KAAlarm : : FILE :
{
// Display a message or file, provided that the same event isn't already being displayed
MessageWin * win = MessageWin : : findEvent ( event . id ( ) ) ;
// Find if we're changing a reminder message to the real message
bool reminder = ( alarm . type ( ) & KAAlarm : : REMINDER_ALARM ) ;
bool replaceReminder = ! reminder & & win & & ( win - > alarmType ( ) & KAAlarm : : REMINDER_ALARM ) ;
if ( ! reminder & & ! event . deferred ( )
& & ( replaceReminder | | ! win ) & & ! noPreAction
& & ! event . preAction ( ) . isEmpty ( ) & & ShellProcess : : authorised ( ) )
{
// It's not a reminder or a deferred alarm, and there is no message window
// (other than a reminder window) currently displayed for this alarm,
// and we need to execute a command before displaying the new window.
// Check whether the command is already being executed for this alarm.
for ( TQValueList < ProcData * > : : Iterator it = mCommandProcesses . begin ( ) ; it ! = mCommandProcesses . end ( ) ; + + it )
{
ProcData * pd = * it ;
if ( pd - > event - > id ( ) = = event . id ( ) & & ( pd - > flags & ProcData : : PRE_ACTION ) )
{
kdDebug ( 5950 ) < < " KAlarmApp::execAlarm(): already executing pre-DISPLAY command " < < endl ;
return pd - > process ; // already executing - don't duplicate the action
}
}
TQString command = event . preAction ( ) ;
kdDebug ( 5950 ) < < " KAlarmApp::execAlarm(): pre-DISPLAY command: " < < command < < endl ;
int flags = ( reschedule ? ProcData : : RESCHEDULE : 0 ) | ( allowDefer ? ProcData : : ALLOW_DEFER : 0 ) ;
if ( doShellCommand ( command , event , & alarm , ( flags | ProcData : : PRE_ACTION ) ) )
return result ; // display the message after the command completes
// Error executing command - display the message even though it failed
}
if ( ! event . enabled ( ) )
delete win ; // event is disabled - close its window
else if ( ! win
| | ( ! win - > hasDefer ( ) & & ! alarm . repeatAtLogin ( ) )
| | replaceReminder )
{
// Either there isn't already a message for this event,
// or there is a repeat-at-login message with no Defer
// button, which needs to be replaced with a new message,
// or the caption needs to be changed from "Reminder" to "Message".
if ( win )
win - > setRecreating ( ) ; // prevent post-alarm actions
delete win ;
( new MessageWin ( event , alarm , reschedule , allowDefer ) ) - > show ( ) ;
}
else
{
// Raise the existing message window and replay any sound
win - > repeat ( alarm ) ; // N.B. this reschedules the alarm
}
break ;
}
case KAAlarm : : COMMAND :
{
int flags = event . commandXterm ( ) ? ProcData : : EXEC_IN_XTERM : 0 ;
TQString command = event . cleanText ( ) ;
if ( event . commandScript ( ) )
{
// Store the command script in a temporary file for execution
kdDebug ( 5950 ) < < " KAlarmApp::execAlarm(): COMMAND: (script) " < < endl ;
TQString tmpfile = createTempScriptFile ( command , false , event , alarm ) ;
if ( tmpfile . isEmpty ( ) )
result = 0 ;
else
result = doShellCommand ( tmpfile , event , & alarm , ( flags | ProcData : : TEMP_FILE ) ) ;
}
else
{
kdDebug ( 5950 ) < < " KAlarmApp::execAlarm(): COMMAND: " < < command < < endl ;
result = doShellCommand ( command , event , & alarm , flags ) ;
}
if ( reschedule )
rescheduleAlarm ( event , alarm , true ) ;
break ;
}
case KAAlarm : : EMAIL :
{
kdDebug ( 5950 ) < < " KAlarmApp::execAlarm(): EMAIL to: " < < event . emailAddresses ( " , " ) < < endl ;
TQStringList errmsgs ;
if ( ! KAMail : : send ( event , errmsgs , ( reschedule | | allowDefer ) ) )
result = 0 ;
if ( ! errmsgs . isEmpty ( ) )
{
// Some error occurred, although the email may have been sent successfully
if ( result )
kdDebug ( 5950 ) < < " KAlarmApp::execAlarm(): copy error: " < < errmsgs [ 1 ] < < endl ;
else
kdDebug ( 5950 ) < < " KAlarmApp::execAlarm(): failed: " < < errmsgs [ 1 ] < < endl ;
( new MessageWin ( event , alarm . dateTime ( ) , errmsgs ) ) - > show ( ) ;
}
if ( reschedule )
rescheduleAlarm ( event , alarm , true ) ;
break ;
}
default :
return 0 ;
}
return result ;
}
/******************************************************************************
* Execute a shell command line specified by an alarm .
* If the PRE_ACTION bit of ' flags ' is set , the alarm will be executed via
* execAlarm ( ) once the command completes , the execAlarm ( ) parameters being
* derived from the remaining bits in ' flags ' .
*/
ShellProcess * KAlarmApp : : doShellCommand ( const TQString & command , const KAEvent & event , const KAAlarm * alarm , int flags )
{
kdDebug ( 5950 ) < < " KAlarmApp::doShellCommand( " < < command < < " , " < < event . id ( ) < < " ) " < < endl ;
TDEProcess : : Communication comms = TDEProcess : : NoCommunication ;
TQString cmd ;
TQString tmpXtermFile ;
if ( flags & ProcData : : EXEC_IN_XTERM )
{
// Execute the command in a terminal window.
cmd = Preferences : : cmdXTermCommand ( ) ;
cmd . replace ( " %t " , aboutData ( ) - > programName ( ) ) ; // set the terminal window title
if ( cmd . find ( " %C " ) > = 0 )
{
// Execute the command from a temporary script file
if ( flags & ProcData : : TEMP_FILE )
cmd . replace ( " %C " , command ) ; // the command is already calling a temporary file
else
{
tmpXtermFile = createTempScriptFile ( command , true , event , * alarm ) ;
if ( tmpXtermFile . isEmpty ( ) )
return 0 ;
cmd . replace ( " %C " , tmpXtermFile ) ; // %C indicates where to insert the command
}
}
else if ( cmd . find ( " %W " ) > = 0 )
{
// Execute the command from a temporary script file,
// with a sleep after the command is executed
tmpXtermFile = createTempScriptFile ( command + TQString : : fromLatin1 ( " \n sleep 86400 \n " ) , true , event , * alarm ) ;
if ( tmpXtermFile . isEmpty ( ) )
return 0 ;
cmd . replace ( " %W " , tmpXtermFile ) ; // %w indicates where to insert the command
}
else if ( cmd . find ( " %w " ) > = 0 )
{
// Append a sleep to the command.
// Quote the command in case it contains characters such as [>|;].
TQString exec = KShellProcess : : quote ( command + TQString : : fromLatin1 ( " ; sleep 86400 " ) ) ;
cmd . replace ( " %w " , exec ) ; // %w indicates where to insert the command string
}
else
{
// Set the command to execute.
// Put it in quotes in case it contains characters such as [>|;].
TQString exec = KShellProcess : : quote ( command ) ;
if ( cmd . find ( " %c " ) > = 0 )
cmd . replace ( " %c " , exec ) ; // %c indicates where to insert the command string
else
cmd . append ( exec ) ; // otherwise, simply append the command string
}
}
else
{
cmd = command ;
comms = TDEProcess : : AllOutput ;
}
ShellProcess * proc = new ShellProcess ( cmd ) ;
connect ( proc , TQT_SIGNAL ( shellExited ( ShellProcess * ) ) , TQT_SLOT ( slotCommandExited ( ShellProcess * ) ) ) ;
TQGuardedPtr < ShellProcess > logproc = 0 ;
if ( comms = = TDEProcess : : AllOutput & & ! event . logFile ( ) . isEmpty ( ) )
{
// Output is to be appended to a log file.
// Set up a logging process to write the command's output to.
connect ( proc , TQT_SIGNAL ( receivedStdout ( TDEProcess * , char * , int ) ) , TQT_SLOT ( slotCommandOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( proc , TQT_SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) , TQT_SLOT ( slotCommandOutput ( TDEProcess * , char * , int ) ) ) ;
logproc = new ShellProcess ( TQString : : fromLatin1 ( " cat >>%1 " ) . arg ( event . logFile ( ) ) ) ;
connect ( logproc , TQT_SIGNAL ( shellExited ( ShellProcess * ) ) , TQT_SLOT ( slotLogProcExited ( ShellProcess * ) ) ) ;
logproc - > start ( TDEProcess : : Stdin ) ;
TQCString heading ;
if ( alarm & & alarm - > dateTime ( ) . isValid ( ) )
{
TQString dateTime = alarm - > dateTime ( ) . isDateOnly ( )
? TDEGlobal : : locale ( ) - > formatDate ( alarm - > dateTime ( ) . date ( ) , true )
: TDEGlobal : : locale ( ) - > formatDateTime ( alarm - > dateTime ( ) . dateTime ( ) ) ;
heading . sprintf ( " \n ******* KAlarm %s ******* \n " , dateTime . latin1 ( ) ) ;
}
else
heading = " \n ******* KAlarm ******* \n " ;
logproc - > writeStdin ( heading , heading . length ( ) + 1 ) ;
}
ProcData * pd = new ProcData ( proc , logproc , new KAEvent ( event ) , ( alarm ? new KAAlarm ( * alarm ) : 0 ) , flags ) ;
if ( flags & ProcData : : TEMP_FILE )
pd - > tempFiles + = command ;
if ( ! tmpXtermFile . isEmpty ( ) )
pd - > tempFiles + = tmpXtermFile ;
mCommandProcesses . append ( pd ) ;
if ( proc - > start ( comms ) )
return proc ;
// Error executing command - report it
kdError ( 5950 ) < < " KAlarmApp::doShellCommand(): command failed to start \n " ;
commandErrorMsg ( proc , event , alarm , flags ) ;
mCommandProcesses . remove ( pd ) ;
delete pd ;
return 0 ;
}
/******************************************************************************
* Create a temporary script file containing the specified command string .
* Reply = path of temporary file , or null string if error .
*/
TQString KAlarmApp : : createTempScriptFile ( const TQString & command , bool insertShell , const KAEvent & event , const KAAlarm & alarm )
{
KTempFile tmpFile ( TQString ( ) , TQString ( ) , 0700 ) ;
tmpFile . setAutoDelete ( false ) ; // don't delete file when it is destructed
TQTextStream * stream = tmpFile . textStream ( ) ;
if ( ! stream )
kdError ( 5950 ) < < " KAlarmApp::createTempScript(): Unable to create a temporary script file " < < endl ;
else
{
if ( insertShell )
* stream < < " #! " < < ShellProcess : : shellPath ( ) < < " \n " ;
* stream < < command ;
tmpFile . close ( ) ;
if ( tmpFile . status ( ) )
kdError ( 5950 ) < < " KAlarmApp::createTempScript(): Error " < < tmpFile . status ( ) < < " writing to temporary script file " < < endl ;
else
return tmpFile . name ( ) ;
}
TQStringList errmsgs ( i18n ( " Error creating temporary script file " ) ) ;
( new MessageWin ( event , alarm . dateTime ( ) , errmsgs ) ) - > show ( ) ;
return TQString ( ) ;
}
/******************************************************************************
* Called when an executing command alarm sends output to stdout or stderr .
*/
void KAlarmApp : : slotCommandOutput ( TDEProcess * proc , char * buffer , int bufflen )
{
//kdDebug(5950) << "KAlarmApp::slotCommandOutput(): '" << TQCString(buffer, bufflen+1) << "'\n";
// Find this command in the command list
for ( TQValueList < ProcData * > : : Iterator it = mCommandProcesses . begin ( ) ; it ! = mCommandProcesses . end ( ) ; + + it )
{
ProcData * pd = * it ;
if ( pd - > process = = proc & & pd - > logProcess )
{
pd - > logProcess - > writeStdin ( buffer , bufflen ) ;
break ;
}
}
}
/******************************************************************************
* Called when a logging process completes .
*/
void KAlarmApp : : slotLogProcExited ( ShellProcess * proc )
{
// Because it's held as a guarded pointer in the ProcData structure,
// we don't need to set any pointers to zero.
delete proc ;
}
/******************************************************************************
* Called when a command alarm ' s execution completes .
*/
void KAlarmApp : : slotCommandExited ( ShellProcess * proc )
{
kdDebug ( 5950 ) < < " KAlarmApp::slotCommandExited() \n " ;
// Find this command in the command list
for ( TQValueList < ProcData * > : : Iterator it = mCommandProcesses . begin ( ) ; it ! = mCommandProcesses . end ( ) ; + + it )
{
ProcData * pd = * it ;
if ( pd - > process = = proc )
{
// Found the command
if ( pd - > logProcess )
pd - > logProcess - > stdinExit ( ) ; // terminate the logging process
// Check its exit status
if ( ! proc - > normalExit ( ) )
{
TQString errmsg = proc - > errorMessage ( ) ;
kdWarning ( 5950 ) < < " KAlarmApp::slotCommandExited( " < < pd - > event - > cleanText ( ) < < " ): " < < errmsg < < endl ;
if ( pd - > messageBoxParent )
{
// Close the existing informational KMessageBox for this process
TQObjectList * dialogs = pd - > messageBoxParent - > queryList ( " KDialogBase " , 0 , false , true ) ;
KDialogBase * dialog = ( KDialogBase * ) dialogs - > getFirst ( ) ;
delete dialog ;
delete dialogs ;
if ( ! pd - > tempFile ( ) )
{
errmsg + = ' \n ' ;
errmsg + = proc - > command ( ) ;
}
KMessageBox : : error ( pd - > messageBoxParent , errmsg ) ;
}
else
commandErrorMsg ( proc , * pd - > event , pd - > alarm , pd - > flags ) ;
}
if ( pd - > preAction ( ) )
execAlarm ( * pd - > event , * pd - > alarm , pd - > reschedule ( ) , pd - > allowDefer ( ) , true ) ;
mCommandProcesses . remove ( it ) ;
delete pd ;
break ;
}
}
// If there are now no executing shell commands, quit if a quit was queued
if ( mPendingQuit & & mCommandProcesses . isEmpty ( ) )
quitIf ( mPendingQuitCode ) ;
}
/******************************************************************************
* Output an error message for a shell command .
*/
void KAlarmApp : : commandErrorMsg ( const ShellProcess * proc , const KAEvent & event , const KAAlarm * alarm , int flags )
{
TQStringList errmsgs ;
if ( flags & ProcData : : PRE_ACTION )
errmsgs + = i18n ( " Pre-alarm action: " ) ;
else if ( flags & ProcData : : POST_ACTION )
errmsgs + = i18n ( " Post-alarm action: " ) ;
errmsgs + = proc - > errorMessage ( ) ;
if ( ! ( flags & ProcData : : TEMP_FILE ) )
errmsgs + = proc - > command ( ) ;
( new MessageWin ( event , ( alarm ? alarm - > dateTime ( ) : DateTime ( ) ) , errmsgs ) ) - > show ( ) ;
}
/******************************************************************************
* Notes that an informational KMessageBox is displayed for this process .
*/
void KAlarmApp : : commandMessage ( ShellProcess * proc , TQWidget * parent )
{
// Find this command in the command list
for ( TQValueList < ProcData * > : : Iterator it = mCommandProcesses . begin ( ) ; it ! = mCommandProcesses . end ( ) ; + + it )
{
ProcData * pd = * it ;
if ( pd - > process = = proc )
{
pd - > messageBoxParent = parent ;
break ;
}
}
}
/******************************************************************************
* Set up remaining DCOP handlers and start processing DCOP calls .
*/
void KAlarmApp : : setUpDcop ( )
{
if ( ! mInitialised )
{
mInitialised = true ; // we're now ready to handle DCOP calls
Daemon : : createDcopHandler ( ) ;
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( processQueue ( ) ) ) ; // process anything already queued
}
}
/******************************************************************************
* If this is the first time through , open the calendar file , optionally start
* the alarm daemon and register with it , and set up the DCOP handler .
*/
bool KAlarmApp : : initCheck ( bool calendarOnly )
{
bool startdaemon ;
AlarmCalendar * cal = AlarmCalendar : : activeCalendar ( ) ;
if ( ! cal - > isOpen ( ) )
{
kdDebug ( 5950 ) < < " KAlarmApp::initCheck(): opening active calendar \n " ;
// First time through. Open the calendar file.
if ( ! cal - > open ( ) )
return false ;
if ( ! mStartOfDay . isValid ( ) )
changeStartOfDay ( ) ; // start of day time has changed, so adjust date-only alarms
/* Need to open the display calendar now, since otherwise if the daemon
* immediately notifies display alarms , they will often be processed while
* redisplayAlarms ( ) is executing open ( ) ( but before open ( ) completes ) ,
* which causes problems ! !
*/
AlarmCalendar : : displayCalendar ( ) - > open ( ) ;
/* Need to open the expired alarm calendar now, since otherwise if the daemon
* immediately notifies multiple alarms , the second alarm is likely to be
* processed while the calendar is executing open ( ) ( but before open ( ) completes ) ,
* which causes a hang ! !
*/
AlarmCalendar : : expiredCalendar ( ) - > open ( ) ;
AlarmCalendar : : expiredCalendar ( ) - > setPurgeDays ( theInstance - > mPrefsExpiredKeepDays ) ;
startdaemon = true ;
}
else
startdaemon = ! Daemon : : isRegistered ( ) ;
if ( ! calendarOnly )
{
setUpDcop ( ) ; // start processing DCOP calls
if ( startdaemon )
Daemon : : start ( ) ; // make sure the alarm daemon is running
}
return true ;
}
/******************************************************************************
* Convert the - - time parameter string into a date / time or date value .
* The parameter is in the form [ [ [ yyyy - ] mm - ] dd - ] hh : mm or yyyy - mm - dd .
* Reply = true if successful .
*/
static bool convWakeTime ( const TQCString & timeParam , TQDateTime & dateTime , bool & noTime )
{
if ( timeParam . length ( ) > 19 )
return false ;
char timeStr [ 20 ] ;
strcpy ( timeStr , timeParam ) ;
int dt [ 5 ] = { - 1 , - 1 , - 1 , - 1 , - 1 } ;
char * s ;
char * end ;
// Get the minute value
if ( ( s = strchr ( timeStr , ' : ' ) ) = = 0 )
noTime = true ;
else
{
noTime = false ;
* s + + = 0 ;
dt [ 4 ] = strtoul ( s , & end , 10 ) ;
if ( end = = s | | * end | | dt [ 4 ] > = 60 )
return false ;
// Get the hour value
if ( ( s = strrchr ( timeStr , ' - ' ) ) = = 0 )
s = timeStr ;
else
* s + + = 0 ;
dt [ 3 ] = strtoul ( s , & end , 10 ) ;
if ( end = = s | | * end | | dt [ 3 ] > = 24 )
return false ;
}
bool dateSet = false ;
if ( s ! = timeStr )
{
dateSet = true ;
// Get the day value
if ( ( s = strrchr ( timeStr , ' - ' ) ) = = 0 )
s = timeStr ;
else
* s + + = 0 ;
dt [ 2 ] = strtoul ( s , & end , 10 ) ;
if ( end = = s | | * end | | dt [ 2 ] = = 0 | | dt [ 2 ] > 31 )
return false ;
if ( s ! = timeStr )
{
// Get the month value
if ( ( s = strrchr ( timeStr , ' - ' ) ) = = 0 )
s = timeStr ;
else
* s + + = 0 ;
dt [ 1 ] = strtoul ( s , & end , 10 ) ;
if ( end = = s | | * end | | dt [ 1 ] = = 0 | | dt [ 1 ] > 12 )
return false ;
if ( s ! = timeStr )
{
// Get the year value
dt [ 0 ] = strtoul ( timeStr , & end , 10 ) ;
if ( end = = timeStr | | * end )
return false ;
}
}
}
TQDate date ( dt [ 0 ] , dt [ 1 ] , dt [ 2 ] ) ;
TQTime time ( 0 , 0 , 0 ) ;
if ( noTime )
{
// No time was specified, so the full date must have been specified
if ( dt [ 0 ] < 0 )
return false ;
}
else
{
// Compile the values into a date/time structure
TQDateTime now = TQDateTime : : currentDateTime ( ) ;
if ( dt [ 0 ] < 0 )
date . setYMD ( now . date ( ) . year ( ) ,
( dt [ 1 ] < 0 ? now . date ( ) . month ( ) : dt [ 1 ] ) ,
( dt [ 2 ] < 0 ? now . date ( ) . day ( ) : dt [ 2 ] ) ) ;
time . setHMS ( dt [ 3 ] , dt [ 4 ] , 0 ) ;
if ( ! dateSet & & time < now . time ( ) )
date = date . addDays ( 1 ) ;
}
if ( ! date . isValid ( ) )
return false ;
dateTime . setDate ( date ) ;
dateTime . setTime ( time ) ;
return true ;
}
/******************************************************************************
* Convert a time interval command line parameter .
* ' timeInterval ' receives the count for the recurType . If ' allowMonthYear ' is
* false , ' timeInterval ' is converted to minutes .
* Reply = true if successful .
*/
static bool convInterval ( const TQCString & timeParam , KARecurrence : : Type & recurType , int & timeInterval , bool allowMonthYear )
{
TQCString timeString = timeParam ;
// Get the recurrence interval
bool ok = true ;
uint interval = 0 ;
bool negative = ( timeString [ 0 ] = = ' - ' ) ;
if ( negative )
timeString = timeString . right ( 1 ) ;
uint length = timeString . length ( ) ;
switch ( timeString [ length - 1 ] )
{
case ' Y ' :
if ( ! allowMonthYear )
ok = false ;
recurType = KARecurrence : : ANNUAL_DATE ;
timeString = timeString . left ( length - 1 ) ;
break ;
case ' W ' :
recurType = KARecurrence : : WEEKLY ;
timeString = timeString . left ( length - 1 ) ;
break ;
case ' D ' :
recurType = KARecurrence : : DAILY ;
timeString = timeString . left ( length - 1 ) ;
break ;
case ' M ' :
{
int i = timeString . find ( ' H ' ) ;
if ( i < 0 )
{
if ( ! allowMonthYear )
ok = false ;
recurType = KARecurrence : : MONTHLY_DAY ;
timeString = timeString . left ( length - 1 ) ;
}
else
{
recurType = KARecurrence : : MINUTELY ;
interval = timeString . left ( i ) . toUInt ( & ok ) * 60 ;
timeString = timeString . mid ( i + 1 , length - i - 2 ) ;
}
break ;
}
default : // should be a digit
recurType = KARecurrence : : MINUTELY ;
break ;
}
if ( ok )
interval + = timeString . toUInt ( & ok ) ;
if ( ! allowMonthYear )
{
// Convert time interval to minutes
switch ( recurType )
{
case KARecurrence : : WEEKLY :
interval * = 7 ;
// fall through to DAILY
case KARecurrence : : DAILY :
interval * = 24 * 60 ;
break ;
default :
break ;
}
}
timeInterval = static_cast < int > ( interval ) ;
if ( negative )
timeInterval = - timeInterval ;
return ok ;
}
KAlarmApp : : ProcData : : ProcData ( ShellProcess * p , ShellProcess * logp , KAEvent * e , KAAlarm * a , int f )
: process ( p ) ,
logProcess ( logp ) ,
event ( e ) ,
alarm ( a ) ,
messageBoxParent ( 0 ) ,
flags ( f )
{ }
KAlarmApp : : ProcData : : ~ ProcData ( )
{
while ( ! tempFiles . isEmpty ( ) )
{
// Delete the temporary file called by the XTerm command
TQFile f ( tempFiles . first ( ) ) ;
f . remove ( ) ;
tempFiles . remove ( tempFiles . begin ( ) ) ;
}
delete process ;
delete event ;
delete alarm ;
}