kalarm

undo.cpp

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