kalarm

functions.cpp

00001 /*
00002  *  functions.cpp  -  miscellaneous functions
00003  *  Program:  kalarm
00004  *  Copyright © 2001-2009 by David Jarvie <djarvie@kde.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"
00022 #include "functions.h"
00023 
00024 #include "alarmcalendar.h"
00025 #include "alarmevent.h"
00026 #include "alarmlistview.h"
00027 #include "daemon.h"
00028 #include "kalarmapp.h"
00029 #include "kamail.h"
00030 #include "mainwindow.h"
00031 #include "messagewin.h"
00032 #include "preferences.h"
00033 #include "shellprocess.h"
00034 #include "templatelistview.h"
00035 #include "templatemenuaction.h"
00036 
00037 #include <qdeepcopy.h>
00038 #include <qdir.h>
00039 #include <qregexp.h>
00040 
00041 #include <kconfig.h>
00042 #include <kaction.h>
00043 #include <kglobal.h>
00044 #include <klocale.h>
00045 #include <kstdguiitem.h>
00046 #include <kstdaccel.h>
00047 #include <kmessagebox.h>
00048 #include <kfiledialog.h>
00049 #include <dcopclient.h>
00050 #include <dcopref.h>
00051 #include <kdcopservicestarter.h>
00052 #include <kdebug.h>
00053 
00054 #include <libkcal/event.h>
00055 #include <libkcal/icalformat.h>
00056 #include <libkpimidentities/identitymanager.h>
00057 #include <libkpimidentities/identity.h>
00058 #include <libkcal/person.h>
00059 
00060 
00061 namespace
00062 {
00063 bool        resetDaemonQueued = false;
00064 QCString    korganizerName = "korganizer";
00065 QString     korgStartError;
00066 #define     KORG_DCOP_OBJECT    "KOrganizerIface"
00067 const char* KORG_DCOP_WINDOW  = "KOrganizer MainWindow";
00068 const char* KMAIL_DCOP_WINDOW = "kmail-mainwindow#1";
00069 
00070 bool sendToKOrganizer(const KAEvent&);
00071 bool deleteFromKOrganizer(const QString& eventID);
00072 bool runKOrganizer();
00073 }
00074 #ifdef HAVE_XTEST
00075 void x11_cancelScreenSaver();
00076 #endif
00077 
00078 
00079 namespace KAlarm
00080 {
00081 
00082 /******************************************************************************
00083 *  Display a main window with the specified event selected.
00084 */
00085 MainWindow* displayMainWindowSelected(const QString& eventID)
00086 {
00087     MainWindow* win = MainWindow::firstWindow();
00088     if (!win)
00089     {
00090         if (theApp()->checkCalendarDaemon())    // ensure calendar is open and daemon started
00091         {
00092             win = MainWindow::create();
00093             win->show();
00094         }
00095     }
00096     else
00097     {
00098         // There is already a main window, so make it the active window
00099         bool visible = win->isVisible();
00100         if (visible)
00101             win->hide();        // in case it's on a different desktop
00102         if (!visible  ||  win->isMinimized())
00103             win->showNormal();
00104         win->raise();
00105         win->setActiveWindow();
00106     }
00107     if (win  &&  !eventID.isEmpty())
00108         win->selectEvent(eventID);
00109     return win;
00110 }
00111 
00112 /******************************************************************************
00113 * Create a New Alarm KAction.
00114 */
00115 KAction* createNewAlarmAction(const QString& label, QObject* receiver, const char* slot, KActionCollection* actions, const char* name)
00116 {
00117     return new KAction(label, "filenew", KStdAccel::openNew(), receiver, slot, actions, name);
00118 }
00119 
00120 /******************************************************************************
00121 * Create a New From Template KAction.
00122 */
00123 TemplateMenuAction* createNewFromTemplateAction(const QString& label, QObject* receiver, const char* slot, KActionCollection* actions, const char* name)
00124 {
00125     return new TemplateMenuAction(label, "new_from_template", receiver, slot, actions, name);
00126 }
00127 
00128 /******************************************************************************
00129 * Add a new active (non-expired) alarm.
00130 * Save it in the calendar file and add it to every main window instance.
00131 * If 'selectionView' is non-null, the selection highlight is moved to the new
00132 * event in that listView instance.
00133 * 'event' is updated with the actual event ID.
00134 */
00135 UpdateStatus addEvent(KAEvent& event, AlarmListView* selectionView, QWidget* errmsgParent, bool useEventID, bool allowKOrgUpdate)
00136 {
00137     kdDebug(5950) << "KAlarm::addEvent(): " << event.id() << endl;
00138     UpdateStatus status = UPDATE_OK;
00139     if (!theApp()->checkCalendarDaemon())    // ensure calendar is open and daemon started
00140         return UPDATE_FAILED;
00141     else
00142     {
00143         // Save the event details in the calendar file, and get the new event ID
00144         AlarmCalendar* cal = AlarmCalendar::activeCalendar();
00145         if (!cal->addEvent(event, useEventID))
00146             status = UPDATE_FAILED;
00147         else if (!cal->save())
00148             status = SAVE_FAILED;
00149     }
00150     if (status == UPDATE_OK)
00151     {
00152         if (allowKOrgUpdate  &&  event.copyToKOrganizer())
00153         {
00154             if (!sendToKOrganizer(event))    // tell KOrganizer to show the event
00155                 status = UPDATE_KORG_ERR;
00156         }
00157 
00158         // Update the window lists
00159         AlarmListView::addEvent(event, selectionView);
00160         return status;
00161     }
00162 
00163     if (errmsgParent)
00164         displayUpdateError(errmsgParent, status, ERR_ADD, 1);
00165     return status;
00166 }
00167 
00168 /******************************************************************************
00169 * Save the event in the expired calendar file and adjust every main window instance.
00170 * The event's ID is changed to an expired ID if necessary.
00171 */
00172 bool addExpiredEvent(KAEvent& event)
00173 {
00174     kdDebug(5950) << "KAlarm::addExpiredEvent(" << event.id() << ")\n";
00175     AlarmCalendar* cal = AlarmCalendar::expiredCalendarOpen();
00176     if (!cal)
00177         return false;
00178     bool archiving = (KAEvent::uidStatus(event.id()) == KAEvent::ACTIVE);
00179     if (archiving)
00180         event.setSaveDateTime(QDateTime::currentDateTime());   // time stamp to control purging
00181     KCal::Event* kcalEvent = cal->addEvent(event);
00182     cal->save();
00183 
00184     // Update window lists
00185     if (!archiving)
00186         AlarmListView::addEvent(event, 0);
00187     else if (kcalEvent)
00188         AlarmListView::modifyEvent(KAEvent(*kcalEvent), 0);
00189     return true;
00190 }
00191 
00192 /******************************************************************************
00193 * Add a new template.
00194 * Save it in the calendar file and add it to every template list view.
00195 * If 'selectionView' is non-null, the selection highlight is moved to the new
00196 * event in that listView instance.
00197 * 'event' is updated with the actual event ID.
00198 */
00199 UpdateStatus addTemplate(KAEvent& event, TemplateListView* selectionView, QWidget* errmsgParent)
00200 {
00201     kdDebug(5950) << "KAlarm::addTemplate(): " << event.id() << endl;
00202     UpdateStatus status = UPDATE_OK;
00203 
00204     // Add the template to the calendar file
00205     AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
00206     if (!cal  ||  !cal->addEvent(event))
00207         status = UPDATE_FAILED;
00208     else if (!cal->save())
00209         status = SAVE_FAILED;
00210     else
00211     {
00212         cal->emitEmptyStatus();
00213 
00214         // Update the window lists
00215         TemplateListView::addEvent(event, selectionView);
00216         return UPDATE_OK;
00217     }
00218 
00219     if (errmsgParent)
00220         displayUpdateError(errmsgParent, status, ERR_TEMPLATE, 1);
00221     return status;
00222 }
00223 
00224 /******************************************************************************
00225 * Modify an active (non-expired) alarm in the calendar file and in every main
00226 * window instance.
00227 * The new event will have a different event ID from the old one.
00228 * If 'selectionView' is non-null, the selection highlight is moved to the
00229 * modified event in that listView instance.
00230 */
00231 UpdateStatus modifyEvent(KAEvent& oldEvent, const KAEvent& newEvent, AlarmListView* selectionView, QWidget* errmsgParent)
00232 {
00233     kdDebug(5950) << "KAlarm::modifyEvent(): '" << oldEvent.id() << endl;
00234 
00235     UpdateStatus status = UPDATE_OK;
00236     if (!newEvent.valid())
00237     {
00238         deleteEvent(oldEvent, true);
00239         status = UPDATE_FAILED;
00240     }
00241     else
00242     {
00243         if (oldEvent.copyToKOrganizer())
00244         {
00245             // Tell KOrganizer to delete its old event.
00246             // But ignore errors, because the user could have manually
00247             // deleted it since KAlarm asked KOrganizer to set it up.
00248             deleteFromKOrganizer(oldEvent.id());
00249         }
00250 
00251         // Update the event in the calendar file, and get the new event ID
00252         AlarmCalendar* cal = AlarmCalendar::activeCalendar();
00253         if (!cal->deleteEvent(oldEvent.id())
00254         ||  !cal->addEvent(const_cast<KAEvent&>(newEvent), true))
00255             status = UPDATE_FAILED;
00256         else if (!cal->save())
00257             status = SAVE_FAILED;
00258         if (status == UPDATE_OK)
00259         {
00260             if (newEvent.copyToKOrganizer())
00261             {
00262                 if (!sendToKOrganizer(newEvent))    // tell KOrganizer to show the new event
00263                     status = UPDATE_KORG_ERR;
00264             }
00265 
00266             // Update the window lists
00267             AlarmListView::modifyEvent(oldEvent.id(), newEvent, selectionView);
00268             return status;
00269         }
00270     }
00271 
00272     if (errmsgParent)
00273         displayUpdateError(errmsgParent, status, ERR_ADD, 1);
00274     return status;
00275 }
00276 
00277 /******************************************************************************
00278 * Update an active (non-expired) alarm from the calendar file and from every
00279 * main window instance.
00280 * The new event will have the same event ID as the old one.
00281 * If 'selectionView' is non-null, the selection highlight is moved to the
00282 * updated event in that listView instance.
00283 * The event is not updated in KOrganizer, since this function is called when an
00284 * existing alarm is rescheduled (due to recurrence or deferral).
00285 */
00286 UpdateStatus updateEvent(KAEvent& event, AlarmListView* selectionView, QWidget* errmsgParent, bool archiveOnDelete, bool incRevision)
00287 {
00288     kdDebug(5950) << "KAlarm::updateEvent(): " << event.id() << endl;
00289 
00290     if (!event.valid())
00291         deleteEvent(event, archiveOnDelete);
00292     else
00293     {
00294         // Update the event in the calendar file.
00295         if (incRevision)
00296             event.incrementRevision();    // ensure alarm daemon sees the event has changed
00297         AlarmCalendar* cal = AlarmCalendar::activeCalendar();
00298         cal->updateEvent(event);
00299         if (!cal->save())
00300         {
00301             if (errmsgParent)
00302                 displayUpdateError(errmsgParent, SAVE_FAILED, ERR_ADD, 1);
00303             return SAVE_FAILED;
00304         }
00305 
00306         // Update the window lists
00307         AlarmListView::modifyEvent(event, selectionView);
00308     }
00309     return UPDATE_OK;
00310 }
00311 
00312 /******************************************************************************
00313 * Update a template in the calendar file and in every template list view.
00314 * If 'selectionView' is non-null, the selection highlight is moved to the
00315 * updated event in that listView instance.
00316 */
00317 UpdateStatus updateTemplate(const KAEvent& event, TemplateListView* selectionView, QWidget* errmsgParent)
00318 {
00319     UpdateStatus status = UPDATE_OK;
00320     AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
00321     if (!cal)
00322         status = UPDATE_FAILED;
00323     else
00324     {
00325         cal->updateEvent(event);
00326         if (!cal->save())
00327             status = SAVE_FAILED;
00328         else
00329         {
00330             TemplateListView::modifyEvent(event.id(), event, selectionView);
00331             return UPDATE_OK;
00332         }
00333     }
00334 
00335     if (errmsgParent)
00336         displayUpdateError(errmsgParent, SAVE_FAILED, ERR_TEMPLATE, 1);
00337     return status;
00338 }
00339 
00340 /******************************************************************************
00341 * Delete an alarm from the calendar file and from every main window instance.
00342 * If the event is archived, the event's ID is changed to an expired ID if necessary.
00343 */
00344 UpdateStatus deleteEvent(KAEvent& event, bool archive, QWidget* errmsgParent)
00345 {
00346     QString id = event.id();
00347     kdDebug(5950) << "KAlarm::deleteEvent(): " << id << endl;
00348 
00349     // Update the window lists
00350     AlarmListView::deleteEvent(id);
00351 
00352     UpdateStatus status = UPDATE_OK;
00353     AlarmCalendar* cal;
00354 
00355     // Delete the event from the calendar file
00356     if (KAEvent::uidStatus(id) == KAEvent::EXPIRED)
00357     {
00358         cal = AlarmCalendar::expiredCalendarOpen();
00359         if (!cal)
00360             status = UPDATE_FAILED;
00361     }
00362     else
00363     {
00364         if (event.copyToKOrganizer())
00365         {
00366             // The event was shown in KOrganizer, so tell KOrganizer to
00367             // delete it. Note that an error could occur if the user
00368             // manually deleted it from KOrganizer since it was set up.
00369             if (!deleteFromKOrganizer(event.id()))
00370                 status = UPDATE_KORG_ERR;
00371         }
00372         if (archive  &&  event.toBeArchived())
00373             addExpiredEvent(event);     // this changes the event ID to an expired ID
00374         cal = AlarmCalendar::activeCalendar();
00375     }
00376     if (status != UPDATE_FAILED)
00377     {
00378         if (!cal->deleteEvent(id, true))   // save calendar after deleting
00379             status = SAVE_FAILED;
00380     }
00381     if (status > UPDATE_KORG_ERR  &&  errmsgParent)
00382         displayUpdateError(errmsgParent, SAVE_FAILED, ERR_DELETE, 1);
00383     return status;
00384 }
00385 
00386 /******************************************************************************
00387 * Delete a template from the calendar file and from every template list view.
00388 */
00389 UpdateStatus deleteTemplate(const KAEvent& event)
00390 {
00391     QString id = event.id();
00392 
00393     // Delete the template from the calendar file
00394     AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
00395     if (!cal)
00396         return UPDATE_FAILED;
00397     if (!cal->deleteEvent(id, true))    // save calendar after deleting
00398         return SAVE_FAILED;
00399     cal->emitEmptyStatus();
00400 
00401     // Update the window lists
00402     TemplateListView::deleteEvent(id);
00403     return UPDATE_OK;
00404 }
00405 
00406 /******************************************************************************
00407 * Delete an alarm from the display calendar.
00408 */
00409 void deleteDisplayEvent(const QString& eventID)
00410 {
00411     kdDebug(5950) << "KAlarm::deleteDisplayEvent(" << eventID << ")\n";
00412 
00413     if (KAEvent::uidStatus(eventID) == KAEvent::DISPLAYING)
00414     {
00415         AlarmCalendar* cal = AlarmCalendar::displayCalendarOpen();
00416         if (cal)
00417             cal->deleteEvent(eventID, true);   // save calendar after deleting
00418     }
00419 }
00420 
00421 /******************************************************************************
00422 * Undelete an expired alarm, and update every main window instance.
00423 * The archive bit is set to ensure that it gets re-archived if it is deleted again.
00424 * If 'selectionView' is non-null, the selection highlight is moved to the
00425 * restored event in that listView instance.
00426 */
00427 UpdateStatus reactivateEvent(KAEvent& event, AlarmListView* selectionView, bool useEventID)
00428 {
00429     QString id = event.id();
00430     kdDebug(5950) << "KAlarm::reactivateEvent(): " << id << endl;
00431 
00432     // Delete the event from the expired calendar file
00433     if (KAEvent::uidStatus(id) == KAEvent::EXPIRED)
00434     {
00435         QDateTime now = QDateTime::currentDateTime();
00436         if (event.occursAfter(now, true))
00437         {
00438             if (event.recurs()  ||  event.repeatCount())
00439                 event.setNextOccurrence(now);   // skip any recurrences in the past
00440             event.setArchive();    // ensure that it gets re-archived if it is deleted
00441 
00442             // Save the event details in the calendar file, and get the new event ID
00443             AlarmCalendar* cal = AlarmCalendar::activeCalendar();
00444             if (!cal->addEvent(event, useEventID))
00445                 return UPDATE_FAILED;
00446             if (!cal->save())
00447                 return SAVE_FAILED;
00448 
00449             UpdateStatus status = UPDATE_OK;
00450             if (event.copyToKOrganizer())
00451             {
00452                 if (!sendToKOrganizer(event))    // tell KOrganizer to show the event
00453                     status = UPDATE_KORG_ERR;
00454             }
00455 
00456             // Update the window lists
00457             AlarmListView::undeleteEvent(id, event, selectionView);
00458 
00459             cal = AlarmCalendar::expiredCalendarOpen();
00460             if (cal)
00461                 cal->deleteEvent(id, true);   // save calendar after deleting
00462             return status;
00463         }
00464     }
00465     return UPDATE_FAILED;
00466 }
00467 
00468 /******************************************************************************
00469 * Enable or disable an alarm in the calendar file and in every main window instance.
00470 * The new event will have the same event ID as the old one.
00471 * If 'selectionView' is non-null, the selection highlight is moved to the
00472 * updated event in that listView instance.
00473 */
00474 UpdateStatus enableEvent(KAEvent& event, AlarmListView* selectionView, bool enable)
00475 {
00476     kdDebug(5950) << "KAlarm::enableEvent(" << enable << "): " << event.id() << endl;
00477 
00478     if (enable != event.enabled())
00479     {
00480         event.setEnabled(enable);
00481 
00482         // Update the event in the calendar file
00483         AlarmCalendar* cal = AlarmCalendar::activeCalendar();
00484         cal->updateEvent(event);
00485         if (!cal->save())
00486             return SAVE_FAILED;
00487 
00488         // If we're disabling a display alarm, close any message window
00489         if (!enable  &&  event.displayAction())
00490         {
00491             MessageWin* win = MessageWin::findEvent(event.id());
00492             delete win;
00493         }
00494 
00495         // Update the window lists
00496         AlarmListView::modifyEvent(event, selectionView);
00497     }
00498     return UPDATE_OK;
00499 }
00500 
00501 /******************************************************************************
00502 * Display an error message about an error saving an event.
00503 */
00504 void displayUpdateError(QWidget* parent, UpdateStatus, UpdateError code, int nAlarms)
00505 {
00506     QString errmsg;
00507     switch (code)
00508     {
00509         case ERR_ADD:
00510             errmsg = (nAlarms > 1) ? i18n("Error saving alarms")
00511                                    : i18n("Error saving alarm");
00512             break;
00513         case ERR_DELETE:
00514             errmsg = (nAlarms > 1) ? i18n("Error deleting alarms")
00515                                    : i18n("Error deleting alarm");
00516             break;
00517         case ERR_REACTIVATE:
00518             errmsg = (nAlarms > 1) ? i18n("Error saving reactivated alarms")
00519                                    : i18n("Error saving reactivated alarm");
00520             break;
00521         case ERR_TEMPLATE:
00522             errmsg = i18n("Error saving alarm template");
00523             break;
00524     }
00525     KMessageBox::error(parent, errmsg);
00526 }
00527 
00528 /******************************************************************************
00529 * Display an error message corresponding to a specified alarm update error code.
00530 */
00531 void displayKOrgUpdateError(QWidget* parent, KOrgUpdateError code, int nAlarms)
00532 {
00533     QString errmsg;
00534     switch (code)
00535     {
00536         case KORG_ERR_ADD:
00537             errmsg = (nAlarms > 1) ? i18n("Unable to show alarms in KOrganizer")
00538                                    : i18n("Unable to show alarm in KOrganizer");
00539             break;
00540         case KORG_ERR_MODIFY:
00541             errmsg = i18n("Unable to update alarm in KOrganizer");
00542             break;
00543         case KORG_ERR_DELETE:
00544             errmsg = (nAlarms > 1) ? i18n("Unable to delete alarms from KOrganizer")
00545                                    : i18n("Unable to delete alarm from KOrganizer");
00546             break;
00547     }
00548     KMessageBox::error(parent, errmsg);
00549 }
00550 
00551 /******************************************************************************
00552 * Display the alarm edit dialogue to edit a specified alarm.
00553 */
00554 bool edit(const QString& eventID)
00555 {
00556     AlarmCalendar* cal;
00557     switch (KAEvent::uidStatus(eventID))
00558     {
00559         case KAEvent::ACTIVE:
00560             cal = AlarmCalendar::activeCalendar();
00561             break;
00562         case KAEvent::TEMPLATE:
00563             cal = AlarmCalendar::templateCalendarOpen();
00564             break;
00565         default:
00566             kdError(5950) << "KAlarm::edit(" << eventID << "): event not active or template" << endl;
00567             return false;
00568     }
00569     KCal::Event* kcalEvent = cal->event(eventID);
00570     if (!kcalEvent)
00571     {
00572         kdError(5950) << "KAlarm::edit(): event ID not found: " << eventID << endl;
00573         return false;
00574     }
00575     KAEvent event(*kcalEvent);
00576     MainWindow::executeEdit(event);
00577     return true;
00578 }
00579 
00580 /******************************************************************************
00581 * Display the alarm edit dialogue to edit a new alarm, optionally preset with
00582 * a template.
00583 */
00584 bool editNew(const QString& templateName)
00585 {
00586     bool result = true;
00587     if (!templateName.isEmpty())
00588     {
00589         AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
00590         if (cal)
00591         {
00592             KAEvent templateEvent = KAEvent::findTemplateName(*cal, templateName);
00593             if (templateEvent.valid())
00594             {
00595                 MainWindow::executeNew(templateEvent);
00596                 return true;
00597             }
00598             kdWarning(5950) << "KAlarm::editNew(" << templateName << "): template not found" << endl;
00599         }
00600         result = false;
00601     }
00602     MainWindow::executeNew();
00603     return result;
00604 }
00605 
00606 /******************************************************************************
00607 *  Returns a list of all alarm templates.
00608 *  If shell commands are disabled, command alarm templates are omitted.
00609 */
00610 QValueList<KAEvent> templateList()
00611 {
00612     QValueList<KAEvent> templates;
00613     AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
00614     if (cal)
00615     {
00616         bool includeCmdAlarms = ShellProcess::authorised();
00617         KCal::Event::List events = cal->events();
00618         for (KCal::Event::List::ConstIterator it = events.begin();  it != events.end();  ++it)
00619         {
00620             KCal::Event* kcalEvent = *it;
00621             KAEvent event(*kcalEvent);
00622             if (includeCmdAlarms  ||  event.action() != KAEvent::COMMAND)
00623                 templates.append(event);
00624         }
00625     }
00626     return templates;
00627 }
00628 
00629 /******************************************************************************
00630 * To be called after an alarm has been edited.
00631 * Prompt the user to re-enable alarms if they are currently disabled, and if
00632 * it's an email alarm, warn if no 'From' email address is configured.
00633 */
00634 void outputAlarmWarnings(QWidget* parent, const KAEvent* event)
00635 {
00636     if (event  &&  event->action() == KAEvent::EMAIL
00637     &&  Preferences::emailAddress().isEmpty())
00638         KMessageBox::information(parent, i18n("Please set the 'From' email address...",
00639                                               "%1\nPlease set it in the Preferences dialog.").arg(KAMail::i18n_NeedFromEmailAddress()));
00640 
00641     if (!Daemon::monitoringAlarms())
00642     {
00643         if (KMessageBox::warningYesNo(parent, i18n("Alarms are currently disabled.\nDo you want to enable alarms now?"),
00644                                       QString::null, i18n("Enable"), i18n("Keep Disabled"),
00645                                       QString::fromLatin1("EditEnableAlarms"))
00646                         == KMessageBox::Yes)
00647             Daemon::setAlarmsEnabled();
00648     }
00649 }
00650 
00651 /******************************************************************************
00652 * Reset the alarm daemon and reload the calendar.
00653 * If the daemon is not already running, start it.
00654 */
00655 void resetDaemon()
00656 {
00657     kdDebug(5950) << "KAlarm::resetDaemon()" << endl;
00658     if (!resetDaemonQueued)
00659     {
00660         resetDaemonQueued = true;
00661         theApp()->processQueue();
00662     }
00663 }
00664 
00665 /******************************************************************************
00666 * This method must only be called from the main KAlarm queue processing loop,
00667 * to prevent asynchronous calendar operations interfering with one another.
00668 *
00669 * If resetDaemon() has been called, reset the alarm daemon and reload the calendars.
00670 * If the daemon is not already running, start it.
00671 */
00672 void resetDaemonIfQueued()
00673 {
00674     if (resetDaemonQueued)
00675     {
00676         kdDebug(5950) << "KAlarm::resetDaemonIfNeeded()" << endl;
00677         AlarmCalendar::activeCalendar()->reload();
00678         AlarmCalendar::expiredCalendar()->reload();
00679 
00680         // Close any message windows for alarms which are now disabled
00681         KAEvent event;
00682         KCal::Event::List events = AlarmCalendar::activeCalendar()->events();
00683         for (KCal::Event::List::ConstIterator it = events.begin();  it != events.end();  ++it)
00684         {
00685             KCal::Event* kcalEvent = *it;
00686             event.set(*kcalEvent);
00687             if (!event.enabled()  &&  event.displayAction())
00688             {
00689                 MessageWin* win = MessageWin::findEvent(event.id());
00690                 delete win;
00691             }
00692         }
00693 
00694         MainWindow::refresh();
00695         if (!Daemon::reset())
00696             Daemon::start();
00697         resetDaemonQueued = false;
00698     }
00699 }
00700 
00701 /******************************************************************************
00702 *  Start KMail if it isn't already running, and optionally iconise it.
00703 *  Reply = reason for failure to run KMail (which may be the empty string)
00704 *        = null string if success.
00705 */
00706 QString runKMail(bool minimise)
00707 {
00708     QCString dcopName;
00709     QString errmsg;
00710     if (!runProgram("kmail", (minimise ? KMAIL_DCOP_WINDOW : ""), dcopName, errmsg))
00711         return i18n("Unable to start KMail\n(%1)").arg(errmsg);
00712     return QString::null;
00713 }
00714 
00715 /******************************************************************************
00716 *  Start another program for DCOP access if it isn't already running.
00717 *  If 'windowName' is not empty, the program's window of that name is iconised.
00718 *  On exit, 'dcopName' contains the DCOP name to access the application, and
00719 *  'errorMessage' contains an error message if failure.
00720 *  Reply = true if the program is now running.
00721 */
00722 bool runProgram(const QCString& program, const QCString& windowName, QCString& dcopName, QString& errorMessage)
00723 {
00724     if (!kapp->dcopClient()->isApplicationRegistered(program))
00725     {
00726         // KOrganizer is not already running, so start it
00727         if (KApplication::startServiceByDesktopName(QString::fromLatin1(program), QString::null, &errorMessage, &dcopName))
00728         {
00729             kdError(5950) << "runProgram(): couldn't start " << program << " (" << errorMessage << ")\n";
00730             return false;
00731         }
00732         // Minimise its window - don't use hide() since this would remove all
00733         // trace of it from the panel if it is not configured to be docked in
00734         // the system tray.
00735         kapp->dcopClient()->send(dcopName, windowName, "minimize()", QString::null);
00736     }
00737     else if (dcopName.isEmpty())
00738         dcopName = program;
00739     errorMessage = QString::null;
00740     return true;
00741 }
00742 
00743 /******************************************************************************
00744 *  Read the size for the specified window from the config file, for the
00745 *  current screen resolution.
00746 *  Reply = true if size set in the config file, in which case 'result' is set
00747 *        = false if no size is set, in which case 'result' is unchanged.
00748 */
00749 bool readConfigWindowSize(const char* window, QSize& result)
00750 {
00751     KConfig* config = KGlobal::config();
00752     config->setGroup(QString::fromLatin1(window));
00753     QWidget* desktop = KApplication::desktop();
00754     QSize s = QSize(config->readNumEntry(QString::fromLatin1("Width %1").arg(desktop->width()), 0),
00755                     config->readNumEntry(QString::fromLatin1("Height %1").arg(desktop->height()), 0));
00756     if (s.isEmpty())
00757         return false;
00758     result = s;
00759     return true;
00760 }
00761 
00762 /******************************************************************************
00763 *  Write the size for the specified window to the config file, for the
00764 *  current screen resolution.
00765 */
00766 void writeConfigWindowSize(const char* window, const QSize& size)
00767 {
00768     KConfig* config = KGlobal::config();
00769     config->setGroup(QString::fromLatin1(window));
00770     QWidget* desktop = KApplication::desktop();
00771     config->writeEntry(QString::fromLatin1("Width %1").arg(desktop->width()), size.width());
00772     config->writeEntry(QString::fromLatin1("Height %1").arg(desktop->height()), size.height());
00773     config->sync();
00774 }
00775 
00776 /******************************************************************************
00777 * Return the current KAlarm version number.
00778 */
00779 int Version()
00780 {
00781     static int version = 0;
00782     if (!version)
00783         version = getVersionNumber(KALARM_VERSION);
00784     return version;
00785 }
00786 
00787 /******************************************************************************
00788 * Convert the supplied KAlarm version string to a version number.
00789 * Reply = version number (double digit for each of major, minor & issue number,
00790 *         e.g. 010203 for 1.2.3
00791 *       = 0 if invalid version string.
00792 */
00793 int getVersionNumber(const QString& version, QString* subVersion)
00794 {
00795     // N.B. Remember to change  Version(int major, int minor, int rev)
00796     //      if the representation returned by this method changes.
00797     if (subVersion)
00798         *subVersion = QString::null;
00799     int count = version.contains('.') + 1;
00800     if (count < 2)
00801         return 0;
00802     bool ok;
00803     unsigned vernum = version.section('.', 0, 0).toUInt(&ok) * 10000;  // major version
00804     if (!ok)
00805         return 0;
00806     unsigned v = version.section('.', 1, 1).toUInt(&ok);               // minor version
00807     if (!ok)
00808         return 0;
00809     vernum += (v < 99 ? v : 99) * 100;
00810     if (count >= 3)
00811     {
00812         // Issue number: allow other characters to follow the last digit
00813         QString issue = version.section('.', 2);
00814         if (!issue.at(0).isDigit())
00815             return 0;
00816         int n = issue.length();
00817         int i;
00818         for (i = 0;  i < n && issue.at(i).isDigit();  ++i) ;
00819         if (subVersion)
00820             *subVersion = issue.mid(i);
00821         v = issue.left(i).toUInt();   // issue number
00822         vernum += (v < 99 ? v : 99);
00823     }
00824     return vernum;
00825 }
00826 
00827 /******************************************************************************
00828 * Check from its mime type whether a file appears to be a text or image file.
00829 * If a text file, its type is distinguished.
00830 * Reply = file type.
00831 */
00832 FileType fileType(const QString& mimetype)
00833 {
00834     static const char* applicationTypes[] = {
00835         "x-shellscript", "x-nawk", "x-awk", "x-perl", "x-python",
00836         "x-desktop", "x-troff", 0 };
00837     static const char* formattedTextTypes[] = {
00838         "html", "xml", 0 };
00839 
00840     if (mimetype.startsWith(QString::fromLatin1("image/")))
00841         return Image;
00842     int slash = mimetype.find('/');
00843     if (slash < 0)
00844         return Unknown;
00845     QString type = mimetype.mid(slash + 1);
00846     const char* typel = type.latin1();
00847     if (mimetype.startsWith(QString::fromLatin1("application")))
00848     {
00849         for (int i = 0;  applicationTypes[i];  ++i)
00850             if (!strcmp(typel, applicationTypes[i]))
00851                 return TextApplication;
00852     }
00853     else if (mimetype.startsWith(QString::fromLatin1("text")))
00854     {
00855         for (int i = 0;  formattedTextTypes[i];  ++i)
00856             if (!strcmp(typel, formattedTextTypes[i]))
00857                 return TextFormatted;
00858         return TextPlain;
00859     }
00860     return Unknown;
00861 }
00862 
00863 /******************************************************************************
00864 * Display a modal dialogue to choose an existing file, initially highlighting
00865 * any specified file.
00866 * @param initialFile The file to initially highlight - must be a full path name or URL.
00867 * @param defaultDir The directory to start in if @p initialFile is empty. If empty,
00868 *                   the user's home directory will be used. Updated to the
00869 *                   directory containing the selected file, if a file is chosen.
00870 * @param mode OR of KFile::Mode values, e.g. ExistingOnly, LocalOnly.
00871 * Reply = URL selected. If none is selected, URL.isEmpty() is true.
00872 */
00873 QString browseFile(const QString& caption, QString& defaultDir, const QString& initialFile,
00874                    const QString& filter, int mode, QWidget* parent, const char* name)
00875 {
00876     QString initialDir = !initialFile.isEmpty() ? QString(initialFile).remove(QRegExp("/[^/]*$"))
00877                        : !defaultDir.isEmpty()  ? defaultDir
00878                        :                          QDir::homeDirPath();
00879     KFileDialog fileDlg(initialDir, filter, parent, name, true);
00880     fileDlg.setOperationMode(mode & KFile::ExistingOnly ? KFileDialog::Opening : KFileDialog::Saving);
00881     fileDlg.setMode(KFile::File | mode);
00882     fileDlg.setCaption(caption);
00883     if (!initialFile.isEmpty())
00884         fileDlg.setSelection(initialFile);
00885     if (fileDlg.exec() != QDialog::Accepted)
00886         return QString::null;
00887     KURL url = fileDlg.selectedURL();
00888     defaultDir = url.path();
00889     return (mode & KFile::LocalOnly) ? url.path() : url.prettyURL();
00890 }
00891 
00892 /******************************************************************************
00893 *  Return the first day of the week for the user's locale.
00894 *  Reply = 1 (Mon) .. 7 (Sun).
00895 */
00896 int localeFirstDayOfWeek()
00897 {
00898     static int firstDay = 0;
00899     if (!firstDay)
00900         firstDay = KGlobal::locale()->weekStartDay();
00901     return firstDay;
00902 }
00903 
00904 /******************************************************************************
00905 *  Return the supplied string with any accelerator code stripped out.
00906 */
00907 QString stripAccel(const QString& text)
00908 {
00909     unsigned len = text.length();
00910     QString out = QDeepCopy<QString>(text);
00911     QChar *corig = (QChar*)out.unicode();
00912     QChar *cout  = corig;
00913     QChar *cin   = cout;
00914     while (len)
00915     {
00916         if ( *cin == '&' )
00917         {
00918             ++cin;
00919             --len;
00920             if ( !len )
00921                 break;
00922         }
00923         *cout = *cin;
00924         ++cout;
00925         ++cin;
00926         --len;
00927     }
00928     unsigned newlen = cout - corig;
00929     if (newlen != out.length())
00930         out.truncate(newlen);
00931     return out;
00932 }
00933 
00934 /******************************************************************************
00935 * Cancel the screen saver, in case it is active.
00936 * Only implemented if the X11 XTest extension is installed.
00937 */
00938 void cancelScreenSaver()
00939 {
00940 #ifdef HAVE_XTEST
00941     x11_cancelScreenSaver();
00942 #endif // HAVE_XTEST
00943 }
00944 
00945 } // namespace KAlarm
00946 
00947 
00948 namespace {
00949 
00950 /******************************************************************************
00951 *  Tell KOrganizer to put an alarm in its calendar.
00952 *  It will be held by KOrganizer as a simple event, without alarms - KAlarm
00953 *  is still responsible for alarming.
00954 */
00955 bool sendToKOrganizer(const KAEvent& event)
00956 {
00957     KCal::Event* kcalEvent = event.event();
00958     QString uid = KAEvent::uid(event.id(), KAEvent::KORGANIZER);
00959     kcalEvent->setUid(uid);
00960     kcalEvent->clearAlarms();
00961     QString userEmail;
00962     switch (event.action())
00963     {
00964         case KAEvent::MESSAGE:
00965         case KAEvent::FILE:
00966         case KAEvent::COMMAND:
00967             kcalEvent->setSummary(event.cleanText());
00968             userEmail = Preferences::emailAddress();
00969             break;
00970         case KAEvent::EMAIL:
00971         {
00972             QString from = event.emailFromId()
00973                          ? KAMail::identityManager()->identityForUoid(event.emailFromId()).fullEmailAddr()
00974                          : Preferences::emailAddress();
00975             AlarmText atext;
00976             atext.setEmail(event.emailAddresses(", "), from, QString::null, QString::null, event.emailSubject(), QString::null);
00977             kcalEvent->setSummary(atext.displayText());
00978             userEmail = from;
00979             break;
00980         }
00981     }
00982     kcalEvent->setOrganizer(KCal::Person(QString::null, userEmail));
00983 
00984     // Translate the event into string format
00985     KCal::ICalFormat format;
00986     format.setTimeZone(QString::null, false);
00987     QString iCal = format.toICalString(kcalEvent);
00988 kdDebug(5950)<<"Korg->"<<iCal<<endl;
00989     delete kcalEvent;
00990 
00991     // Send the event to KOrganizer
00992     if (!runKOrganizer())     // start KOrganizer if it isn't already running
00993         return false;
00994     QByteArray  data, replyData;
00995     QCString    replyType;
00996     QDataStream arg(data, IO_WriteOnly);
00997     arg << iCal;
00998     if (kapp->dcopClient()->call(korganizerName, KORG_DCOP_OBJECT, "addIncidence(QString)", data, replyType, replyData)
00999     &&  replyType == "bool")
01000     {
01001         bool result;
01002         QDataStream reply(replyData, IO_ReadOnly);
01003         reply >> result;
01004         if (result)
01005         {
01006             kdDebug(5950) << "sendToKOrganizer(" << uid << "): success\n";
01007             return true;
01008         }
01009     }
01010     kdError(5950) << "sendToKOrganizer(): KOrganizer addEvent(" << uid << ") dcop call failed\n";
01011     return false;
01012 }
01013 
01014 /******************************************************************************
01015 *  Tell KOrganizer to delete an event from its calendar.
01016 */
01017 bool deleteFromKOrganizer(const QString& eventID)
01018 {
01019     if (!runKOrganizer())     // start KOrganizer if it isn't already running
01020         return false;
01021     QString newID = KAEvent::uid(eventID, KAEvent::KORGANIZER);
01022     QByteArray  data, replyData;
01023     QCString    replyType;
01024     QDataStream arg(data, IO_WriteOnly);
01025     arg << newID << true;
01026     if (kapp->dcopClient()->call(korganizerName, KORG_DCOP_OBJECT, "deleteIncidence(QString,bool)", data, replyType, replyData)
01027     &&  replyType == "bool")
01028     {
01029         bool result;
01030         QDataStream reply(replyData, IO_ReadOnly);
01031         reply >> result;
01032         if (result)
01033         {
01034             kdDebug(5950) << "deleteFromKOrganizer(" << newID << "): success\n";
01035             return true;
01036         }
01037     }
01038     kdError(5950) << "sendToKOrganizer(): KOrganizer deleteEvent(" << newID << ") dcop call failed\n";
01039     return false;
01040 }
01041 
01042 /******************************************************************************
01043 * Start KOrganizer if not already running, and create its DCOP interface.
01044 */
01045 bool runKOrganizer()
01046 {
01047     QString error;
01048     QCString dcopService;
01049     int result = KDCOPServiceStarter::self()->findServiceFor("DCOP/Organizer", QString::null, QString::null, &error, &dcopService);
01050     if (result)
01051     {
01052         kdDebug(5950) << "Unable to start DCOP/Organizer: " << dcopService << " " << error << endl;
01053         return false;
01054     }
01055     // If Kontact is running, there is be a load() method which needs to be called
01056     // to load KOrganizer into Kontact. But if KOrganizer is running independently,
01057     // the load() method doesn't exist.
01058     QCString dummy;
01059     if (!kapp->dcopClient()->findObject(dcopService, KORG_DCOP_OBJECT, "", QByteArray(), dummy, dummy))
01060     {
01061         DCOPRef ref(dcopService, dcopService); // talk to the KUniqueApplication or its Kontact wrapper
01062         DCOPReply reply = ref.call("load()");
01063         if (!reply.isValid() || !(bool)reply)
01064         {
01065             kdWarning(5950) << "Error loading " << dcopService << endl;
01066             return false;
01067         }
01068         if (!kapp->dcopClient()->findObject(dcopService, KORG_DCOP_OBJECT, "", QByteArray(), dummy, dummy))
01069         {
01070             kdWarning(5950) << "Unable to access KOrganizer's "KORG_DCOP_OBJECT" DCOP object" << endl;
01071             return false;
01072         }
01073     }
01074     return true;
01075 }
01076 
01077 } // namespace
01078 
01079 #ifdef HAVE_XTEST
01080 #include <X11/keysym.h>
01081 #include <X11/extensions/XTest.h>
01082 #include <qwindowdefs.h>
01083 
01084 /******************************************************************************
01085 * Cancel the screen saver, in case it is active.
01086 * Only implemented if the X11 XTest extension is installed.
01087 */
01088 void x11_cancelScreenSaver()
01089 {
01090     kdDebug(5950) << "KAlarm::cancelScreenSaver()" << endl;
01091     Display* display = qt_xdisplay();
01092     static int XTestKeyCode = 0;
01093     if (!XTestKeyCode)
01094         XTestKeyCode = XKeysymToKeycode(display, XK_Shift_L);
01095     XTestFakeKeyEvent(display, XTestKeyCode, true, CurrentTime);
01096     XTestFakeKeyEvent(display, XTestKeyCode, false, CurrentTime);
01097     XSync(display, false);
01098 }
01099 #endif // HAVE_XTEST
KDE Home | KDE Accessibility Home | Description of Access Keys