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