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_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::UpdateError 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_EXPIRED:    err = i18n("Cannot reactivate expired alarm");  break;
00356         case UndoItem::ERR_PROG:       err = i18n("Program error");  break;
00357         default:                       err = i18n("Unknown error");  break;
00358     }
00359     KMessageBox::sorry(parent, i18n("Undo-action: message", "%1: %2").arg(action).arg(err));
00360     return false;
00361 }
00362 
00363 /******************************************************************************
00364 *  Add an undo item to the start of one of the lists.
00365 */
00366 void Undo::add(UndoItem* item, bool undo)
00367 {
00368     if (item)
00369     {
00370         // Limit the number of items stored
00371         int undoCount = mUndoList.count();
00372         int redoCount = mRedoList.count();
00373         if (undoCount + redoCount >= maxCount - 1)
00374         {
00375             if (undoCount)
00376                 mUndoList.pop_back();
00377             else
00378                 mRedoList.pop_back();
00379         }
00380 
00381         // Append the new item
00382         List* list = undo ? &mUndoList : &mRedoList;
00383         list->prepend(item);
00384     }
00385 }
00386 
00387 /******************************************************************************
00388 *  Remove an undo item from one of the lists.
00389 */
00390 void Undo::remove(UndoItem* item, bool undo)
00391 {
00392     List* list = undo ? &mUndoList : &mRedoList;
00393     if (!list->isEmpty())
00394         list->remove(item);
00395 }
00396 
00397 /******************************************************************************
00398 *  Replace an undo item in one of the lists.
00399 */
00400 void Undo::replace(UndoItem* old, UndoItem* New)
00401 {
00402     Type type = old->type();
00403     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00404     if (!list)
00405         return;
00406     Iterator it = list->find(old);
00407     if (it != list->end())
00408     {
00409         New->setType(type);    // ensure the item points to the correct list
00410         *it = New;
00411         old->setType(NONE);    // mark the old item as no longer being in a list
00412     }
00413 }
00414 
00415 /******************************************************************************
00416 *  Return the action description of the latest undo/redo item.
00417 */
00418 QString Undo::actionText(Undo::Type type)
00419 {
00420     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00421     return (list && !list->isEmpty()) ? list->first()->actionText() : QString::null;
00422 }
00423 
00424 /******************************************************************************
00425 *  Return the action description of the undo/redo item with the specified ID.
00426 */
00427 QString Undo::actionText(Undo::Type type, int id)
00428 {
00429     UndoItem* undo = getItem(id, type);
00430     return undo ? undo->actionText() : QString::null;
00431 }
00432 
00433 /******************************************************************************
00434 *  Return the alarm description of the undo/redo item with the specified ID.
00435 */
00436 QString Undo::description(Undo::Type type, int id)
00437 {
00438     UndoItem* undo = getItem(id, type);
00439     return undo ? undo->description() : QString::null;
00440 }
00441 
00442 /******************************************************************************
00443 *  Return the descriptions of all undo or redo items, in order latest first.
00444 *  For alarms which have undergone more than one change, only the first one is
00445 *  listed, to force dependent undos to be executed in their correct order.
00446 *  If 'ids' is non-null, also returns a list of their corresponding IDs.
00447 */
00448 QValueList<int> Undo::ids(Undo::Type type)
00449 {
00450     QValueList<int> ids;
00451     QStringList ignoreIDs;
00452 //int n=0;
00453     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00454     if (!list)
00455         return ids;
00456     for (Iterator it = list->begin();  it != list->end();  ++it)
00457     {
00458         // Check whether this item should be ignored because it is a
00459         // deendent undo. If not, add this item's ID to the ignore list.
00460         UndoItem* item = *it;
00461         bool omit = false;
00462         if (item->operation() == UndoItem::MULTI)
00463         {
00464             // If any item in a multi-undo is disqualified, omit the whole multi-undo
00465             QStringList newIDs;
00466             const Undo::List& undos = ((UndoMultiBase*)item)->undos();
00467             for (Undo::List::ConstIterator u = undos.begin();  u != undos.end();  ++u)
00468             {
00469                 QString evid = (*u)->eventID();
00470                 if (ignoreIDs.find(evid) != ignoreIDs.end())
00471                     omit = true;
00472                 else if (omit)
00473                     ignoreIDs.append(evid);
00474                 else
00475                     newIDs.append(evid);
00476             }
00477             if (omit)
00478             {
00479                 for (QStringList::ConstIterator i = newIDs.begin();  i != newIDs.end();  ++i)
00480                     ignoreIDs.append(*i);
00481             }
00482         }
00483         else
00484         {
00485             omit = (ignoreIDs.find(item->eventID()) != ignoreIDs.end());
00486             if (!omit)
00487                 ignoreIDs.append(item->eventID());
00488             if (item->operation() == UndoItem::EDIT)
00489                 ignoreIDs.append(item->oldEventID());   // continue looking for its post-edit ID
00490         }
00491         if (!omit)
00492             ids.append(item->id());
00493 //else kdDebug(5950)<<"Undo::ids(): omit "<<item->actionText()<<": "<<item->description()<<endl;
00494     }
00495 //kdDebug(5950)<<"Undo::ids(): "<<n<<" -> "<<ids.count()<<endl;
00496     return ids;
00497 }
00498 
00499 /******************************************************************************
00500 *  Emit the appropriate 'changed' signal.
00501 */
00502 void Undo::emitChanged()
00503 {
00504     if (mInstance)
00505         mInstance->emitChanged(actionText(UNDO), actionText(REDO));
00506 }
00507 
00508 /******************************************************************************
00509 *  Return the item with the specified ID.
00510 */
00511 UndoItem* Undo::getItem(int id, Undo::Type type)
00512 {
00513     List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0;
00514     if (list)
00515     {
00516         for (Iterator it = list->begin();  it != list->end();  ++it)
00517         {
00518             if ((*it)->id() == id)
00519                 return *it;
00520         }
00521     }
00522     return 0;
00523 }
00524 
00525 /******************************************************************************
00526 *  Find an item with the specified ID.
00527 */
00528 Undo::Iterator Undo::findItem(int id, Undo::Type type)
00529 {
00530     List* list = (type == UNDO) ? &mUndoList : &mRedoList;
00531     Iterator it;
00532     for (it = list->begin();  it != list->end();  ++it)
00533     {
00534         if ((*it)->id() == id)
00535             break;
00536     }
00537     return it;
00538 }
00539 
00540 
00541 /*=============================================================================
00542 =  Class: UndoItem
00543 =  A single undo action.
00544 =============================================================================*/
00545 int               UndoItem::mLastId = 0;
00546 UndoItem::Error   UndoItem::mRestoreError;
00547 UndoItem::Warning UndoItem::mRestoreWarning;
00548 int               UndoItem::mRestoreWarningCount;
00549 
00550 /******************************************************************************
00551 *  Constructor.
00552 *  Optionally appends the undo to the list of undos.
00553 */
00554 UndoItem::UndoItem(Undo::Type type)
00555     : mId(0),
00556       mType(type)
00557 {
00558     if (type != Undo::NONE)
00559     {
00560         mId = ++mLastId;
00561         if (mId < 0)
00562             mId = mLastId = 1;    // wrap round if we reach a negative number
00563         Undo::add(this, (mType == Undo::UNDO));
00564     }
00565 }
00566 
00567 /******************************************************************************
00568 *  Destructor.
00569 *  Removes the undo from the list (if it's in the list).
00570 */
00571 UndoItem::~UndoItem()
00572 {
00573     if (mType != Undo::NONE)
00574         Undo::remove(this, (mType == Undo::UNDO));
00575 }
00576 
00577 /******************************************************************************
00578 *  Return the description of an event.
00579 */
00580 QString UndoItem::description(const KAEvent& event) const
00581 {
00582     return (mCalendar == KAEvent::TEMPLATE) ? event.templateName() : AlarmText::summary(event);
00583 }
00584 
00585 /******************************************************************************
00586 *  Return the action description of an add or delete Undo/Redo item for displaying.
00587 */
00588 QString UndoItem::addDeleteActionText(KAEvent::Status calendar, bool add)
00589 {
00590     switch (calendar)
00591     {
00592         case KAEvent::ACTIVE:
00593             if (add)
00594                 return i18n("Action to create a new alarm", "New alarm");
00595             else
00596                 return i18n("Action to delete an alarm", "Delete alarm");
00597         case KAEvent::TEMPLATE:
00598             if (add)
00599                 return i18n("Action to create a new alarm template", "New template");
00600             else
00601                 return i18n("Action to delete an alarm template", "Delete template");
00602         case KAEvent::EXPIRED:
00603             return i18n("Delete expired alarm");
00604         default:
00605             break;
00606     }
00607     return QString::null;
00608 }
00609 
00610 
00611 /*=============================================================================
00612 =  Class: UndoMultiBase
00613 =  Undo item for multiple alarms.
00614 =============================================================================*/
00615 
00616 template <class T>
00617 UndoMulti<T>::UndoMulti(Undo::Type type, const QValueList<KAEvent>& events)
00618     : UndoMultiBase(type)    // UNDO only
00619 {
00620     for (QValueList<KAEvent>::ConstIterator it = events.begin();  it != events.end();  ++it)
00621         mUndos.append(new T(Undo::NONE, *it));
00622 }
00623 
00624 UndoMultiBase::~UndoMultiBase()
00625 {
00626     for (Undo::List::Iterator it = mUndos.begin();  it != mUndos.end();  ++it)
00627         delete *it;
00628 }
00629 
00630 /******************************************************************************
00631 *  Undo the item, i.e. restore multiple alarms which were deleted (or delete
00632 *  alarms which were restored).
00633 *  Create a redo item to delete (or restore) the alarms again.
00634 *  Reply = redo item.
00635 */
00636 template <class T>
00637 UndoItem* UndoMulti<T>::restore()
00638 {
00639     Undo::List newUndos;
00640     for (Undo::List::Iterator it = mUndos.begin();  it != mUndos.end();  ++it)
00641     {
00642         UndoItem* undo = (*it)->restore();
00643         if (undo)
00644             newUndos.append(undo);
00645     }
00646     if (newUndos.isEmpty())
00647         return 0;
00648 
00649     // Create a redo item to delete the alarm again
00650     return createRedo(newUndos);
00651 }
00652 
00653 /******************************************************************************
00654 *  If one of the multiple items has the specified ID, delete it.
00655 *  If an item is deleted and there is only one item left, the UndoMulti
00656 *  instance is removed from its list and replaced by the remaining UndoItem instead.
00657 *  Reply = true if this instance was replaced. The caller must delete it.
00658 *        = false otherwise.
00659 */
00660 template <class T>
00661 bool UndoMulti<T>::deleteID(const QString& id)
00662 {
00663     for (Undo::List::Iterator it = mUndos.begin();  it != mUndos.end();  ++it)
00664     {
00665         UndoItem* item = *it;
00666         if (item->eventID() == id)
00667         {
00668             // Found a matching entry - remove it
00669             mUndos.remove(it);
00670             if (mUndos.count() == 1)
00671             {
00672                 // There is only one entry left after removal.
00673                 // Replace 'this' multi instance with the remaining single entry.
00674                 replaceWith(item);
00675                 return true;
00676             }
00677             else
00678             {
00679                 delete item;
00680                 return false;
00681             }
00682         }
00683     }
00684     return false;
00685 }
00686 
00687 
00688 /*=============================================================================
00689 =  Class: UndoAdd
00690 =  Undo item for alarm creation.
00691 =============================================================================*/
00692 
00693 UndoAdd::UndoAdd(Undo::Type type, const KAEvent& event)
00694     : UndoItem(type),
00695       mEventID(event.id())
00696 {
00697     setCalendar(KAEvent::uidStatus(mEventID));
00698     mDescription = UndoItem::description(event);    // calendar must be set before calling this
00699 }
00700 
00701 UndoAdd::UndoAdd(Undo::Type type, const KAEvent& event, KAEvent::Status cal)
00702     : UndoItem(type),
00703       mEventID(KAEvent::uid(event.id(), cal))
00704 {
00705     setCalendar(cal);
00706     mDescription = UndoItem::description(event);    // calendar must be set before calling this
00707 }
00708 
00709 /******************************************************************************
00710 *  Undo the item, i.e. delete the alarm which was added.
00711 *  Create a redo item to add the alarm back again.
00712 *  Reply = redo item.
00713 */
00714 UndoItem* UndoAdd::doRestore(bool setArchive)
00715 {
00716     // Retrieve the current state of the alarm
00717     kdDebug(5950) << "UndoAdd::doRestore(" << mEventID << ")\n";
00718     const KCal::Event* kcalEvent = AlarmCalendar::getEvent(mEventID);
00719     if (!kcalEvent)
00720     {
00721         mRestoreError = ERR_NOT_FOUND;    // alarm is no longer in calendar
00722         return 0;
00723     }
00724     KAEvent event(*kcalEvent); 
00725 
00726     // Create a redo item to recreate the alarm.
00727     // Do it now, since 'event' gets modified by KAlarm::deleteEvent()
00728     UndoItem* undo = createRedo(event);
00729 
00730     switch (calendar())
00731     {
00732         case KAEvent::ACTIVE:
00733             if (setArchive)
00734                 event.setArchive();
00735             // Archive it if it has already triggered
00736             if (KAlarm::deleteEvent(event, true) == KAlarm::UPDATE_KORG_ERR)
00737             {
00738                 mRestoreWarning = WARN_KORG_DELETE;
00739                 ++mRestoreWarningCount;
00740             }
00741             break;
00742         case KAEvent::TEMPLATE:
00743             KAlarm::deleteTemplate(event);
00744             break;
00745         case KAEvent::EXPIRED:    // redoing the deletion of an expired alarm
00746             KAlarm::deleteEvent(event);
00747             break;
00748         default:
00749             delete undo;
00750             mRestoreError = ERR_PROG;
00751             return 0;
00752     }
00753     return undo;
00754 }
00755 
00756 /******************************************************************************
00757 *  Create a redo item to add the alarm back again.
00758 */
00759 UndoItem* UndoAdd::createRedo(const KAEvent& event)
00760 {
00761     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00762     return new UndoDelete(t, event);
00763 }
00764 
00765 /******************************************************************************
00766 *  Return the action description of the Undo item for displaying.
00767 */
00768 QString UndoAdd::actionText() const
00769 {
00770     return addDeleteActionText(calendar(), (type() == Undo::UNDO));
00771 }
00772 
00773 
00774 /*=============================================================================
00775 =  Class: UndoEdit
00776 =  Undo item for alarm edit.
00777 =============================================================================*/
00778 
00779 UndoEdit::UndoEdit(Undo::Type type, const KAEvent& oldEvent, const QString& newEventID, const QString& description)
00780     : UndoItem(type),
00781       mOldEvent(new KAEvent(oldEvent)),
00782       mNewEventID(newEventID),
00783       mDescription(description)
00784 {
00785     setCalendar(KAEvent::uidStatus(mNewEventID));
00786 }
00787 
00788 UndoEdit::~UndoEdit()
00789 {
00790     delete mOldEvent;
00791 }
00792 
00793 /******************************************************************************
00794 *  Undo the item, i.e. undo an edit to a previously existing alarm.
00795 *  Create a redo item to reapply the edit.
00796 *  Reply = redo item.
00797 */
00798 UndoItem* UndoEdit::restore()
00799 {
00800     kdDebug(5950) << "UndoEdit::restore(" << mNewEventID << ")\n";
00801     // Retrieve the current state of the alarm
00802     const KCal::Event* kcalEvent = AlarmCalendar::getEvent(mNewEventID);
00803     if (!kcalEvent)
00804     {
00805         mRestoreError = ERR_NOT_FOUND;    // alarm is no longer in calendar
00806         return 0;
00807     }
00808     KAEvent newEvent(*kcalEvent); 
00809 
00810     // Create a redo item to restore the edit
00811     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00812     UndoItem* undo = new UndoEdit(t, newEvent, mOldEvent->id(), mDescription);
00813 
00814     switch (calendar())
00815     {
00816         case KAEvent::ACTIVE:
00817             if (KAlarm::modifyEvent(newEvent, *mOldEvent, 0) == KAlarm::UPDATE_KORG_ERR)
00818             {
00819                 mRestoreWarning = WARN_KORG_MODIFY;
00820                 ++mRestoreWarningCount;
00821             }
00822             break;
00823         case KAEvent::TEMPLATE:
00824             KAlarm::updateTemplate(*mOldEvent, 0);
00825             break;
00826         case KAEvent::EXPIRED:    // editing of expired events is not allowed
00827         default:
00828             delete undo;
00829             mRestoreError = ERR_PROG;
00830             return 0;
00831     }
00832     return undo;
00833 }
00834 
00835 /******************************************************************************
00836 *  Return the action description of the Undo item for displaying.
00837 */
00838 QString UndoEdit::actionText() const
00839 {
00840     switch (calendar())
00841     {
00842         case KAEvent::ACTIVE:
00843             return i18n("Action to edit an alarm", "Edit alarm");
00844         case KAEvent::TEMPLATE:
00845             return i18n("Action to edit an alarm template", "Edit template");
00846         default:
00847             break;
00848     }
00849     return QString::null;
00850 }
00851 
00852 
00853 /*=============================================================================
00854 =  Class: UndoDelete
00855 =  Undo item for alarm deletion.
00856 =============================================================================*/
00857 
00858 UndoDelete::UndoDelete(Undo::Type type, const KAEvent& event)
00859     : UndoItem(type),
00860       mEvent(new KAEvent(event))
00861 {
00862     setCalendar(KAEvent::uidStatus(mEvent->id()));
00863 }
00864 
00865 UndoDelete::~UndoDelete()
00866 {
00867     delete mEvent;
00868 }
00869 
00870 /******************************************************************************
00871 *  Undo the item, i.e. restore an alarm which was deleted.
00872 *  Create a redo item to delete the alarm again.
00873 *  Reply = redo item.
00874 */
00875 UndoItem* UndoDelete::restore()
00876 {
00877     kdDebug(5950) << "UndoDelete::restore(" << mEvent->id() << ")\n";
00878     // Restore the original event
00879     switch (calendar())
00880     {
00881         case KAEvent::ACTIVE:
00882             if (mEvent->toBeArchived())
00883             {
00884                 // It was archived when it was deleted
00885                 mEvent->setUid(KAEvent::EXPIRED);
00886                 switch (KAlarm::reactivateEvent(*mEvent, 0, true))
00887                 {
00888                     case KAlarm::UPDATE_KORG_ERR:
00889                         mRestoreWarning = WARN_KORG_ADD;
00890                         ++mRestoreWarningCount;
00891                         break;
00892                     case KAlarm::UPDATE_ERROR:
00893                         mRestoreError = ERR_EXPIRED;
00894                         return 0;
00895                     case KAlarm::UPDATE_OK:
00896                         break;
00897                 }
00898             }
00899             else
00900             {
00901                 switch (KAlarm::addEvent(*mEvent, 0, true))
00902                 {
00903                     case KAlarm::UPDATE_KORG_ERR:
00904                         mRestoreWarning = WARN_KORG_ADD;
00905                         ++mRestoreWarningCount;
00906                         break;
00907                     case KAlarm::UPDATE_ERROR:
00908                         mRestoreError = ERR_CREATE;
00909                         return 0;
00910                     case KAlarm::UPDATE_OK:
00911                         break;
00912                 }
00913             }
00914             break;
00915         case KAEvent::TEMPLATE:
00916             if (!KAlarm::addTemplate(*mEvent, 0))
00917             {
00918                 mRestoreError = ERR_CREATE;
00919                 return 0;
00920             }
00921             break;
00922         case KAEvent::EXPIRED:
00923             if (!KAlarm::addExpiredEvent(*mEvent))
00924             {
00925                 mRestoreError = ERR_CREATE;
00926                 return 0;
00927             }
00928             break;
00929         default:
00930             mRestoreError = ERR_PROG;
00931             return 0;
00932     }
00933 
00934     // Create a redo item to delete the alarm again
00935     return createRedo(*mEvent);
00936 }
00937 
00938 /******************************************************************************
00939 *  Create a redo item to archive the alarm again.
00940 */
00941 UndoItem* UndoDelete::createRedo(const KAEvent& event)
00942 {
00943     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00944     return new UndoAdd(t, event);
00945 }
00946 
00947 /******************************************************************************
00948 *  Return the action description of the Undo item for displaying.
00949 */
00950 QString UndoDelete::actionText() const
00951 {
00952     return addDeleteActionText(calendar(), (type() == Undo::REDO));
00953 }
00954 
00955 
00956 /*=============================================================================
00957 =  Class: UndoDeletes
00958 =  Undo item for multiple alarm deletion.
00959 =============================================================================*/
00960 
00961 /******************************************************************************
00962 *  Create a redo item to delete the alarms again.
00963 */
00964 UndoItem* UndoDeletes::createRedo(Undo::List& undos)
00965 {
00966     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
00967     return new UndoDeletes(t, undos);
00968 }
00969 
00970 /******************************************************************************
00971 *  Return the action description of the Undo item for displaying.
00972 */
00973 QString UndoDeletes::actionText() const
00974 {
00975     if (mUndos.isEmpty())
00976         return QString::null;
00977     for (Undo::List::ConstIterator it = mUndos.begin();  it != mUndos.end();  ++it)
00978     {
00979         switch ((*it)->calendar())
00980         {
00981             case KAEvent::ACTIVE:
00982                 return i18n("Delete multiple alarms");
00983             case KAEvent::TEMPLATE:
00984                 return i18n("Delete multiple templates");
00985             case KAEvent::EXPIRED:
00986                 break;    // check if they are ALL expired
00987             default:
00988                 return QString::null;
00989         }
00990     }
00991     return i18n("Delete multiple expired alarms");
00992 }
00993 
00994 
00995 /*=============================================================================
00996 =  Class: UndoReactivate
00997 =  Undo item for alarm reactivation.
00998 =============================================================================*/
00999 
01000 /******************************************************************************
01001 *  Undo the item, i.e. re-archive the alarm which was reactivated.
01002 *  Create a redo item to reactivate the alarm back again.
01003 *  Reply = redo item.
01004 */
01005 UndoItem* UndoReactivate::restore()
01006 {
01007     kdDebug(5950) << "UndoReactivate::restore()\n";
01008     // Validate the alarm's calendar
01009     switch (calendar())
01010     {
01011         case KAEvent::ACTIVE:
01012             break;
01013         default:
01014             mRestoreError = ERR_PROG;
01015             return 0;
01016     }
01017     return UndoAdd::doRestore(true);     // restore alarm, ensuring that it is re-archived
01018 }
01019 
01020 /******************************************************************************
01021 *  Create a redo item to add the alarm back again.
01022 */
01023 UndoItem* UndoReactivate::createRedo(const KAEvent& event)
01024 {
01025     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
01026     return new UndoDeactivate(t, event);
01027 }
01028 
01029 /******************************************************************************
01030 *  Return the action description of the Undo item for displaying.
01031 */
01032 QString UndoReactivate::actionText() const
01033 {
01034     return i18n("Reactivate alarm");
01035 }
01036 
01037 
01038 /*=============================================================================
01039 =  Class: UndoDeactivate
01040 =  Redo item for alarm reactivation.
01041 =============================================================================*/
01042 
01043 /******************************************************************************
01044 *  Undo the item, i.e. reactivate an alarm which was archived.
01045 *  Create a redo item to archive the alarm again.
01046 *  Reply = redo item.
01047 */
01048 UndoItem* UndoDeactivate::restore()
01049 {
01050     kdDebug(5950) << "UndoDeactivate::restore()\n";
01051     // Validate the alarm's calendar
01052     switch (calendar())
01053     {
01054         case KAEvent::ACTIVE:
01055             break;
01056         default:
01057             mRestoreError = ERR_PROG;
01058             return 0;
01059     }
01060 
01061     return UndoDelete::restore();
01062 }
01063 
01064 /******************************************************************************
01065 *  Create a redo item to archive the alarm again.
01066 */
01067 UndoItem* UndoDeactivate::createRedo(const KAEvent& event)
01068 {
01069     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
01070     return new UndoReactivate(t, event);
01071 }
01072 
01073 /******************************************************************************
01074 *  Return the action description of the Undo item for displaying.
01075 */
01076 QString UndoDeactivate::actionText() const
01077 {
01078     return i18n("Reactivate alarm");
01079 }
01080 
01081 
01082 /*=============================================================================
01083 =  Class: UndoReactivates
01084 =  Undo item for multiple alarm reactivation.
01085 =============================================================================*/
01086 
01087 /******************************************************************************
01088 *  Create a redo item to reactivate the alarms again.
01089 */
01090 UndoItem* UndoReactivates::createRedo(Undo::List& undos)
01091 {
01092     Undo::Type t = (type() == Undo::UNDO) ? Undo::REDO : (type() == Undo::REDO) ? Undo::UNDO : Undo::NONE;
01093     return new UndoReactivates(t, undos);
01094 }
01095 
01096 /******************************************************************************
01097 *  Return the action description of the Undo item for displaying.
01098 */
01099 QString UndoReactivates::actionText() const
01100 {
01101     return i18n("Reactivate multiple alarms");
01102 }
KDE Home | KDE Accessibility Home | Description of Access Keys