kalarm

functions.cpp

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