kalarm

functions.cpp
1 /*
2  * functions.cpp - miscellaneous functions
3  * Program: kalarm
4  * Copyright © 2001-2009 by David Jarvie <djarvie@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "kalarm.h"
22 #include "functions.h"
23 
24 #include "alarmcalendar.h"
25 #include "alarmevent.h"
26 #include "alarmlistview.h"
27 #include "daemon.h"
28 #include "kalarmapp.h"
29 #include "kamail.h"
30 #include "mainwindow.h"
31 #include "messagewin.h"
32 #include "preferences.h"
33 #include "shellprocess.h"
34 #include "templatelistview.h"
35 #include "templatemenuaction.h"
36 
37 #include <tqdeepcopy.h>
38 #include <tqdir.h>
39 #include <tqregexp.h>
40 
41 #include <tdeconfig.h>
42 #include <tdeaction.h>
43 #include <tdeglobal.h>
44 #include <tdelocale.h>
45 #include <kstdguiitem.h>
46 #include <tdestdaccel.h>
47 #include <tdemessagebox.h>
48 #include <tdefiledialog.h>
49 #include <dcopclient.h>
50 #include <dcopref.h>
51 #include <kdcopservicestarter.h>
52 #include <kdebug.h>
53 
54 #include <libkcal/event.h>
55 #include <libkcal/icalformat.h>
56 #include <libkpimidentities/identitymanager.h>
57 #include <libkpimidentities/identity.h>
58 #include <libkcal/person.h>
59 
60 
61 namespace
62 {
63 bool resetDaemonQueued = false;
64 TQCString korganizerName = "korganizer";
65 TQString korgStartError;
66 #define KORG_DCOP_OBJECT "KOrganizerIface"
67 //const char* KORG_DCOP_WINDOW = "KOrganizer MainWindow"; // not used
68 const char* KMAIL_DCOP_WINDOW = "kmail-mainwindow#1";
69 
70 bool sendToKOrganizer(const KAEvent&);
71 bool deleteFromKOrganizer(const TQString& eventID);
72 bool runKOrganizer();
73 }
74 
75 
76 namespace KAlarm
77 {
78 
79 /******************************************************************************
80 * Display a main window with the specified event selected.
81 */
82 MainWindow* displayMainWindowSelected(const TQString& eventID)
83 {
84  MainWindow* win = MainWindow::firstWindow();
85  if (!win)
86  {
87  if (theApp()->checkCalendarDaemon()) // ensure calendar is open and daemon started
88  {
89  win = MainWindow::create();
90  win->show();
91  }
92  }
93  else
94  {
95  // There is already a main window, so make it the active window
96  bool visible = win->isVisible();
97  if (visible)
98  win->hide(); // in case it's on a different desktop
99  if (!visible || win->isMinimized())
100  win->showNormal();
101  win->raise();
102  win->setActiveWindow();
103  }
104  if (win && !eventID.isEmpty())
105  win->selectEvent(eventID);
106  return win;
107 }
108 
109 /******************************************************************************
110 * Create a New Alarm TDEAction.
111 */
112 TDEAction* createNewAlarmAction(const TQString& label, TQObject* receiver, const char* slot, TDEActionCollection* actions, const char* name)
113 {
114  return new TDEAction(label, "document-new", TDEStdAccel::openNew(), receiver, slot, actions, name);
115 }
116 
117 /******************************************************************************
118 * Create a New From Template TDEAction.
119 */
120 TemplateMenuAction* createNewFromTemplateAction(const TQString& label, TQObject* receiver, const char* slot, TDEActionCollection* actions, const char* name)
121 {
122  return new TemplateMenuAction(label, "new_from_template", receiver, slot, actions, name);
123 }
124 
125 /******************************************************************************
126 * Add a new active (non-expired) alarm.
127 * Save it in the calendar file and add it to every main window instance.
128 * If 'selectionView' is non-null, the selection highlight is moved to the new
129 * event in that listView instance.
130 * 'event' is updated with the actual event ID.
131 */
132 UpdateStatus addEvent(KAEvent& event, AlarmListView* selectionView, TQWidget* errmsgParent, bool useEventID, bool allowKOrgUpdate)
133 {
134  kdDebug(5950) << "KAlarm::addEvent(): " << event.id() << endl;
135  UpdateStatus status = UPDATE_OK;
136  if (!theApp()->checkCalendarDaemon()) // ensure calendar is open and daemon started
137  return UPDATE_FAILED;
138  else
139  {
140  // Save the event details in the calendar file, and get the new event ID
141  AlarmCalendar* cal = AlarmCalendar::activeCalendar();
142  if (!cal->addEvent(event, useEventID))
143  status = UPDATE_FAILED;
144  else if (!cal->save())
145  status = SAVE_FAILED;
146  }
147  if (status == UPDATE_OK)
148  {
149  if (allowKOrgUpdate && event.copyToKOrganizer())
150  {
151  if (!sendToKOrganizer(event)) // tell KOrganizer to show the event
152  status = UPDATE_KORG_ERR;
153  }
154 
155  // Update the window lists
156  AlarmListView::addEvent(event, selectionView);
157  return status;
158  }
159 
160  if (errmsgParent)
161  displayUpdateError(errmsgParent, status, ERR_ADD, 1);
162  return status;
163 }
164 
165 /******************************************************************************
166 * Save the event in the expired calendar file and adjust every main window instance.
167 * The event's ID is changed to an expired ID if necessary.
168 */
169 bool addExpiredEvent(KAEvent& event)
170 {
171  kdDebug(5950) << "KAlarm::addExpiredEvent(" << event.id() << ")\n";
172  AlarmCalendar* cal = AlarmCalendar::expiredCalendarOpen();
173  if (!cal)
174  return false;
175  bool archiving = (KAEvent::uidStatus(event.id()) == KAEvent::ACTIVE);
176  if (archiving)
177  event.setSaveDateTime(TQDateTime::currentDateTime()); // time stamp to control purging
178  KCal::Event* kcalEvent = cal->addEvent(event);
179  cal->save();
180 
181  // Update window lists
182  if (!archiving)
183  AlarmListView::addEvent(event, 0);
184  else if (kcalEvent)
185  AlarmListView::modifyEvent(KAEvent(*kcalEvent), 0);
186  return true;
187 }
188 
189 /******************************************************************************
190 * Add a new template.
191 * Save it in the calendar file and add it to every template list view.
192 * If 'selectionView' is non-null, the selection highlight is moved to the new
193 * event in that listView instance.
194 * 'event' is updated with the actual event ID.
195 */
196 UpdateStatus addTemplate(KAEvent& event, TemplateListView* selectionView, TQWidget* errmsgParent)
197 {
198  kdDebug(5950) << "KAlarm::addTemplate(): " << event.id() << endl;
199  UpdateStatus status = UPDATE_OK;
200 
201  // Add the template to the calendar file
202  AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
203  if (!cal || !cal->addEvent(event))
204  status = UPDATE_FAILED;
205  else if (!cal->save())
206  status = SAVE_FAILED;
207  else
208  {
209  cal->emitEmptyStatus();
210 
211  // Update the window lists
212  TemplateListView::addEvent(event, selectionView);
213  return UPDATE_OK;
214  }
215 
216  if (errmsgParent)
217  displayUpdateError(errmsgParent, status, ERR_TEMPLATE, 1);
218  return status;
219 }
220 
221 /******************************************************************************
222 * Modify an active (non-expired) alarm in the calendar file and in every main
223 * window instance.
224 * The new event will have a different event ID from the old one.
225 * If 'selectionView' is non-null, the selection highlight is moved to the
226 * modified event in that listView instance.
227 */
228 UpdateStatus modifyEvent(KAEvent& oldEvent, const KAEvent& newEvent, AlarmListView* selectionView, TQWidget* errmsgParent)
229 {
230  kdDebug(5950) << "KAlarm::modifyEvent(): '" << oldEvent.id() << endl;
231 
232  UpdateStatus status = UPDATE_OK;
233  if (!newEvent.valid())
234  {
235  deleteEvent(oldEvent, true);
236  status = UPDATE_FAILED;
237  }
238  else
239  {
240  if (oldEvent.copyToKOrganizer())
241  {
242  // Tell KOrganizer to delete its old event.
243  // But ignore errors, because the user could have manually
244  // deleted it since KAlarm asked KOrganizer to set it up.
245  deleteFromKOrganizer(oldEvent.id());
246  }
247 
248  // Update the event in the calendar file, and get the new event ID
249  AlarmCalendar* cal = AlarmCalendar::activeCalendar();
250  if (!cal->deleteEvent(oldEvent.id())
251  || !cal->addEvent(const_cast<KAEvent&>(newEvent), true))
252  status = UPDATE_FAILED;
253  else if (!cal->save())
254  status = SAVE_FAILED;
255  if (status == UPDATE_OK)
256  {
257  if (newEvent.copyToKOrganizer())
258  {
259  if (!sendToKOrganizer(newEvent)) // tell KOrganizer to show the new event
260  status = UPDATE_KORG_ERR;
261  }
262 
263  // Update the window lists
264  AlarmListView::modifyEvent(oldEvent.id(), newEvent, selectionView);
265  return status;
266  }
267  }
268 
269  if (errmsgParent)
270  displayUpdateError(errmsgParent, status, ERR_ADD, 1);
271  return status;
272 }
273 
274 /******************************************************************************
275 * Update an active (non-expired) alarm from the calendar file and from every
276 * main window instance.
277 * The new event will have the same event ID as the old one.
278 * If 'selectionView' is non-null, the selection highlight is moved to the
279 * updated event in that listView instance.
280 * The event is not updated in KOrganizer, since this function is called when an
281 * existing alarm is rescheduled (due to recurrence or deferral).
282 */
283 UpdateStatus updateEvent(KAEvent& event, AlarmListView* selectionView, TQWidget* errmsgParent, bool archiveOnDelete, bool incRevision)
284 {
285  kdDebug(5950) << "KAlarm::updateEvent(): " << event.id() << endl;
286 
287  if (!event.valid())
288  deleteEvent(event, archiveOnDelete);
289  else
290  {
291  // Update the event in the calendar file.
292  if (incRevision)
293  event.incrementRevision(); // ensure alarm daemon sees the event has changed
294  AlarmCalendar* cal = AlarmCalendar::activeCalendar();
295  cal->updateEvent(event);
296  if (!cal->save())
297  {
298  if (errmsgParent)
299  displayUpdateError(errmsgParent, SAVE_FAILED, ERR_ADD, 1);
300  return SAVE_FAILED;
301  }
302 
303  // Update the window lists
304  AlarmListView::modifyEvent(event, selectionView);
305  }
306  return UPDATE_OK;
307 }
308 
309 /******************************************************************************
310 * Update a template in the calendar file and in every template list view.
311 * If 'selectionView' is non-null, the selection highlight is moved to the
312 * updated event in that listView instance.
313 */
314 UpdateStatus updateTemplate(const KAEvent& event, TemplateListView* selectionView, TQWidget* errmsgParent)
315 {
316  UpdateStatus status = UPDATE_OK;
317  AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
318  if (!cal)
319  status = UPDATE_FAILED;
320  else
321  {
322  cal->updateEvent(event);
323  if (!cal->save())
324  status = SAVE_FAILED;
325  else
326  {
327  TemplateListView::modifyEvent(event.id(), event, selectionView);
328  return UPDATE_OK;
329  }
330  }
331 
332  if (errmsgParent)
333  displayUpdateError(errmsgParent, SAVE_FAILED, ERR_TEMPLATE, 1);
334  return status;
335 }
336 
337 /******************************************************************************
338 * Delete an alarm from the calendar file and from every main window instance.
339 * If the event is archived, the event's ID is changed to an expired ID if necessary.
340 */
341 UpdateStatus deleteEvent(KAEvent& event, bool archive, TQWidget* errmsgParent)
342 {
343  TQString id = event.id();
344  kdDebug(5950) << "KAlarm::deleteEvent(): " << id << endl;
345 
346  // Update the window lists
347  AlarmListView::deleteEvent(id);
348 
349  UpdateStatus status = UPDATE_OK;
350  AlarmCalendar* cal;
351 
352  // Delete the event from the calendar file
353  if (KAEvent::uidStatus(id) == KAEvent::EXPIRED)
354  {
355  cal = AlarmCalendar::expiredCalendarOpen();
356  if (!cal)
357  status = UPDATE_FAILED;
358  }
359  else
360  {
361  if (event.copyToKOrganizer())
362  {
363  // The event was shown in KOrganizer, so tell KOrganizer to
364  // delete it. Note that an error could occur if the user
365  // manually deleted it from KOrganizer since it was set up.
366  if (!deleteFromKOrganizer(event.id()))
367  status = UPDATE_KORG_ERR;
368  }
369  if (archive && event.toBeArchived())
370  addExpiredEvent(event); // this changes the event ID to an expired ID
371  cal = AlarmCalendar::activeCalendar();
372  }
373  if (status != UPDATE_FAILED)
374  {
375  if (!cal->deleteEvent(id, true)) // save calendar after deleting
376  status = SAVE_FAILED;
377  }
378  if (status > UPDATE_KORG_ERR && errmsgParent)
379  displayUpdateError(errmsgParent, SAVE_FAILED, ERR_DELETE, 1);
380  return status;
381 }
382 
383 /******************************************************************************
384 * Delete a template from the calendar file and from every template list view.
385 */
386 UpdateStatus deleteTemplate(const KAEvent& event)
387 {
388  TQString id = event.id();
389 
390  // Delete the template from the calendar file
391  AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
392  if (!cal)
393  return UPDATE_FAILED;
394  if (!cal->deleteEvent(id, true)) // save calendar after deleting
395  return SAVE_FAILED;
396  cal->emitEmptyStatus();
397 
398  // Update the window lists
399  TemplateListView::deleteEvent(id);
400  return UPDATE_OK;
401 }
402 
403 /******************************************************************************
404 * Delete an alarm from the display calendar.
405 */
406 void deleteDisplayEvent(const TQString& eventID)
407 {
408  kdDebug(5950) << "KAlarm::deleteDisplayEvent(" << eventID << ")\n";
409 
410  if (KAEvent::uidStatus(eventID) == KAEvent::DISPLAYING)
411  {
412  AlarmCalendar* cal = AlarmCalendar::displayCalendarOpen();
413  if (cal)
414  cal->deleteEvent(eventID, true); // save calendar after deleting
415  }
416 }
417 
418 /******************************************************************************
419 * Undelete an expired alarm, and update every main window instance.
420 * The archive bit is set to ensure that it gets re-archived if it is deleted again.
421 * If 'selectionView' is non-null, the selection highlight is moved to the
422 * restored event in that listView instance.
423 */
424 UpdateStatus reactivateEvent(KAEvent& event, AlarmListView* selectionView, bool useEventID)
425 {
426  TQString id = event.id();
427  kdDebug(5950) << "KAlarm::reactivateEvent(): " << id << endl;
428 
429  // Delete the event from the expired calendar file
430  if (KAEvent::uidStatus(id) == KAEvent::EXPIRED)
431  {
432  TQDateTime now = TQDateTime::currentDateTime();
433  if (event.occursAfter(now, true))
434  {
435  if (event.recurs() || event.repeatCount())
436  event.setNextOccurrence(now); // skip any recurrences in the past
437  event.setArchive(); // ensure that it gets re-archived if it is deleted
438 
439  // Save the event details in the calendar file, and get the new event ID
440  AlarmCalendar* cal = AlarmCalendar::activeCalendar();
441  if (!cal->addEvent(event, useEventID))
442  return UPDATE_FAILED;
443  if (!cal->save())
444  return SAVE_FAILED;
445 
446  UpdateStatus status = UPDATE_OK;
447  if (event.copyToKOrganizer())
448  {
449  if (!sendToKOrganizer(event)) // tell KOrganizer to show the event
450  status = UPDATE_KORG_ERR;
451  }
452 
453  // Update the window lists
454  AlarmListView::undeleteEvent(id, event, selectionView);
455 
456  cal = AlarmCalendar::expiredCalendarOpen();
457  if (cal)
458  cal->deleteEvent(id, true); // save calendar after deleting
459  return status;
460  }
461  }
462  return UPDATE_FAILED;
463 }
464 
465 /******************************************************************************
466 * Enable or disable an alarm in the calendar file and in every main window instance.
467 * The new event will have the same event ID as the old one.
468 * If 'selectionView' is non-null, the selection highlight is moved to the
469 * updated event in that listView instance.
470 */
471 UpdateStatus enableEvent(KAEvent& event, AlarmListView* selectionView, bool enable)
472 {
473  kdDebug(5950) << "KAlarm::enableEvent(" << enable << "): " << event.id() << endl;
474 
475  if (enable != event.enabled())
476  {
477  event.setEnabled(enable);
478 
479  // Update the event in the calendar file
480  AlarmCalendar* cal = AlarmCalendar::activeCalendar();
481  cal->updateEvent(event);
482  if (!cal->save())
483  return SAVE_FAILED;
484 
485  // If we're disabling a display alarm, close any message window
486  if (!enable && event.displayAction())
487  {
488  MessageWin* win = MessageWin::findEvent(event.id());
489  delete win;
490  }
491 
492  // Update the window lists
493  AlarmListView::modifyEvent(event, selectionView);
494  }
495  return UPDATE_OK;
496 }
497 
498 /******************************************************************************
499 * Display an error message about an error saving an event.
500 */
501 void displayUpdateError(TQWidget* parent, UpdateStatus, UpdateError code, int nAlarms)
502 {
503  TQString errmsg;
504  switch (code)
505  {
506  case ERR_ADD:
507  errmsg = (nAlarms > 1) ? i18n("Error saving alarms")
508  : i18n("Error saving alarm");
509  break;
510  case ERR_DELETE:
511  errmsg = (nAlarms > 1) ? i18n("Error deleting alarms")
512  : i18n("Error deleting alarm");
513  break;
514  case ERR_REACTIVATE:
515  errmsg = (nAlarms > 1) ? i18n("Error saving reactivated alarms")
516  : i18n("Error saving reactivated alarm");
517  break;
518  case ERR_TEMPLATE:
519  errmsg = i18n("Error saving alarm template");
520  break;
521  }
522  KMessageBox::error(parent, errmsg);
523 }
524 
525 /******************************************************************************
526 * Display an error message corresponding to a specified alarm update error code.
527 */
528 void displayKOrgUpdateError(TQWidget* parent, KOrgUpdateError code, int nAlarms)
529 {
530  TQString errmsg;
531  switch (code)
532  {
533  case KORG_ERR_ADD:
534  errmsg = (nAlarms > 1) ? i18n("Unable to show alarms in KOrganizer")
535  : i18n("Unable to show alarm in KOrganizer");
536  break;
537  case KORG_ERR_MODIFY:
538  errmsg = i18n("Unable to update alarm in KOrganizer");
539  break;
540  case KORG_ERR_DELETE:
541  errmsg = (nAlarms > 1) ? i18n("Unable to delete alarms from KOrganizer")
542  : i18n("Unable to delete alarm from KOrganizer");
543  break;
544  }
545  KMessageBox::error(parent, errmsg);
546 }
547 
548 /******************************************************************************
549 * Display the alarm edit dialogue to edit a specified alarm.
550 */
551 bool edit(const TQString& eventID)
552 {
553  AlarmCalendar* cal;
554  switch (KAEvent::uidStatus(eventID))
555  {
556  case KAEvent::ACTIVE:
557  cal = AlarmCalendar::activeCalendar();
558  break;
559  case KAEvent::TEMPLATE:
560  cal = AlarmCalendar::templateCalendarOpen();
561  break;
562  default:
563  kdError(5950) << "KAlarm::edit(" << eventID << "): event not active or template" << endl;
564  return false;
565  }
566  KCal::Event* kcalEvent = cal->event(eventID);
567  if (!kcalEvent)
568  {
569  kdError(5950) << "KAlarm::edit(): event ID not found: " << eventID << endl;
570  return false;
571  }
572  KAEvent event(*kcalEvent);
573  MainWindow::executeEdit(event);
574  return true;
575 }
576 
577 /******************************************************************************
578 * Display the alarm edit dialogue to edit a new alarm, optionally preset with
579 * a template.
580 */
581 bool editNew(const TQString& templateName)
582 {
583  bool result = true;
584  if (!templateName.isEmpty())
585  {
586  AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
587  if (cal)
588  {
589  KAEvent templateEvent = KAEvent::findTemplateName(*cal, templateName);
590  if (templateEvent.valid())
591  {
592  MainWindow::executeNew(templateEvent);
593  return true;
594  }
595  kdWarning(5950) << "KAlarm::editNew(" << templateName << "): template not found" << endl;
596  }
597  result = false;
598  }
599  MainWindow::executeNew();
600  return result;
601 }
602 
603 /******************************************************************************
604 * Returns a list of all alarm templates.
605 * If shell commands are disabled, command alarm templates are omitted.
606 */
607 TQValueList<KAEvent> templateList()
608 {
609  TQValueList<KAEvent> templates;
610  AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
611  if (cal)
612  {
613  bool includeCmdAlarms = ShellProcess::authorised();
614  KCal::Event::List events = cal->events();
615  for (KCal::Event::List::ConstIterator it = events.begin(); it != events.end(); ++it)
616  {
617  KCal::Event* kcalEvent = *it;
618  KAEvent event(*kcalEvent);
619  if (includeCmdAlarms || event.action() != KAEvent::COMMAND)
620  templates.append(event);
621  }
622  }
623  return templates;
624 }
625 
626 /******************************************************************************
627 * To be called after an alarm has been edited.
628 * Prompt the user to re-enable alarms if they are currently disabled, and if
629 * it's an email alarm, warn if no 'From' email address is configured.
630 */
631 void outputAlarmWarnings(TQWidget* parent, const KAEvent* event)
632 {
633  if (event && event->action() == KAEvent::EMAIL
634  && Preferences::emailAddress().isEmpty())
635  KMessageBox::information(parent, i18n("Please set the 'From' email address...",
636  "%1\nPlease set it in the Preferences dialog.").arg(KAMail::i18n_NeedFromEmailAddress()));
637 
638  if (!Daemon::monitoringAlarms())
639  {
640  if (KMessageBox::warningYesNo(parent, i18n("Alarms are currently disabled.\nDo you want to enable alarms now?"),
641  TQString(), i18n("Enable"), i18n("Keep Disabled"),
642  TQString::fromLatin1("EditEnableAlarms"))
643  == KMessageBox::Yes)
644  Daemon::setAlarmsEnabled();
645  }
646 }
647 
648 /******************************************************************************
649 * Reset the alarm daemon and reload the calendar.
650 * If the daemon is not already running, start it.
651 */
652 void resetDaemon()
653 {
654  kdDebug(5950) << "KAlarm::resetDaemon()" << endl;
655  if (!resetDaemonQueued)
656  {
657  resetDaemonQueued = true;
658  theApp()->processQueue();
659  }
660 }
661 
662 /******************************************************************************
663 * This method must only be called from the main KAlarm queue processing loop,
664 * to prevent asynchronous calendar operations interfering with one another.
665 *
666 * If resetDaemon() has been called, reset the alarm daemon and reload the calendars.
667 * If the daemon is not already running, start it.
668 */
669 void resetDaemonIfQueued()
670 {
671  if (resetDaemonQueued)
672  {
673  kdDebug(5950) << "KAlarm::resetDaemonIfNeeded()" << endl;
674  AlarmCalendar::activeCalendar()->reload();
675  AlarmCalendar::expiredCalendar()->reload();
676 
677  // Close any message windows for alarms which are now disabled
678  KAEvent event;
679  KCal::Event::List events = AlarmCalendar::activeCalendar()->events();
680  for (KCal::Event::List::ConstIterator it = events.begin(); it != events.end(); ++it)
681  {
682  KCal::Event* kcalEvent = *it;
683  event.set(*kcalEvent);
684  if (!event.enabled() && event.displayAction())
685  {
686  MessageWin* win = MessageWin::findEvent(event.id());
687  delete win;
688  }
689  }
690 
691  MainWindow::refresh();
692  if (!Daemon::reset())
693  Daemon::start();
694  resetDaemonQueued = false;
695  }
696 }
697 
698 /******************************************************************************
699 * Start KMail if it isn't already running, and optionally iconise it.
700 * Reply = reason for failure to run KMail (which may be the empty string)
701 * = null string if success.
702 */
703 TQString runKMail(bool minimise)
704 {
705  TQCString dcopName;
706  TQString errmsg;
707  if (!runProgram("kmail", (minimise ? KMAIL_DCOP_WINDOW : ""), dcopName, errmsg))
708  return i18n("Unable to start KMail\n(%1)").arg(errmsg);
709  return TQString();
710 }
711 
712 /******************************************************************************
713 * Start another program for DCOP access if it isn't already running.
714 * If 'windowName' is not empty, the program's window of that name is iconised.
715 * On exit, 'dcopName' contains the DCOP name to access the application, and
716 * 'errorMessage' contains an error message if failure.
717 * Reply = true if the program is now running.
718 */
719 bool runProgram(const TQCString& program, const TQCString& windowName, TQCString& dcopName, TQString& errorMessage)
720 {
721  if (!kapp->dcopClient()->isApplicationRegistered(program))
722  {
723  // KOrganizer is not already running, so start it
724  if (TDEApplication::startServiceByDesktopName(TQString::fromLatin1(program), TQString(), &errorMessage, &dcopName))
725  {
726  kdError(5950) << "runProgram(): couldn't start " << program << " (" << errorMessage << ")\n";
727  return false;
728  }
729  // Minimise its window - don't use hide() since this would remove all
730  // trace of it from the panel if it is not configured to be docked in
731  // the system tray.
732  kapp->dcopClient()->send(dcopName, windowName, "minimize()", TQString());
733  }
734  else if (dcopName.isEmpty())
735  dcopName = program;
736  errorMessage = TQString();
737  return true;
738 }
739 
740 /******************************************************************************
741 * Read the size for the specified window from the config file, for the
742 * current screen resolution.
743 * Reply = true if size set in the config file, in which case 'result' is set
744 * = false if no size is set, in which case 'result' is unchanged.
745 */
746 bool readConfigWindowSize(const char* window, TQSize& result)
747 {
748  TDEConfig* config = TDEGlobal::config();
749  config->setGroup(TQString::fromLatin1(window));
750  TQWidget* desktop = TDEApplication::desktop();
751  TQSize s = TQSize(config->readNumEntry(TQString::fromLatin1("Width %1").arg(desktop->width()), 0),
752  config->readNumEntry(TQString::fromLatin1("Height %1").arg(desktop->height()), 0));
753  if (s.isEmpty())
754  return false;
755  result = s;
756  return true;
757 }
758 
759 /******************************************************************************
760 * Write the size for the specified window to the config file, for the
761 * current screen resolution.
762 */
763 void writeConfigWindowSize(const char* window, const TQSize& size)
764 {
765  TDEConfig* config = TDEGlobal::config();
766  config->setGroup(TQString::fromLatin1(window));
767  TQWidget* desktop = TDEApplication::desktop();
768  config->writeEntry(TQString::fromLatin1("Width %1").arg(desktop->width()), size.width());
769  config->writeEntry(TQString::fromLatin1("Height %1").arg(desktop->height()), size.height());
770  config->sync();
771 }
772 
773 /******************************************************************************
774 * Return the current KAlarm version number.
775 */
776 int Version()
777 {
778  static int version = 0;
779  if (!version)
780  version = getVersionNumber(KALARM_VERSION);
781  return version;
782 }
783 
784 /******************************************************************************
785 * Convert the supplied KAlarm version string to a version number.
786 * Reply = version number (double digit for each of major, minor & issue number,
787 * e.g. 010203 for 1.2.3
788 * = 0 if invalid version string.
789 */
790 int getVersionNumber(const TQString& version, TQString* subVersion)
791 {
792  // N.B. Remember to change Version(int major, int minor, int rev)
793  // if the representation returned by this method changes.
794  if (subVersion)
795  *subVersion = TQString();
796  int count = version.contains('.') + 1;
797  if (count < 2)
798  return 0;
799  bool ok;
800  unsigned vernum = version.section('.', 0, 0).toUInt(&ok) * 10000; // major version
801  if (!ok)
802  return 0;
803  unsigned v = version.section('.', 1, 1).toUInt(&ok); // minor version
804  if (!ok)
805  return 0;
806  vernum += (v < 99 ? v : 99) * 100;
807  if (count >= 3)
808  {
809  // Issue number: allow other characters to follow the last digit
810  TQString issue = version.section('.', 2);
811  if (!issue.at(0).isDigit())
812  return 0;
813  int n = issue.length();
814  int i;
815  for (i = 0; i < n && issue.at(i).isDigit(); ++i) ;
816  if (subVersion)
817  *subVersion = issue.mid(i);
818  v = issue.left(i).toUInt(); // issue number
819  vernum += (v < 99 ? v : 99);
820  }
821  return vernum;
822 }
823 
824 /******************************************************************************
825 * Check from its mime type whether a file appears to be a text or image file.
826 * If a text file, its type is distinguished.
827 * Reply = file type.
828 */
829 FileType fileType(const TQString& mimetype)
830 {
831  static const char* applicationTypes[] = {
832  "x-shellscript", "x-nawk", "x-awk", "x-perl", "x-python",
833  "x-desktop", "x-troff", 0 };
834  static const char* formattedTextTypes[] = {
835  "html", "xml", 0 };
836 
837  if (mimetype.startsWith(TQString::fromLatin1("image/")))
838  return Image;
839  int slash = mimetype.find('/');
840  if (slash < 0)
841  return Unknown;
842  TQString type = mimetype.mid(slash + 1);
843  const char* typel = type.latin1();
844  if (mimetype.startsWith(TQString::fromLatin1("application")))
845  {
846  for (int i = 0; applicationTypes[i]; ++i)
847  if (!strcmp(typel, applicationTypes[i]))
848  return TextApplication;
849  }
850  else if (mimetype.startsWith(TQString::fromLatin1("text")))
851  {
852  for (int i = 0; formattedTextTypes[i]; ++i)
853  if (!strcmp(typel, formattedTextTypes[i]))
854  return TextFormatted;
855  return TextPlain;
856  }
857  return Unknown;
858 }
859 
860 /******************************************************************************
861 * Display a modal dialogue to choose an existing file, initially highlighting
862 * any specified file.
863 * @param initialFile The file to initially highlight - must be a full path name or URL.
864 * @param defaultDir The directory to start in if @p initialFile is empty. If empty,
865 * the user's home directory will be used. Updated to the
866 * directory containing the selected file, if a file is chosen.
867 * @param mode OR of KFile::Mode values, e.g. ExistingOnly, LocalOnly.
868 * Reply = URL selected. If none is selected, URL.isEmpty() is true.
869 */
870 TQString browseFile(const TQString& caption, TQString& defaultDir, const TQString& initialFile,
871  const TQString& filter, int mode, TQWidget* parent, const char* name)
872 {
873  TQString initialDir = !initialFile.isEmpty() ? TQString(initialFile).remove(TQRegExp("/[^/]*$"))
874  : !defaultDir.isEmpty() ? defaultDir
875  : TQDir::homeDirPath();
876  KFileDialog fileDlg(initialDir, filter, parent, name, true);
877  fileDlg.setOperationMode(mode & KFile::ExistingOnly ? KFileDialog::Opening : KFileDialog::Saving);
878  fileDlg.setMode(KFile::File | mode);
879  fileDlg.setCaption(caption);
880  if (!initialFile.isEmpty())
881  fileDlg.setSelection(initialFile);
882  if (fileDlg.exec() != TQDialog::Accepted)
883  return TQString();
884  KURL url = fileDlg.selectedURL();
885  defaultDir = url.path();
886  return (mode & KFile::LocalOnly) ? url.path() : url.prettyURL();
887 }
888 
889 /******************************************************************************
890 * Return the first day of the week for the user's locale.
891 * Reply = 1 (Mon) .. 7 (Sun).
892 */
893 int localeFirstDayOfWeek()
894 {
895  static int firstDay = 0;
896  if (!firstDay)
897  firstDay = TDEGlobal::locale()->weekStartDay();
898  return firstDay;
899 }
900 
901 /******************************************************************************
902 * Return the supplied string with any accelerator code stripped out.
903 */
904 TQString stripAccel(const TQString& text)
905 {
906  unsigned len = text.length();
907  TQString out = TQDeepCopy<TQString>(text);
908  TQChar *corig = (TQChar*)out.unicode();
909  TQChar *cout = corig;
910  TQChar *cin = cout;
911  while (len)
912  {
913  if ( *cin == '&' )
914  {
915  ++cin;
916  --len;
917  if ( !len )
918  break;
919  }
920  *cout = *cin;
921  ++cout;
922  ++cin;
923  --len;
924  }
925  unsigned newlen = cout - corig;
926  if (newlen != out.length())
927  out.truncate(newlen);
928  return out;
929 }
930 
931 } // namespace KAlarm
932 
933 
934 namespace {
935 
936 /******************************************************************************
937 * Tell KOrganizer to put an alarm in its calendar.
938 * It will be held by KOrganizer as a simple event, without alarms - KAlarm
939 * is still responsible for alarming.
940 */
941 bool sendToKOrganizer(const KAEvent& event)
942 {
943  KCal::Event* kcalEvent = event.event();
944  TQString uid = KAEvent::uid(event.id(), KAEvent::KORGANIZER);
945  kcalEvent->setUid(uid);
946  kcalEvent->clearAlarms();
947  TQString userEmail;
948  switch (event.action())
949  {
950  case KAEvent::MESSAGE:
951  case KAEvent::FILE:
952  case KAEvent::COMMAND:
953  kcalEvent->setSummary(event.cleanText());
954  userEmail = Preferences::emailAddress();
955  break;
956  case KAEvent::EMAIL:
957  {
958  TQString from = event.emailFromId()
959  ? KAMail::identityManager()->identityForUoid(event.emailFromId()).fullEmailAddr()
960  : Preferences::emailAddress();
961  AlarmText atext;
962  atext.setEmail(event.emailAddresses(", "), from, TQString(), TQString(), event.emailSubject(), TQString());
963  kcalEvent->setSummary(atext.displayText());
964  userEmail = from;
965  break;
966  }
967  }
968  kcalEvent->setOrganizer(KCal::Person(TQString(), userEmail));
969 
970  // Translate the event into string format
971  KCal::ICalFormat format;
972  format.setTimeZone(TQString(), false);
973  TQString iCal = format.toICalString(kcalEvent);
974 kdDebug(5950)<<"Korg->"<<iCal<<endl;
975  delete kcalEvent;
976 
977  // Send the event to KOrganizer
978  if (!runKOrganizer()) // start KOrganizer if it isn't already running
979  return false;
980  TQByteArray data, replyData;
981  TQCString replyType;
982  TQDataStream arg(data, IO_WriteOnly);
983  arg << iCal;
984  if (kapp->dcopClient()->call(korganizerName, KORG_DCOP_OBJECT, "addIncidence(TQString)", data, replyType, replyData)
985  && replyType == "bool")
986  {
987  bool result;
988  TQDataStream reply(replyData, IO_ReadOnly);
989  reply >> result;
990  if (result)
991  {
992  kdDebug(5950) << "sendToKOrganizer(" << uid << "): success\n";
993  return true;
994  }
995  }
996  kdError(5950) << "sendToKOrganizer(): KOrganizer addEvent(" << uid << ") dcop call failed\n";
997  return false;
998 }
999 
1000 /******************************************************************************
1001 * Tell KOrganizer to delete an event from its calendar.
1002 */
1003 bool deleteFromKOrganizer(const TQString& eventID)
1004 {
1005  if (!runKOrganizer()) // start KOrganizer if it isn't already running
1006  return false;
1007  TQString newID = KAEvent::uid(eventID, KAEvent::KORGANIZER);
1008  TQByteArray data, replyData;
1009  TQCString replyType;
1010  TQDataStream arg(data, IO_WriteOnly);
1011  arg << newID << true;
1012  if (kapp->dcopClient()->call(korganizerName, KORG_DCOP_OBJECT, "deleteIncidence(TQString,bool)", data, replyType, replyData)
1013  && replyType == "bool")
1014  {
1015  bool result;
1016  TQDataStream reply(replyData, IO_ReadOnly);
1017  reply >> result;
1018  if (result)
1019  {
1020  kdDebug(5950) << "deleteFromKOrganizer(" << newID << "): success\n";
1021  return true;
1022  }
1023  }
1024  kdError(5950) << "sendToKOrganizer(): KOrganizer deleteEvent(" << newID << ") dcop call failed\n";
1025  return false;
1026 }
1027 
1028 /******************************************************************************
1029 * Start KOrganizer if not already running, and create its DCOP interface.
1030 */
1031 bool runKOrganizer()
1032 {
1033  TQString error;
1034  TQCString dcopService;
1035  int result = KDCOPServiceStarter::self()->findServiceFor("DCOP/Organizer", TQString(), TQString(), &error, &dcopService);
1036  if (result)
1037  {
1038  kdDebug(5950) << "Unable to start DCOP/Organizer: " << dcopService << " " << error << endl;
1039  return false;
1040  }
1041  // If Kontact is running, there is be a load() method which needs to be called
1042  // to load KOrganizer into Kontact. But if KOrganizer is running independently,
1043  // the load() method doesn't exist.
1044  TQCString dummy;
1045  if (!kapp->dcopClient()->findObject(dcopService, KORG_DCOP_OBJECT, "", TQByteArray(), dummy, dummy))
1046  {
1047  DCOPRef ref(dcopService, dcopService); // talk to the TDEUniqueApplication or its Kontact wrapper
1048  DCOPReply reply = ref.call("load()");
1049  if (!reply.isValid() || !(bool)reply)
1050  {
1051  kdWarning(5950) << "Error loading " << dcopService << endl;
1052  return false;
1053  }
1054  if (!kapp->dcopClient()->findObject(dcopService, KORG_DCOP_OBJECT, "", TQByteArray(), dummy, dummy))
1055  {
1056  kdWarning(5950) << "Unable to access KOrganizer's " KORG_DCOP_OBJECT " DCOP object" << endl;
1057  return false;
1058  }
1059  }
1060  return true;
1061 }
1062 
1063 } // namespace
1064 
1065 #ifdef HAVE_XTEST
1066 #include <X11/keysym.h>
1067 #include <X11/extensions/XTest.h>
1068 #include <tqwindowdefs.h>
1069 
1070 /******************************************************************************
1071 * Cancel the screen saver, in case it is active.
1072 * Only implemented if the X11 XTest extension is installed.
1073 */
1074 void x11_cancelScreenSaver()
1075 {
1076  kdDebug(5950) << "KAlarm::cancelScreenSaver()" << endl;
1077  Display* display = tqt_xdisplay();
1078  static int XTestKeyCode = 0;
1079  if (!XTestKeyCode)
1080  XTestKeyCode = XKeysymToKeycode(display, XK_Shift_L);
1081  XTestFakeKeyEvent(display, XTestKeyCode, true, CurrentTime);
1082  XTestFakeKeyEvent(display, XTestKeyCode, false, CurrentTime);
1083  XSync(display, false);
1084 }
1085 #endif // HAVE_XTEST
represents calendar alarms and events
Provides read and write access to calendar files.
Definition: alarmcalendar.h:37
KAEvent corresponds to a KCal::Event instance.
Definition: alarmevent.h:232
void setTimeZone(const TQString &id, bool utc)
TQString toICalString(Incidence *)
void setOrganizer(const Person &o)
void setUid(const TQString &)
void setSummary(const TQString &summary)
MessageWin: A window to display an alarm message.
Definition: messagewin.h:45
miscellaneous functions
UpdateStatus
Return codes from calendar update functions.
Definition: functions.h:51
FileType
Return codes from fileType()
Definition: functions.h:47
the KAlarm application object
main application window
displays an alarm message