kalarm

undo.cpp

00001 /*
00002  *  undo.cpp  -  undo/redo facility
00003  *  Program:  kalarm
00004  *  Copyright © 2005,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 <qobject.h>
00024 #include <qstringlist.h>
00025 
00026 #include <kapplication.h>
00027 #include <klocale.h>
00028 #include <kmessagebox.h>
00029 #include <kdebug.h>
00030 
00031 #include "alarmcalendar.h"
00032 #include "alarmevent.h"
00033 #include "alarmtext.h"
00034 #include "functions.h"
00035 #include "undo.moc"
00036 
00037 static int maxCount = 12;
00038 
00039 
00040 class UndoItem
00041 {
00042     public:
00043         enum Operation { ADD, EDIT, DELETE, REACTIVATE, DEACTIVATE, MULTI };
00044         UndoItem();           // needed by QValueList
00045         virtual ~UndoItem();
00046         virtual Operation operation() const = 0;
00047         virtual QString   actionText() const = 0;
00048         virtual QString   description() const   { return QString::null; }
00049         virtual QString   eventID() const       { return QString::null; }
00050         virtual QString   oldEventID() const    { return QString::null; }
00051         virtual QString   newEventID() const    { return QString::null; }
00052         int               id() const            { return mId; }
00053         Undo::Type        type() const          { return mType; }
00054         void              setType(Undo::Type t) { mType = t; }
00055         KAEvent::Status   calendar() const      { return mCalendar; }
00056         virtual void      setCalendar(KAEvent::Status s) { mCalendar = s; }
00057         virtual UndoItem* restore() = 0;
00058         virtual bool      deleteID(const QString& /*id*/)  { return false; }
00059 
00060         enum Error   { ERR_NONE, ERR_PROG, ERR_NOT_FOUND, ERR_CREATE, ERR_TEMPLATE, ERR_EXPIRED };
00061         enum Warning { WARN_NONE, WARN_KORG_ADD, WARN_KORG_MODIFY, WARN_KORG_DELETE };
00062         static int        mLastId;
00063         static Error      mRestoreError;         // error code valid only if restore() returns 0
00064         static Warning    mRestoreWarning;       // warning code set by restore()
00065         static int        mRestoreWarningCount;  // item count for mRestoreWarning (to allow i18n messages to work correctly)
00066 
00067     protected:
00068         UndoItem(Undo::Type);
00069         static QString    addDeleteActionText(KAEvent::Status, bool add);
00070         QString           description(const KAEvent&) const;
00071         void              replaceWith(UndoItem* item)   { Undo::replace(this, item); }
00072 
00073         int               mId;     // unique identifier (only for mType = UNDO, REDO)
00074         Undo::Type        mType;   // which list (if any) the object is in
00075         KAEvent::Status   mCalendar;
00076 };
00077 
00078 class UndoMultiBase : public UndoItem
00079 {
00080     public:
00081         UndoMultiBase(Undo::Type t) : UndoItem(t) { }
00082         UndoMultiBase(Undo::Type t, Undo::List& undos) : UndoItem(t), mUndos(undos) { }
00083         ~UndoMultiBase();
00084         const Undo::List& undos() const         { return mUndos; }
00085     protected:
00086         Undo::List  mUndos;    // this list must always have >= 2 entries
00087 };
00088 
00089 template <class T> class UndoMulti : public UndoMultiBase
00090 {
00091     public:
00092         UndoMulti(Undo::Type, const QValueList<KAEvent>&);
00093         UndoMulti(Undo::Type t, Undo::List& undos)  : UndoMultiBase(t, undos) { }
00094         virtual Operation operation() const     { return MULTI; }
00095         virtual UndoItem* restore();
00096         virtual bool      deleteID(const QString& id);
00097         virtual UndoItem* createRedo(Undo::List&) = 0;
00098 };
00099 
00100 class UndoAdd : public UndoItem
00101 {
00102     public:
00103         UndoAdd(Undo::Type, const KAEvent&);
00104         UndoAdd(Undo::Type, const KAEvent&, KAEvent::Status);
00105         virtual Operation operation() const     { return ADD; }
00106         virtual QString   actionText() const;
00107         virtual QString   description() const   { return mDescription; }
00108         virtual QString   eventID() const       { return mEventID; }
00109         virtual QString   newEventID() const    { return mEventID; }
00110         virtual UndoItem* restore()             { return doRestore(); }
00111     protected:
00112         UndoItem*         doRestore(bool setArchive = false);
00113         virtual UndoItem* createRedo(const KAEvent&);
00114     private:
00115         QString  mEventID;
00116         QString  mDescription;
00117 };
00118 
00119 class UndoEdit : public UndoItem
00120 {
00121     public:
00122         UndoEdit(Undo::Type, const KAEvent& oldEvent, const QString& newEventID, const QString& description);
00123         ~UndoEdit();
00124         virtual Operation operation() const     { return EDIT; }
00125         virtual QString   actionText() const;
00126         virtual QString   description() const   { return mDescription; }
00127         virtual QString   eventID() const       { return mNewEventID; }
00128         virtual QString   oldEventID() const    { return mOldEvent->id(); }
00129         virtual QString   newEventID() const    { return mNewEventID; }
00130         virtual UndoItem* restore();
00131     private:
00132         KAEvent*  mOldEvent;
00133         QString   mNewEventID;
00134         QString   mDescription;
00135 };
00136 
00137 class UndoDelete : public UndoItem
00138 {
00139     public:
00140         UndoDelete(Undo::Type, const KAEvent&);
00141         ~UndoDelete();
00142         virtual Operation operation() const     { return DELETE; }
00143         virtual QString   actionText() const;
00144         virtual QString   description() const   { return UndoItem::description(*mEvent); }
00145         virtual QString   eventID() const       { return mEvent->id(); }
00146         virtual QString   oldEventID() const    { return mEvent->id(); }
00147         virtual UndoItem* restore();
00148         KAEvent*  event() const                 { return mEvent; }
00149     protected:
00150         virtual UndoItem* createRedo(const KAEvent&);
00151     private:
00152         KAEvent*  mEvent;
00153 };
00154 
00155 class UndoReactivate : public UndoAdd
00156 {
00157     public:
00158         UndoReactivate(Undo::Type t, const KAEvent& e)  : UndoAdd(t, e, KAEvent::ACTIVE) { }
00159         virtual Operation operation() const     { return REACTIVATE; }
00160         virtual QString   actionText() const;
00161         virtual UndoItem* restore();
00162     protected:
00163         virtual UndoItem* createRedo(const KAEvent&);
00164 };
00165 
00166 class UndoDeactivate : public UndoDelete
00167 {
00168     public:
00169         UndoDeactivate(Undo::Type t, const KAEvent& e)  : UndoDelete(t, e) { }
00170         virtual Operation operation() const     { return DEACTIVATE; }
00171         virtual QString   actionText() const;
00172         virtual UndoItem* restore();
00173     protected:
00174         virtual UndoItem* createRedo(const KAEvent&);
00175 };
00176 
00177 class UndoDeletes : public UndoMulti<UndoDelete>
00178 {
00179     public:
00180         UndoDeletes(Undo::Type t, const QValueList<KAEvent>& events)
00181                           : UndoMulti<UndoDelete>(t, events) { }   // UNDO only
00182         UndoDeletes(Undo::Type t, Undo::List& undos)
00183                           : UndoMulti<UndoDelete>(t, undos) { }
00184         virtual QString   actionText() const;
00185         virtual UndoItem* createRedo(Undo::List&);
00186 };
00187 
00188 class UndoReactivates : public UndoMulti<UndoReactivate>
00189 {
00190     public:
00191         UndoReactivates(Undo::Type t, const QValueList<KAEvent>& events)
00192                           : UndoMulti<UndoReactivate>(t, events) { }   // UNDO only
00193         UndoReactivates(Undo::Type t, Undo::List& undos)
00194                           : UndoMulti<UndoReactivate>(t, undos) { }
00195         virtual QString   actionText() const;
00196         virtual UndoItem* createRedo(Undo::List&);
00197 };
00198 
00199 Undo*       Undo::mInstance = 0;
00200 Undo::List  Undo::mUndoList;
00201 Undo::List  Undo::mRedoList;
00202 
00203 
00204 /******************************************************************************
00205 *  Create the one and only instance of the Undo class.
00206 */
00207 Undo* Undo::instance()
00208 {
00209     if (!mInstance)
00210         mInstance = new Undo(kapp);
00211     return mInstance;
00212 }
00213 
00214 /******************************************************************************
00215 *  Clear the lists of undo and redo items.
00216 */
00217 void Undo::clear()
00218 {
00219     if (!mUndoList.isEmpty()  ||  !mRedoList.isEmpty())
00220     {
00221         mInstance->blockSignals(true);
00222         while (mUndoList.count())
00223             delete mUndoList.first();    // N.B. 'delete' removes the object from the list
00224         while (mRedoList.count())
00225             delete mRedoList.first();    // N.B. 'delete' removes the object from the list
00226         mInstance->blockSignals(false);
00227         emitChanged();
00228     }
00229 }
00230 
00231 /******************************************************************************
00232 *  Create an undo item and add it to the list of undos.
00233 *  N.B. The base class constructor adds the object to the undo list.
00234 */
00235 void Undo::saveAdd(const KAEvent& event)
00236 {
00237     new UndoAdd(UNDO, event);
00238     emitChanged();
00239 }
00240 
00241 void Undo::saveEdit(const KAEvent& oldEvent, const KAEvent& newEvent)
00242 {
00243     new UndoEdit(UNDO, oldEvent, newEvent.id(), AlarmText::summary(newEvent));
00244     removeRedos(oldEvent.id());    // remove any redos which are made invalid by this edit
00245     emitChanged();
00246 }
00247 
00248 void Undo::saveDelete(const KAEvent& event)
00249 {
00250     new UndoDelete(UNDO, event);
00251     removeRedos(event.id());    // remove any redos which are made invalid by this deletion
00252     emitChanged();
00253 }
00254 
00255 void Undo::saveDeletes(const QValueList<KAEvent>& events)
00256 {
00257     int count = events.count();
00258     if (count == 1)
00259         saveDelete(events.first());
00260     else if (count > 1)
00261     {
00262         new UndoDeletes(UNDO, events);
00263         for (QValueList<KAEvent>::ConstIterator it = events.begin();  it != events.end();  ++it)
00264             removeRedos((*it).id());    // remove any redos which are made invalid by these deletions
00265         emitChanged();
00266     }
00267 }
00268 
00269 void Undo::saveReactivate(const KAEvent& event)
00270 {
00271     new UndoReactivate(UNDO, event);
00272     emitChanged();
00273 }
00274 
00275 void Undo::saveReactivates(const QValueList<KAEvent>& events)
00276 {
00277     int count = events.count();
00278     if (count == 1)
00279         saveReactivate(events.first());
00280     else if (count > 1)
00281     {
00282         new UndoReactivates(UNDO, events);
00283         emitChanged();
00284     }
00285 }
00286 
00287 /******************************************************************************
00288 *  Remove any redos which are made invalid by a new undo.
00289 */
00290 void Undo::removeRedos(const QString& eventID)
00291 {
00292     QString id = eventID;
00293     for (Iterator it = mRedoList.begin();  it != mRedoList.end();  )
00294     {
00295         UndoItem* item = *it;
00296 //kdDebug(5950)<<"removeRedos(): "<<item->eventID()<<" (looking for "<<id<<")"<<endl;
00297         if (item->operation() == UndoItem::MULTI)
00298         {
00299             if (item->deleteID(id))
00300             {
00301                 // The old multi-redo was replaced with a new single redo
00302                 delete item;
00303             }
00304             ++it;
00305         }
00306         else if (item->eventID() == id)
00307         {
00308             if (item->operation() == UndoItem::EDIT)
00309                 id = item->oldEventID();   // continue looking for its post-edit ID
00310             item->setType(NONE);    // prevent the destructor removing it from the list
00311             delete item;
00312             it = mRedoList.remove(it);
00313         }
00314         else
00315             ++it;
00316     }
00317 }
00318 
00319 /******************************************************************************
00320 *  Undo or redo a specified item.
00321 *  Reply = true if success, or if the item no longer exists.
00322 */
00323 bool Undo::undo(Undo::Iterator it, Undo::Type type, QWidget* parent, const QString& action)
00324 {
00325     UndoItem::mRestoreError   = UndoItem::ERR_NONE;
00326     UndoItem::mRestoreWarning = UndoItem::WARN_NONE;
00327     UndoItem::mRestoreWarningCount = 0;
00328     if (it != mUndoList.end()  &&  it != mRedoList.end()  &&  (*it)->type() == type)
00329     {
00330         (*it)->restore();
00331         delete *it;    // N.B. 'delete' removes the object from its list
00332         emitChanged();
00333     }
00334 
00335     QString err;
00336     switch (UndoItem::mRestoreError)
00337     {
00338         case UndoItem::ERR_NONE:
00339         {
00340             KAlarm::KOrgUpdateError errcode;
00341             switch (UndoItem::mRestoreWarning)
00342             {
00343                 case UndoItem::WARN_KORG_ADD:     errcode = KAlarm::KORG_ERR_ADD;  break;
00344                 case UndoItem::WARN_KORG_MODIFY:  errcode = KAlarm::KORG_ERR_MODIFY;  break;
00345                 case UndoItem::WARN_KORG_DELETE:  errcode = KAlarm::KORG_ERR_DELETE;  break;
00346                 case UndoItem::WARN_NONE:
00347                 default:
00348                     return true;
00349             }
00350             KAlarm::displayKOrgUpdateError(parent, errcode, UndoItem::mRestoreWarningCount);
00351             return true;
00352         }
00353         case UndoItem::ERR_NOT_FOUND:  err = i18n("Alarm not found");  break;
00354         case UndoItem::ERR_CREATE:     err = i18n("Error recreating alarm");  break;
00355         case UndoItem::ERR_TEMPLATE:   err = i18n("Error recreating alarm template");  break;
00356         case UndoItem::ERR_EXPIRED:    err = i18n("Cannot reactivate expired alarm");  break;
00357         case UndoItem::ERR_PROG:       err = i18n("Program error");  break;
00358         default:                       err = i18n("Unknown error");  break;
00359     }
00360     KMessageBox::sorry(parent, i18n("Undo-action: message", "%1: %2").arg(action).arg(err));
00361     return false;
00362 }
00363 
00364 /******************************************************************************
00365 *  Add an undo item to the start of one of the lists.
00366 */
00367 void Undo::add(UndoItem* item, bool undo)
00368 {
00369     if (item)
00370     {
00371         // Limit the number of items stored
00372         int undoCount = mUndoList.count();
00373         int redoCount = mRedoList.count();
00374         if (undoCount + redoCount >= maxCount - 1)
00375         {
00376             if (undoCount)
00377                 mUndoList.pop_back();
00378             else
00379                 mRedoList.pop_back();
00380         }
00381 
00382         // Append the new item
00383         List* list = undo ? &mUndoList : &mRedoList;
00384         list->prepend(item);
00385     }
00386 }
00387 
00388 /******************************************************************************
00389 *  Remove an undo item from one of the lists.
00390 */
00391 void Undo::remove(UndoItem* item, bool undo)
00392 {
00393     List* list = undo ? &mUndoList : &mRedoList;
00394     if (!list->isEmpty())
00395         list->remove(item);
00396 }
00397 
00398 /******************************************************************************
00399 *  Replace an undo item in one of the lists.
00400 */
00401 void Undo::replace(UndoItem* old, UndoItem* New)
00402 {
00403     Type type = old->type();
00404     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00405     if (!list)
00406         return;
00407     Iterator it = list->find(old);
00408     if (it != list->end())
00409     {
00410         New->setType(type);    // ensure the item points to the correct list
00411         *it = New;
00412         old->setType(NONE);    // mark the old item as no longer being in a list
00413     }
00414 }
00415 
00416 /******************************************************************************
00417 *  Return the action description of the latest undo/redo item.
00418 */
00419 QString Undo::actionText(Undo::Type type)
00420 {
00421     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00422     return (list && !list->isEmpty()) ? list->first()->actionText() : QString::null;
00423 }
00424 
00425 /******************************************************************************
00426 *  Return the action description of the undo/redo item with the specified ID.
00427 */
00428 QString Undo::actionText(Undo::Type type, int id)
00429 {
00430     UndoItem* undo = getItem(id, type);
00431     return undo ? undo->actionText() : QString::null;
00432 }
00433 
00434 /******************************************************************************
00435 *  Return the alarm description of the undo/redo item with the specified ID.
00436 */
00437 QString Undo::description(Undo::Type type, int id)
00438 {
00439     UndoItem* undo = getItem(id, type);
00440     return undo ? undo->description() : QString::null;
00441 }
00442 
00443 /******************************************************************************
00444 *  Return the descriptions of all undo or redo items, in order latest first.
00445 *  For alarms which have undergone more than one change, only the first one is
00446 *  listed, to force dependent undos to be executed in their correct order.
00447 *  If 'ids' is non-null, also returns a list of their corresponding IDs.
00448 */
00449 QValueList<int> Undo::ids(Undo::Type type)
00450 {
00451     QValueList<int> ids;
00452     QStringList ignoreIDs;
00453 //int n=0;
00454     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00455     if (!list)
00456         return ids;
00457     for (Iterator it = list->begin();  it != list->end();  ++it)
00458     {
00459         // Check whether this item should be ignored because it is a
00460         // deendent undo. If not, add this item's ID to the ignore list.
00461         UndoItem* item = *it;
00462         bool omit = false;
00463         if (item->operation() == UndoItem::MULTI)
00464         {
00465             // If any item in a multi-undo is disqualified, omit the whole multi-undo
00466             QStringList newIDs;
00467             const Undo::List& undos = ((UndoMultiBase*)item)->undos();
00468             for (Undo::List::ConstIterator u = undos.begin();  u != undos.end();  ++u)
00469             {
00470                 QString evid = (*u)->eventID();
00471                 if (ignoreIDs.find(evid) != ignoreIDs.end())
00472                     omit = true;
00473                 else if (omit)
00474                     ignoreIDs.append(evid);
00475                 else
00476                     newIDs.append(evid);
00477             }
00478             if (omit)
00479             {
00480                 for (QStringList::ConstIterator i = newIDs.begin();  i != newIDs.end();  ++i)
00481                     ignoreIDs.append(*i);
00482             }
00483         }
00484         else
00485         {
00486             omit = (ignoreIDs.find(item->eventID()) != ignoreIDs.end());
00487             if (!omit)
00488                 ignoreIDs.append(item->eventID());
00489             if (item->operation() == UndoItem::EDIT)
00490                 ignoreIDs.append(item->oldEventID());   // continue looking for its post-edit ID
00491         }
00492         if (!omit)
00493             ids.append(item->id());
00494 //else kdDebug(5950)<<"Undo::ids(): omit "<<item->actionText()<<": "<<item->description()<<endl;
00495     }
00496 //kdDebug(5950)<<"Undo::ids(): "<<n<<" -> "<<ids.count()<<endl;
00497     return ids;
00498 }
00499 
00500 /******************************************************************************
00501 *  Emit the appropriate 'changed' signal.
00502 */
00503 void Undo::emitChanged()
00504 {
00505     if (mInstance)
00506         mInstance->emitChanged(actionText(UNDO), actionText(REDO));
00507 }
00508 
00509 /******************************************************************************
00510 *  Return the item with the specified ID.
00511 */
00512 UndoItem* Undo::getItem(int id, Undo::Type type)
00513 {
00514     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00515     if (list)
00516     {
00517         for (Iterator it = list->begin();  it != list->end();  ++it)
00518         {
00519             if ((*it)->id() == id)
00520                 return *it;
00521         }
00522     }
00523     return 0;
00524 }
00525 
00526 /******************************************************************************
00527 *  Find an item with the specified ID.
00528 */
00529 Undo::Iterator Undo::findItem(int id, Undo::Type type)
00530 {
00531     List* list = (type == UNDO) ? &mUndoList : &mRedoList;
00532     Iterator it;
00533     for (it = list->begin();  it != list->end();  ++it)
00534     {
00535         if ((*it)->id() == id)
00536             break;
00537     }
00538     return it;
00539 }
00540 
00541 
00542 /*=============================================================================
00543 =  Class: UndoItem
00544 =  A single undo action.
00545 =============================================================================*/
00546 int               UndoItem::mLastId = 0;
00547 UndoItem::Error   UndoItem::mRestoreError;
00548 UndoItem::Warning UndoItem::mRestoreWarning;
00549 int               UndoItem::mRestoreWarningCount;
00550 
00551 /******************************************************************************
00552 *  Constructor.
00553 *  Optionally appends the undo to the list of undos.
00554 */
00555 UndoItem::UndoItem(Undo::Type type)
00556     : mId(0),
00557       mType(type)
00558 {
00559     if (type != Undo::NONE)
00560     {
00561         mId = ++mLastId;
00562         if (mId < 0)
00563             mId = mLastId = 1;    // wrap round if we reach a negative number
00564         Undo::add(this, (mType == Undo::UNDO));
00565     }
00566 }
00567 
00568 /******************************************************************************
00569 *  Destructor.
00570 *  Removes the undo from the list (if it's in the list).
00571 */
00572 UndoItem::~UndoItem()
00573 {
00574     if (mType != Undo::NONE)
00575         Undo::remove(this, (mType == Undo::UNDO));
00576 }
00577 
00578 /******************************************************************************
00579 *  Return the description of an event.
00580 */
00581 QString UndoItem::description(const KAEvent& event) const
00582 {
00583     return (mCalendar == KAEvent::TEMPLATE) ? event.templateName() : AlarmText::summary(event);
00584 }
00585 
00586 /******************************************************************************
00587 *  Return the action description of an add or delete Undo/Redo item for displaying.
00588 */
00589 QString UndoItem::addDeleteActionText(KAEvent::Status calendar, bool add)
00590 {
00591     switch (calendar)
00592     {
00593         case KAEvent::ACTIVE:
00594             if (add)
00595                 return i18n("Action to create a new alarm", "New alarm");
00596             else
00597                 return i18n("Action to delete an alarm", "Delete alarm");
00598         case KAEvent::TEMPLATE:
00599             if (add)
00600                 return i18n("Action to create a new alarm template", "New template");
00601             else
00602                 return i18n("Action to delete an alarm template", "Delete template");
00603         case KAEvent::EXPIRED:
00604             return i18n("Delete expired alarm");
00605         default:
00606             break;
00607     }
00608     return QString::null;
00609 }
00610 
00611 
00612 /*=============================================================================
00613 =  Class: UndoMultiBase
00614 =  Undo item for multiple alarms.
00615 =============================================================================*/
00616 
00617 template <class T>
00618 UndoMulti<T>::UndoMulti(Undo::Type type, const QValueList<KAEvent>& events)
00619     : UndoMultiBase(type)    // UNDO only
00620 {
00621     for (QValueList<KAEvent>::ConstIterator it = events.begin();  it != events.end();  ++it)
00622         mUndos.append(new T(Undo::NONE, *it));
00623 }
00624 
00625 UndoMultiBase::~UndoMultiBase()
00626 {
00627     for (Undo::List::Iterator it = mUndos.begin();  it != mUndos.end();  ++it)
00628         delete *it;
00629 }
00630 
00631 /******************************************************************************
00632 *  Undo the item, i.e. restore multiple alarms which were deleted (or delete
00633 *  alarms which were restored).
00634 *  Create a redo item to delete (or restore) the alarms again.
00635 *  Reply = redo item.
00636 */
00637 template <class T>
00638 UndoItem* UndoMulti<T>::restore()
00639 {
00640     Undo::List newUndos;
00641     for (Undo::List::Iterator it = mUndos.begin();  it != mUndos.end();  ++it)
00642     {
00643         UndoItem* undo = (*it)->restore();
00644         if (undo)
00645             newUndos.append(undo);
00646     }
00647     if (newUndos.isEmpty())
00648         return 0;
00649 
00650     // Create a redo item to delete the alarm again
00651     return createRedo(newUndos);
00652 }
00653 
00654 /******************************************************************************
00655 *  If one of the multiple items has the specified ID, delete it.
00656 *  If an item is deleted and there is only one item left, the UndoMulti
00657 *  instance is removed from its list and replaced by the remaining UndoItem instead.
00658 *  Reply = true if this instance was replaced. The caller must delete it.
00659 *        = false otherwise.
00660 */
00661 template <class T>
00662 bool UndoMulti<T>::deleteID(const QString& id)
00663 {
00664     for (Undo::List::Iterator it = mUndos.begin();  it != mUndos.end();  ++it)
00665     {
00666         UndoItem* item = *it;
00667         if (item->eventID() == id)
00668         {
00669             // Found a matching entry - remove it
00670             mUndos.remove(it);
00671             if (mUndos.count() == 1)
00672             {
00673                 // There is only one entry left after removal.
00674                 // Replace 'this' multi instance with the remaining single entry.
00675                 replaceWith(item);
00676                 return true;
00677             }
00678             else
00679             {
00680                 delete item;
00681                 return false;
00682             }
00683         }
00684     }
00685     return false;
00686 }
00687 
00688 
00689 /*=============================================================================
00690 =  Class: UndoAdd
00691 =  Undo item for alarm creation.
00692 =============================================================================*/
00693 
00694 UndoAdd::UndoAdd(Undo::Type type, const KAEvent& event)
00695     : UndoItem(type),
00696       mEventID(event.id())
00697 {
00698     setCalendar(KAEvent::uidStatus(mEventID));
00699     mDescription = UndoItem::description(event);    // calendar must be set before calling this
00700 }
00701 
00702 UndoAdd::UndoAdd(Undo::Type type, const KAEvent& event, KAEvent::Status cal)
00703     : UndoItem(type),
00704       mEventID(KAEvent::uid(event.id(), cal))
00705 {
00706     setCalendar(cal);
00707     mDescription = UndoItem::description(event);    // calendar must be set before calling this
00708 }
00709 
00710 /******************************************************************************
00711 *  Undo the item, i.e. delete the alarm which was added.
00712 *  Create a redo item to add the alarm back again.
00713 *  Reply = redo item.
00714 */
00715 UndoItem* UndoAdd::doRestore(bool setArchive)
00716 {
00717     // Retrieve the current state of the alarm
00718     kdDebug(5950) << "UndoAdd::doRestore(" << mEventID << ")\n";
00719     const KCal::Event* kcalEvent = AlarmCalendar::getEvent(mEventID);
00720     if (!kcalEvent)
00721     {
00722         mRestoreError = ERR_NOT_FOUND;    // alarm is no longer in calendar
00723         return 0;
00724     }
00725     KAEvent event(*kcalEvent); 
00726 
00727     // Create a redo item to recreate the alarm.
00728     // Do it now, since 'event' gets modified by KAlarm::deleteEvent()
00729     UndoItem* undo = createRedo(event);
00730 
00731     switch (calendar())
00732     {
00733         case KAEvent::ACTIVE:
00734             if (setArchive)
00735                 event.setArchive();
00736             // Archive it if it has already triggered
00737             switch (KAlarm::deleteEvent(event, true))
00738             {
00739                 case KAlarm::UPDATE_ERROR:
00740                 case KAlarm::UPDATE_FAILED:
00741                 case KAlarm::SAVE_FAILED:
00742                     mRestoreError = ERR_CREATE;
00743                     break;
00744                 case KAlarm::UPDATE_KORG_ERR:
00745                     mRestoreWarning = WARN_KORG_DELETE;
00746                     ++mRestoreWarningCount;
00747                     break;
00748                 default:
00749                     break;
00750             }
00751             break;
00752         case KAEvent::TEMPLATE:
00753             if (KAlarm::deleteTemplate(event) != KAlarm::UPDATE_OK)
00754                 mRestoreError = ERR_TEMPLATE;
00755             break;
00756         case KAEvent::EXPIRED:    // redoing the deletion of an expired alarm
00757             KAlarm::deleteEvent(event);
00758             break;
00759         default:
00760             delete undo;
00761             mRestoreError = ERR_PROG;
00762             return 0;
00763     }
00764     return undo;
00765 }
00766 
00767 /******************************************************************************
00768 *  Create a redo item to add the alarm back again.
00769 */
00770 UndoItem* UndoAdd::createRedo(const KAEvent& event)
00771 {
00772     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00773     return new UndoDelete(t, event);
00774 }
00775 
00776 /******************************************************************************
00777 *  Return the action description of the Undo item for displaying.
00778 */
00779 QString UndoAdd::actionText() const
00780 {
00781     return addDeleteActionText(calendar(), (type() == Undo::UNDO));
00782 }
00783 
00784 
00785 /*=============================================================================
00786 =  Class: UndoEdit
00787 =  Undo item for alarm edit.
00788 =============================================================================*/
00789 
00790 UndoEdit::UndoEdit(Undo::Type type, const KAEvent& oldEvent, const QString& newEventID, const QString& description)
00791     : UndoItem(type),
00792       mOldEvent(new KAEvent(oldEvent)),
00793       mNewEventID(newEventID),
00794       mDescription(description)
00795 {
00796     setCalendar(KAEvent::uidStatus(mNewEventID));
00797 }
00798 
00799 UndoEdit::~UndoEdit()
00800 {
00801     delete mOldEvent;
00802 }
00803 
00804 /******************************************************************************
00805 *  Undo the item, i.e. undo an edit to a previously existing alarm.
00806 *  Create a redo item to reapply the edit.
00807 *  Reply = redo item.
00808 */
00809 UndoItem* UndoEdit::restore()
00810 {
00811     kdDebug(5950) << "UndoEdit::restore(" << mNewEventID << ")\n";
00812     // Retrieve the current state of the alarm
00813     const KCal::Event* kcalEvent = AlarmCalendar::getEvent(mNewEventID);
00814     if (!kcalEvent)
00815     {
00816         mRestoreError = ERR_NOT_FOUND;    // alarm is no longer in calendar
00817         return 0;
00818     }
00819     KAEvent newEvent(*kcalEvent); 
00820 
00821     // Create a redo item to restore the edit
00822     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00823     UndoItem* undo = new UndoEdit(t, newEvent, mOldEvent->id(), mDescription);
00824 
00825     switch (calendar())
00826     {
00827         case KAEvent::ACTIVE:
00828             switch (KAlarm::modifyEvent(newEvent, *mOldEvent, 0))
00829             {
00830                 case KAlarm::UPDATE_ERROR:
00831                 case KAlarm::UPDATE_FAILED:
00832                 case KAlarm::SAVE_FAILED:
00833                     mRestoreError = ERR_CREATE;
00834                     break;
00835                 case KAlarm::UPDATE_KORG_ERR:
00836                     mRestoreWarning = WARN_KORG_MODIFY;
00837                     ++mRestoreWarningCount;
00838                     break;
00839                 default:
00840                     break;
00841             }
00842             break;
00843         case KAEvent::TEMPLATE:
00844             if (KAlarm::updateTemplate(*mOldEvent, 0) != KAlarm::UPDATE_OK)
00845                 mRestoreError = ERR_TEMPLATE;
00846             break;
00847         case KAEvent::EXPIRED:    // editing of expired events is not allowed
00848         default:
00849             delete undo;
00850             mRestoreError = ERR_PROG;
00851             return 0;
00852     }
00853     return undo;
00854 }
00855 
00856 /******************************************************************************
00857 *  Return the action description of the Undo item for displaying.
00858 */
00859 QString UndoEdit::actionText() const
00860 {
00861     switch (calendar())
00862     {
00863         case KAEvent::ACTIVE:
00864             return i18n("Action to edit an alarm", "Edit alarm");
00865         case KAEvent::TEMPLATE:
00866             return i18n("Action to edit an alarm template", "Edit template");
00867         default:
00868             break;
00869     }
00870     return QString::null;
00871 }
00872 
00873 
00874 /*=============================================================================
00875 =  Class: UndoDelete
00876 =  Undo item for alarm deletion.
00877 =============================================================================*/
00878 
00879 UndoDelete::UndoDelete(Undo::Type type, const KAEvent& event)
00880     : UndoItem(type),
00881       mEvent(new KAEvent(event))
00882 {
00883     setCalendar(KAEvent::uidStatus(mEvent->id()));
00884 }
00885 
00886 UndoDelete::~UndoDelete()
00887 {
00888     delete mEvent;
00889 }
00890 
00891 /******************************************************************************
00892 *  Undo the item, i.e. restore an alarm which was deleted.
00893 *  Create a redo item to delete the alarm again.
00894 *  Reply = redo item.
00895 */
00896 UndoItem* UndoDelete::restore()
00897 {
00898     kdDebug(5950) << "UndoDelete::restore(" << mEvent->id() << ")\n";
00899     // Restore the original event
00900     switch (calendar())
00901     {
00902         case KAEvent::ACTIVE:
00903             if (mEvent->toBeArchived())
00904             {
00905                 // It was archived when it was deleted
00906                 mEvent->setUid(KAEvent::EXPIRED);
00907                 switch (KAlarm::reactivateEvent(*mEvent, 0, true))
00908                 {
00909                     case KAlarm::UPDATE_KORG_ERR:
00910                         mRestoreWarning = WARN_KORG_ADD;
00911                         ++mRestoreWarningCount;
00912                         break;
00913                     case KAlarm::UPDATE_ERROR:
00914                     case KAlarm::UPDATE_FAILED:
00915                     case KAlarm::SAVE_FAILED:
00916                         mRestoreError = ERR_EXPIRED;
00917                         return 0;
00918                     case KAlarm::UPDATE_OK:
00919                         break;
00920                 }
00921             }
00922             else
00923             {
00924                 switch (KAlarm::addEvent(*mEvent, 0, 0, true))
00925                 {
00926                     case KAlarm::UPDATE_KORG_ERR:
00927                         mRestoreWarning = WARN_KORG_ADD;
00928                         ++mRestoreWarningCount;
00929                         break;
00930                     case KAlarm::UPDATE_ERROR:
00931                     case KAlarm::UPDATE_FAILED:
00932                     case KAlarm::SAVE_FAILED:
00933                         mRestoreError = ERR_CREATE;
00934                         return 0;
00935                     case KAlarm::UPDATE_OK:
00936                         break;
00937                 }
00938             }
00939             break;
00940         case KAEvent::TEMPLATE:
00941             if (KAlarm::addTemplate(*mEvent, 0) != KAlarm::UPDATE_OK)
00942             {
00943                 mRestoreError = ERR_CREATE;
00944                 return 0;
00945             }
00946             break;
00947         case KAEvent::EXPIRED:
00948             if (!KAlarm::addExpiredEvent(*mEvent))
00949             {
00950                 mRestoreError = ERR_CREATE;
00951                 return 0;
00952             }
00953             break;
00954         default:
00955             mRestoreError = ERR_PROG;
00956             return 0;
00957     }
00958 
00959     // Create a redo item to delete the alarm again
00960     return createRedo(*mEvent);
00961 }
00962 
00963 /******************************************************************************
00964 *  Create a redo item to archive the alarm again.
00965 */
00966 UndoItem* UndoDelete::createRedo(const KAEvent& event)
00967 {
00968     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00969     return new UndoAdd(t, event);
00970 }
00971 
00972 /******************************************************************************
00973 *  Return the action description of the Undo item for displaying.
00974 */
00975 QString UndoDelete::actionText() const
00976 {
00977     return addDeleteActionText(calendar(), (type() == Undo::REDO));
00978 }
00979 
00980 
00981 /*=============================================================================
00982 =  Class: UndoDeletes
00983 =  Undo item for multiple alarm deletion.
00984 =============================================================================*/
00985 
00986 /******************************************************************************
00987 *  Create a redo item to delete the alarms again.
00988 */
00989 UndoItem* UndoDeletes::createRedo(Undo::List& undos)
00990 {
00991     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00992     return new UndoDeletes(t, undos);
00993 }
00994 
00995 /******************************************************************************
00996 *  Return the action description of the Undo item for displaying.
00997 */
00998 QString UndoDeletes::actionText() const
00999 {
01000     if (mUndos.isEmpty())
01001         return QString::null;
01002     for (Undo::List::ConstIterator it = mUndos.begin();  it != mUndos.end();  ++it)
01003     {
01004         switch ((*it)->calendar())
01005         {
01006             case KAEvent::ACTIVE:
01007                 return i18n("Delete multiple alarms");
01008             case KAEvent::TEMPLATE:
01009                 return i18n("Delete multiple templates");
01010             case KAEvent::EXPIRED:
01011                 break;    // check if they are ALL expired
01012             default:
01013                 return QString::null;
01014         }
01015     }
01016     return i18n("Delete multiple expired alarms");
01017 }
01018 
01019 
01020 /*=============================================================================
01021 =  Class: UndoReactivate
01022 =  Undo item for alarm reactivation.
01023 =============================================================================*/
01024 
01025 /******************************************************************************
01026 *  Undo the item, i.e. re-archive the alarm which was reactivated.
01027 *  Create a redo item to reactivate the alarm back again.
01028 *  Reply = redo item.
01029 */
01030 UndoItem* UndoReactivate::restore()
01031 {
01032     kdDebug(5950) << "UndoReactivate::restore()\n";
01033     // Validate the alarm's calendar
01034     switch (calendar())
01035     {
01036         case KAEvent::ACTIVE:
01037             break;
01038         default:
01039             mRestoreError = ERR_PROG;
01040             return 0;
01041     }
01042     return UndoAdd::doRestore(true);     // restore alarm, ensuring that it is re-archived
01043 }
01044 
01045 /******************************************************************************
01046 *  Create a redo item to add the alarm back again.
01047 */
01048 UndoItem* UndoReactivate::createRedo(const KAEvent& event)
01049 {
01050     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
01051     return new UndoDeactivate(t, event);
01052 }
01053 
01054 /******************************************************************************
01055 *  Return the action description of the Undo item for displaying.
01056 */
01057 QString UndoReactivate::actionText() const
01058 {
01059     return i18n("Reactivate alarm");
01060 }
01061 
01062 
01063 /*=============================================================================
01064 =  Class: UndoDeactivate
01065 =  Redo item for alarm reactivation.
01066 =============================================================================*/
01067 
01068 /******************************************************************************
01069 *  Undo the item, i.e. reactivate an alarm which was archived.
01070 *  Create a redo item to archive the alarm again.
01071 *  Reply = redo item.
01072 */
01073 UndoItem* UndoDeactivate::restore()
01074 {
01075     kdDebug(5950) << "UndoDeactivate::restore()\n";
01076     // Validate the alarm's calendar
01077     switch (calendar())
01078     {
01079         case KAEvent::ACTIVE:
01080             break;
01081         default:
01082             mRestoreError = ERR_PROG;
01083             return 0;
01084     }
01085 
01086     return UndoDelete::restore();
01087 }
01088 
01089 /******************************************************************************
01090 *  Create a redo item to archive the alarm again.
01091 */
01092 UndoItem* UndoDeactivate::createRedo(const KAEvent& event)
01093 {
01094     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
01095     return new UndoReactivate(t, event);
01096 }
01097 
01098 /******************************************************************************
01099 *  Return the action description of the Undo item for displaying.
01100 */
01101 QString UndoDeactivate::actionText() const
01102 {
01103     return i18n("Reactivate alarm");
01104 }
01105 
01106 
01107 /*=============================================================================
01108 =  Class: UndoReactivates
01109 =  Undo item for multiple alarm reactivation.
01110 =============================================================================*/
01111 
01112 /******************************************************************************
01113 *  Create a redo item to reactivate the alarms again.
01114 */
01115 UndoItem* UndoReactivates::createRedo(Undo::List& undos)
01116 {
01117     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
01118     return new UndoReactivates(t, undos);
01119 }
01120 
01121 /******************************************************************************
01122 *  Return the action description of the Undo item for displaying.
01123 */
01124 QString UndoReactivates::actionText() const
01125 {
01126     return i18n("Reactivate multiple alarms");
01127 }
KDE Home | KDE Accessibility Home | Description of Access Keys