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 <tqobject.h> 00024 #include <tqstringlist.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 TQValueList 00045 virtual ~UndoItem(); 00046 virtual Operation operation() const = 0; 00047 virtual TQString actionText() const = 0; 00048 virtual TQString description() const { return TQString(); } 00049 virtual TQString eventID() const { return TQString(); } 00050 virtual TQString oldEventID() const { return TQString(); } 00051 virtual TQString newEventID() const { return TQString(); } 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 TQString& /*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 TQString addDeleteActionText(KAEvent::Status, bool add); 00070 TQString 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 TQValueList<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 TQString& 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 TQString actionText() const; 00107 virtual TQString description() const { return mDescription; } 00108 virtual TQString eventID() const { return mEventID; } 00109 virtual TQString 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 TQString mEventID; 00116 TQString mDescription; 00117 }; 00118 00119 class UndoEdit : public UndoItem 00120 { 00121 public: 00122 UndoEdit(Undo::Type, const KAEvent& oldEvent, const TQString& newEventID, const TQString& description); 00123 ~UndoEdit(); 00124 virtual Operation operation() const { return EDIT; } 00125 virtual TQString actionText() const; 00126 virtual TQString description() const { return mDescription; } 00127 virtual TQString eventID() const { return mNewEventID; } 00128 virtual TQString oldEventID() const { return mOldEvent->id(); } 00129 virtual TQString newEventID() const { return mNewEventID; } 00130 virtual UndoItem* restore(); 00131 private: 00132 KAEvent* mOldEvent; 00133 TQString mNewEventID; 00134 TQString 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 TQString actionText() const; 00144 virtual TQString description() const { return UndoItem::description(*mEvent); } 00145 virtual TQString eventID() const { return mEvent->id(); } 00146 virtual TQString 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 TQString 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 TQString 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 TQValueList<KAEvent>& events) 00181 : UndoMulti<UndoDelete>(t, events) { } // UNDO only 00182 UndoDeletes(Undo::Type t, Undo::List& undos) 00183 : UndoMulti<UndoDelete>(t, undos) { } 00184 virtual TQString 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 TQValueList<KAEvent>& events) 00192 : UndoMulti<UndoReactivate>(t, events) { } // UNDO only 00193 UndoReactivates(Undo::Type t, Undo::List& undos) 00194 : UndoMulti<UndoReactivate>(t, undos) { } 00195 virtual TQString 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(TQT_TQOBJECT(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 TQValueList<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 (TQValueList<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 TQValueList<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 TQString& eventID) 00291 { 00292 TQString 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, TQWidget* parent, const TQString& 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 TQString 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 TQString Undo::actionText(Undo::Type type) 00420 { 00421 List* list = (type == UNDO) ? &mUndoList : (type == REDO) ? &mRedoList : 0; 00422 return (list && !list->isEmpty()) ? list->first()->actionText() : TQString(); 00423 } 00424 00425 /****************************************************************************** 00426 * Return the action description of the undo/redo item with the specified ID. 00427 */ 00428 TQString Undo::actionText(Undo::Type type, int id) 00429 { 00430 UndoItem* undo = getItem(id, type); 00431 return undo ? undo->actionText() : TQString(); 00432 } 00433 00434 /****************************************************************************** 00435 * Return the alarm description of the undo/redo item with the specified ID. 00436 */ 00437 TQString Undo::description(Undo::Type type, int id) 00438 { 00439 UndoItem* undo = getItem(id, type); 00440 return undo ? undo->description() : TQString(); 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 TQValueList<int> Undo::ids(Undo::Type type) 00450 { 00451 TQValueList<int> ids; 00452 TQStringList 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 TQStringList newIDs; 00467 const Undo::List& undos = ((UndoMultiBase*)item)->undos(); 00468 for (Undo::List::ConstIterator u = undos.begin(); u != undos.end(); ++u) 00469 { 00470 TQString 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 (TQStringList::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 TQString 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 TQString 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 TQString(); 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 TQValueList<KAEvent>& events) 00619 : UndoMultiBase(type) // UNDO only 00620 { 00621 for (TQValueList<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 TQString& 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 TQString 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 TQString& newEventID, const TQString& 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 TQString 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 TQString(); 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 TQString 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 TQString UndoDeletes::actionText() const 00999 { 01000 if (mUndos.isEmpty()) 01001 return TQString(); 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 TQString(); 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 TQString 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 TQString 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 TQString UndoReactivates::actionText() const 01125 { 01126 return i18n("Reactivate multiple alarms"); 01127 }