kalarm

functions.cpp

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