123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788 |
- /* GCSx
- ** UNDO.CPP
- **
- ** Editor undo and redo functionality
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** This program is free software; you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation; either version 2 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program; if not, write to the Free Software
- ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- int UndoBuffer::undoHeaderSize = (sizeof(UndoBuffer::UndoHeader) + sizeof(Uint32) - 1) / sizeof(Uint32); // Size in Uint32s
- #ifndef NDEBUG
- int UndoBuffer::undoThresh = -1;
- void UndoBuffer::setUndoThreshold(int level) { start_func
- if (level) undoThresh = level - 1;
- else undoThresh = -1;
- }
- #endif
- UndoBuffer::UndoBuffer(WorldEdit* myWorld) { start_func
- world = myWorld;
- requestedSize = UNDO_DEFAULT_BUFFER;
- undoSizeAlloc = 0;
- undoBuffer = NULL;
- undoPos = 0;
- inUndo = 0;
- suspendUndo = 0;
- undoBlock = 0;
- undoBlockDidFirst = 0;
- undoBlockFirstPos = 0;
- undoNonUndoable = 0;
- }
- UndoBuffer::~UndoBuffer() { start_func
- // Delete allocated objects etc.
- clearUndo();
- }
- void UndoBuffer::disableUndo() { start_func
- suspendUndo = 1;
- }
- void UndoBuffer::enableUndo() { start_func
- suspendUndo = 0;
- }
- int UndoBuffer::bufferSize(int requested) { start_func
- // Check range
- if (requested < 0) requested = 0;
- if (requested > UNDO_MAX_BUFFER) requested = UNDO_MAX_BUFFER;
- if (requested != requestedSize) {
- clearUndo();
- requestedSize = requested;
- }
-
- return requestedSize;
- }
- Uint32* UndoBuffer::createUndoSpace(UndoType chunkType, int id, int subid, int xPos, int yPos, int xSize, int ySize, Window* srcWin, Uint32 data1, Uint32 data2, Uint32 data3, void* item1, void* item2, void* item3) throw_Undo { start_func
- Uint32* result;
- UndoHeader* header;
-
- // If in an undo, "cancel" any further undos by returning NULL
- if (inUndo) return NULL;
-
- // If in a block, cancel further undos if user was already warned
- if ((undoBlock) && (undoNonUndoable)) return NULL;
- // Cancel if undo has been explicitly disabled
- if (!requestedSize) return NULL;
- if (suspendUndo) {
- clearUndo();
- return NULL;
- }
-
- #ifndef NDEBUG
- // Threshold enabled?
- if (undoThresh == 0) {
- warnUser();
- return NULL;
- }
- if (undoThresh > 0) --undoThresh;
- #endif
- if (undoBuffer == NULL) {
- // 256 = number of Uint32s in 1k
- undoBuffer = new(nothrow) Uint32[requestedSize * 256];
-
- if (undoBuffer == NULL) {
- warnUser();
- return NULL;
- }
-
- undoSizeAlloc = requestedSize * 256;
- undoPos = 0;
- header = (UndoHeader*)undoBuffer;
- header->sourceWindow = 0;
- header->prevChunkSize = 0;
- header->type = UNDO_EMPTY;
- header->id = 0;
- header->xSize = 0;
- header->ySize = 0;
- }
-
- // Too big?
- if (xSize * ySize + undoHeaderSize * 2 >= undoSizeAlloc) {
- warnUser();
- return NULL;
- }
-
- // Too big to fit with the rest of our merged data?
- if ((undoBlock) && (undoBlockDidFirst) && (xSize * ySize + undoHeaderSize * 2 + undoPos - undoBlockFirstPos >= undoSizeAlloc)) {
- warnUser();
- return NULL;
- }
-
- // If we add at undoPos, we need to fit there the entire new chunk,
- // plus header, plus header of empty chunk; find out how much needs to be deleted
- int deletingAt = 0;
- while (xSize * ySize + undoHeaderSize * 2 + undoPos - deletingAt > undoSizeAlloc) {
- int isMerged = 1;
- // Delete any merged chunks as well
- while (isMerged) {
- // Delete first item in list, retry
- header = (UndoHeader*)(undoBuffer + deletingAt);
- deleteUndoBlock(deletingAt);
- // Move down to delete initial chunk
- deletingAt += header->xSize * header->ySize + undoHeaderSize;
- // New initial chunk's "previous size" is now 0
- header = (UndoHeader*)(undoBuffer + deletingAt);
- header->prevChunkSize = 0;
- // Is next block merged with the one we just did?
- isMerged = header->type & UNDO_MERGE;
- }
- }
-
- // Move data to finalize deletion
- if (deletingAt) {
- memmove(undoBuffer, undoBuffer + deletingAt, (undoPos + undoHeaderSize - deletingAt) * 4);
- undoPos -= deletingAt;
- }
-
- // Delete any redo chunks here or later
- clearRedo();
-
- // There's now room for the new chunk- create it.
- // Current 'blank' or redo chunk at undoPos already has proper "previous size" value
- // This also overwrites any existing redo- no redo allowed after an action
- result = undoBuffer + undoPos + undoHeaderSize;
- header = (UndoHeader*)(undoBuffer + undoPos);
- header->sourceWindow = srcWin ? srcWin->getId() : 0;
- header->type = chunkType;
- // (part of a block?)
- if (undoBlock) {
- if (undoBlockDidFirst) header->type = (UndoType)(header->type | UNDO_MERGE);
- else {
- undoBlockFirstPos = undoPos;
- undoBlockDidFirst = 1;
- }
- }
- header->id = id;
- header->subid = subid;
- header->xSize = xSize;
- header->ySize = ySize;
- header->xPos = xPos;
- header->yPos = yPos;
- header->data1 = data1;
- header->data2 = data2;
- header->data3 = data3;
- header->item1 = item1;
- header->item2 = item2;
- header->item3 = item3;
-
- // Create new 'blank' chunk
- undoPos += xSize * ySize + undoHeaderSize;
- header = (UndoHeader*)(undoBuffer + undoPos);
- header->sourceWindow = 0;
- header->prevChunkSize = xSize * ySize + undoHeaderSize;
- header->type = UNDO_EMPTY;
- header->id = 0;
- header->xSize = 0;
- header->ySize = 0;
- // Done
- return result;
- }
- void UndoBuffer::preUndoBlock() { start_func
- if ((inUndo) || (suspendUndo)) return;
- if (!undoBlock) {
- undoBlockDidFirst = 0;
- undoBlockFirstPos = 0;
- undoNonUndoable = 0;
- }
- ++undoBlock;
- debugWrite(DEBUG_UNDO, "undo: pre-block (%d)", undoBlock);
- }
- void UndoBuffer::postUndoBlock() { start_func
- if ((inUndo) || (suspendUndo)) return;
- // Could already be at zero if blocks were nested and the inner one
- // threw an exception
- if (undoBlock) {
- debugWrite(DEBUG_UNDO, "undo: post-block (%d)", undoBlock);
- --undoBlock;
- }
- }
- void UndoBuffer::cancelLastUndo() { start_func
- UndoHeader* header = (UndoHeader*)(undoBuffer + undoPos);
- header->type = (UndoType)(header->type & ~UNDO_MERGE);
- undoPos -= header->prevChunkSize;
- clearRedo();
- }
- void UndoBuffer::cancelUndoBlock() { start_func
- // Must be in a block
- if (undoBlock) {
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: cancel block (%d)", undoBlock);
- // (no longer in a block)
- undoBlock = 0;
- if (undoBlockDidFirst) {
- undoPerform();
- clearRedo();
- }
- }
- }
- void UndoBuffer::storeUndoLayerTile(int layerId, int x, int y, int w, int h, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer tiles");
- // Find layer
- LayerEdit* layer = world->findLayer(layerId);
- assert(layer);
- assert(layer->getType() == Layer::LAYER_TILE);
-
- int layerWidth = layer->getXSize();
- int layerHeight = layer->getYSize();
- // Bound
- if ((x >= layerWidth) || (y >= layerHeight) || (x + w <= 0) || (y + h <= 0)) return;
- if (x < 0) {
- w += x;
- x = 0;
- }
- if (y < 0) {
- h += y;
- y = 0;
- }
- if (x + w > layerWidth) w = layerWidth - x;
- if (y + h > layerHeight) h = layerHeight - y;
-
- // Fx/Coll present?
- int ext = layer->getUsesExt();
- int fx = layer->getUsesFx();
- if ((fx) || (ext)) preUndoBlock();
- Uint32* bufferExt = NULL;
- Uint32* bufferFx = NULL;
- Uint32* buffer = createUndoSpace(UNDO_LAYERTILE, layerId, 0, x, y, w, h, srcWin);
- if (!buffer) {
- if ((fx) || (ext)) postUndoBlock();
- return;
- }
- if (ext) {
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer extended data");
- bufferExt = createUndoSpace(UNDO_LAYERTILEEXT, layerId, 0, x, y, w, h, srcWin);
- if (!bufferExt) {
- postUndoBlock();
- return;
- }
- }
- if (fx) {
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer effects");
- bufferFx = createUndoSpace(UNDO_LAYERTILEFX, layerId, 0, x, y, w, h, srcWin);
- if (!bufferFx) {
- postUndoBlock();
- return;
- }
- }
-
- Rect rect = { x, y, w, h };
- layer->loadLayer(buffer, bufferExt, bufferFx, w, rect);
- if ((fx) || (ext)) postUndoBlock();
- }
- void UndoBuffer::storeUndoTile(int tileSetId, int tileNum, int x, int y, int w, int h, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store tile");
- // Find tileset
- TileSetEdit* tiles = dynamic_cast<TileSetEdit*>(world->findTileSet(tileSetId));
- assert(tiles);
- int tileWidth = tiles->getWidth();
- int tileHeight = tiles->getHeight();
- // Bound
- if ((x >= tileWidth) || (y >= tileHeight) || (x + w <= 0) || (y + h <= 0)) return;
- if (x < 0) {
- w += x;
- x = 0;
- }
- if (y < 0) {
- h += y;
- y = 0;
- }
- if (x + w > tileWidth) w = tileWidth - x;
- if (y + h > tileHeight) h = tileHeight - y;
- Uint32* buffer;
- buffer = createUndoSpace(UNDO_TILE, tileSetId, tileNum, x, y, w, h, srcWin);
- if (!buffer) return;
- int pitch;
- const Uint8* source;
- int lineSize = w * 4;
- pitch = tiles->viewTilePitch();
- source = tiles->viewTileData(tileNum) + y * pitch + x * 4;
- // @TODO: matrixCopy()
- for (; h > 0; --h) {
- memcpy(buffer, source, lineSize);
- buffer += w;
- source += pitch;
- }
- }
- void UndoBuffer::storeUndoColl(int tileSetId, int collNum, int x, int y, int w, int h, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store collision map");
- // Find tileset
- TileSetEdit* tiles = dynamic_cast<TileSetEdit*>(world->findTileSet(tileSetId));
- assert(tiles);
- int tileWidth = tiles->getWidth();
- int tileHeight = tiles->getHeight();
- // Bound
- if ((x >= tileWidth) || (y >= tileHeight) || (x + w <= 0) || (y + h <= 0)) return;
- if (x < 0) {
- w += x;
- x = 0;
- }
- if (y < 0) {
- h += y;
- y = 0;
- }
- if (x + w > tileWidth) w = tileWidth - x;
- if (y + h > tileHeight) h = tileHeight - y;
-
- // Everything is in chunks 32 wide
- if (x & 31) {
- w += x & 31;
- x -= x & 31;
- }
- x /= 32;
- w = (w + 31) / 32;
- Uint32* buffer;
- buffer = createUndoSpace(UNDO_TILECOLL, tileSetId, collNum, x, y, w, h, srcWin);
- if (!buffer) return;
- const Uint32* source;
- source = tiles->viewCollData(collNum) + w * tileHeight + y;
- // @TODO: matrixCopy()
- for (; w > 0; --w) {
- memcpy(buffer, source, h);
- buffer += h;
- source += tileHeight;
- }
- }
- void UndoBuffer::storeUndoLayerSwap(int sceneId, int layer1, int layer2, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer swap");
- createUndoSpace(UNDO_LAYERMOVE, sceneId, 0, layer1, layer2, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoTileGlyph(int tileSetId, int tileNum, int oldWidth, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store tile glyph");
- createUndoSpace(UNDO_TILEGLYPH, tileSetId, tileNum, oldWidth, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoLayerType(int layerId, int oldType, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer type");
- createUndoSpace(UNDO_LAYERTYPE, layerId, 0, oldType, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoLayerSize(int layerId, int oldWidth, int oldHeight, Uint32* data, Uint32* dataExt, Uint32* dataFx, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer size");
- // If undo space not created, we delete saved buffers now, unless we're "in undo"
- if ((!createUndoSpace(UNDO_LAYERSIZE, layerId, 0, oldWidth, oldHeight, 0, 0, srcWin, 0, 0, 0, data, dataExt, dataFx)) && (!inUndo)) {
- delete[] data;
- delete[] dataExt;
- delete[] dataFx;
- }
- }
- void UndoBuffer::storeUndoLayerFx(int layerId, int oldUsesFx, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer effects flag");
- createUndoSpace(UNDO_LAYERFX, layerId, 0, oldUsesFx, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoLayerExt(int layerId, int oldUsesExt, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer extended data flag");
- createUndoSpace(UNDO_LAYEREXT, layerId, 0, oldUsesExt, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoLayerTileSet(int layerId, int oldTileSet, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer tileset");
- createUndoSpace(UNDO_LAYERTILESET, layerId, 0, oldTileSet, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoLayerSpawnSelection(set<int>* selectionData, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store spawn selection");
- assert(selectionData);
-
- set<int>* newData = new set<int>(*selectionData);
- try {
- Uint32* buffer = createUndoSpace(UNDO_LAYERSPAWNSEL, 0, 0, 0, 0, 0, 0, srcWin, 0, 0, 0, selectionData, newData);
- if (!buffer) {
- delete newData;
- return;
- }
- }
- catch (...) {
- delete newData;
- throw;
- }
- }
- void UndoBuffer::storeUndoLayerTileSelection(Uint8** selectionData, int x, int y, int w, int h, int pitch, int selectXOffs, int selectYOffs, int selectType, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer selection");
- assert(selectionData);
- assert((x + w) / 8 <= pitch);
- // place x in byte terms, round down, alter width to cover
- w += x % 8;
- x = x / 8;
-
- // break width into byte width and 32bit width
- int bytewidth = w / 8;
- if (w % 8) ++bytewidth;
-
- int width32 = w / 32;
- if (w % 32) ++width32;
- Uint32* buffer = createUndoSpace(UNDO_LAYERTILESEL, bytewidth, pitch, x, y, width32, h, srcWin, selectXOffs, selectYOffs, selectType, selectionData);
- if (!buffer) return;
- matrixCopy(*selectionData + x + y * pitch, buffer, bytewidth, h, pitch, width32 * 4);
- }
- void UndoBuffer::storeUndoLayerTileCur(Uint32 layerAffect, Uint32 layerView, Uint32 layerDim, Window* srcWin) throw_Undo { start_func
- createUndoSpace(UNDO_LAYERTILECUR, 0, 0, 0, 0, 0, 0, srcWin, layerAffect, layerView, layerDim);
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer affected/view");
- }
- void UndoBuffer::storeUndoLayerTileTemp(Uint32** data, Uint32** dataExt, Uint32** dataFx, int x, int y, int w, int h, int pitch, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer floating selection");
- assert(data);
- assert(x + w <= pitch);
-
- // NULL ptr at *dataFx also means no Fx data; same for ext
- if (dataExt) {
- if (!*dataExt) dataExt = NULL;
- }
- if (dataFx) {
- if (!*dataFx) dataFx = NULL;
- }
- Uint32* buffer = createUndoSpace(UNDO_LAYERTILETMP, 0, pitch, x, y, w, h * (1 + (dataFx ? 1 : 0) + (dataExt ? 1 : 0)), srcWin, 0, 0, 0, data, dataExt, dataFx);
- if (!buffer) return;
- matrixCopy(*data + x + y * pitch, buffer, w * 4, h, pitch * 4, w * 4);
- if (dataExt) matrixCopy(*dataExt + x + y * pitch, buffer + w * h, w * 4, h, pitch * 4, w * 4);
- if (dataFx) matrixCopy(*dataFx + x + y * pitch, buffer + (w * h) * (dataExt ? 2 : 1), w * 4, h, pitch * 4, w * 4);
- }
- void UndoBuffer::storeUndoPaintSelection(SDL_Surface** selectionData, int x, int y, int w, int h, int selectXOffs, int selectYOffs, int selectType, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store tile selection");
- assert(selectionData);
- int tileWidth = (*selectionData)->w;
- int tileHeight = (*selectionData)->h;
- // Bound
- if ((x >= tileWidth) || (y >= tileHeight) || (x + w <= 0) || (y + h <= 0)) return;
- if (x < 0) {
- w += x;
- x = 0;
- }
- if (y < 0) {
- h += y;
- y = 0;
- }
- if (x + w > tileWidth) w = tileWidth - x;
- if (y + h > tileHeight) h = tileHeight - y;
- Uint32* buffer = createUndoSpace(UNDO_PAINTSEL, 0, 0, x, y, w, h, srcWin, selectXOffs, selectYOffs, selectType, selectionData);
- if (!buffer) return;
-
- int pitch = (*selectionData)->pitch;
- const Uint8* source = (Uint8*)((*selectionData)->pixels) + y * pitch + x * 4;
- int lineSize = w * 4;
-
- // @TODO: matrixCopy()
- for (; h > 0; --h) {
- memcpy(buffer, source, lineSize);
- buffer += w;
- source += pitch;
- }
- }
- void UndoBuffer::storeUndoName(UndoType type, int id, const string& oldName, const string& newName, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store name");
- assert((type == UNDO_FOLDERNAME) || (type == UNDO_SPAWNNAME) || (type == UNDO_WORLDNAME) || (type == UNDO_SCENENAME) || (type == UNDO_TILENAME) || (type == UNDO_LAYERNAME) || (type == UNDO_ANIMGROUPNAME) || (type == UNDO_SCRIPTNAME));
- Uint32* buffer;
- int size = (max(oldName.size(), newName.size()) + 3) / 4;
- buffer = createUndoSpace(type, id, 0, oldName.size(), 0, size, 1, srcWin);
- if (!buffer) return;
-
- memcpy(buffer, oldName.c_str(), oldName.size());
- }
- int UndoBuffer::storeUndoLayerDelete(int id, int sceneId, int pos, LayerEdit* object, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer deletion");
- // If undo space not created, we delete object now, unless we're "in undo"
- if ((!createUndoSpace(UNDO_LAYERDELETE, id, sceneId, pos, 0, 0, 0, srcWin, 0, 0, 0, object)) && (!inUndo)) {
- return 0;
- }
- return 1;
- }
- int UndoBuffer::storeUndoLayerCreate(int id, int sceneId, int pos, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store layer creation");
- if (createUndoSpace(UNDO_LAYERCREATE, id, sceneId, pos, 0, 0, 0, srcWin)) return 1;
- return 0;
- }
- int UndoBuffer::storeUndoSpawnDelete(int id, int layerId, SpawnEdit* object, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store spawn deletion");
- // If undo space not created, we delete object now, unless we're "in undo"
- if ((!createUndoSpace(UNDO_SPAWNDELETE, id, layerId, 0, 0, 0, 0, srcWin, 0, 0, 0, object)) && (!inUndo)) {
- return 0;
- }
- return 1;
- }
- int UndoBuffer::storeUndoSpawnCreate(int id, int layerId, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store spawn creation");
- if (createUndoSpace(UNDO_SPAWNCREATE, id, layerId, 0, 0, 0, 0, srcWin)) return 1;
- return 0;
- }
- int UndoBuffer::storeUndoCreate(UndoType type, int id, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store creation");
- assert((type == UNDO_FOLDERCREATE) || (type == UNDO_SCENECREATE) || (type == UNDO_TILECREATE) || (type == UNDO_SCENECREATE) || (type == UNDO_ANIMGROUPCREATE) || (type == UNDO_SCRIPTCREATE));
- if (createUndoSpace(type, id, 0, 0, 0, 0, 0, srcWin)) return 1;
- return 0;
- }
- int UndoBuffer::storeUndoDelete(UndoType type, int id, void* object, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store deletion");
- assert((type == UNDO_FOLDERDELETE) || (type == UNDO_TILEDELETE) || (type == UNDO_SCENEDELETE) || (type == UNDO_ANIMGROUPDELETE) || (type == UNDO_SCRIPTDELETE));
- // If undo space not created, we delete object now, unless we're "in undo"
- if ((!createUndoSpace(type, id, 0, 0, 0, 0, 0, srcWin, 0, 0, 0, object)) && (!inUndo)) {
- return 0;
- }
- return 1;
- }
- void UndoBuffer::storeUndoSpawnMove(int id, int layerId, int oldX, int oldY, Window* srcWin) throw_Undo { start_func
- createUndoSpace(UNDO_SPAWNMOVE, id, layerId, oldX, oldY, 0, 0, srcWin);
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store spawn move");
- }
- void UndoBuffer::storeUndoSpawnSprite(int id, int layerId, int animId, int tileId, int subId, Window* srcWin) throw_Undo { start_func
- createUndoSpace(UNDO_SPAWNSPRITE, id, layerId, 0, 0, 0, 0, srcWin, animId, tileId, subId);
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store spawn image");
- }
- void UndoBuffer::storeUndoSpawnScript(int id, int layerId, int scriptId, Window* srcWin) throw_Undo { start_func
- createUndoSpace(UNDO_SPAWNSCRIPT, id, layerId, 0, 0, 0, 0, srcWin, scriptId);
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store spawn script");
- }
- void UndoBuffer::storeUndoFolderAdd(int id, SaveLoad* item, int pos, Window* srcWin) throw_Undo { start_func
- assert(item);
- assert(item->getId());
- createUndoSpace(UNDO_FOLDERADD, id, item->getBlockType(), 0, 0, 0, 0, srcWin, item->getId(), pos);
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store folder add");
- }
- void UndoBuffer::storeUndoFolderRemove(int id, SaveLoad* item, int pos, Window* srcWin) throw_Undo { start_func
- assert(item);
- assert(item->getId());
- createUndoSpace(UNDO_FOLDERREMOVE, id, item->getBlockType(), 0, 0, 0, 0, srcWin, item->getId(), pos);
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store folder add");
- }
- void UndoBuffer::storeUndoTileSize(int tileSetId, int oldWidth, int oldHeight, int oldCount, int oldMaps, SDL_Surface* surface, Uint32* fontWidths, Uint32* collisionMaps, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store tileset size");
- // If undo space not created, we delete saved buffers now, unless we're "in undo"
- if ((!createUndoSpace(UNDO_TILESIZE, tileSetId, 0, oldWidth, oldHeight, 0, 0, srcWin, oldCount, oldMaps, 0, surface, fontWidths, collisionMaps)) && (!inUndo)) {
- if (surface) SDL_FreeSurface(surface);
- delete[] fontWidths;
- delete[] collisionMaps;
- }
- }
- void UndoBuffer::storeUndoTilePerLine(int tileSetId, int oldPerLine, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store tileset per line");
- createUndoSpace(UNDO_TILEPERLINE, tileSetId, 0, oldPerLine, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoTileTrans(int tileSetId, int oldDefaultTransparent, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store tileset transparency flag");
- createUndoSpace(UNDO_TILETRANS, tileSetId, 0, oldDefaultTransparent, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoWorldStart(int oldStart, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store world starting scene");
- createUndoSpace(UNDO_WORLDSTART, 0, 0, oldStart, 0, 0, 0, srcWin);
- }
- void UndoBuffer::storeUndoScriptDefault(int id, int animId, int tileId, int subId, Window* srcWin) throw_Undo { start_func
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store script default image");
- createUndoSpace(UNDO_SCRIPTDEFAULT, id, 0, 0, 0, 0, 0, srcWin, animId, tileId, subId);
- }
- void UndoBuffer::storeUndoScriptModify(int id, const list<string>& source, int lineNum, int count, EditBox* editbox, Window* srcWin) throw_Undo { start_func
- storeUndoScript(UNDO_SCRIPTMODIFY, id, source, lineNum, count, editbox, srcWin);
- }
- void UndoBuffer::storeUndoScript(UndoType type, int id, const list<string>& source, int lineNum, int count, EditBox* editbox, Window* srcWin) throw_Undo { start_func
- assert(lineNum >= 0);
- assert(count > 0);
- assert((type == UNDO_SCRIPTMODIFY) || (type == UNDO_SCRIPTREMOVE));
-
- // Shortcut to prevent extra work and iterating to already-deleted data
- if (inUndo) return;
-
- if (type == UNDO_SCRIPTMODIFY) debugWrite(DEBUG_UNDO, "undo: store script modify");
- else debugWrite(DEBUG_UNDO, "undo: store script remove");
- list<string>::const_iterator begin = source.begin();
- for (int pos = 0; pos < lineNum; ++pos) {
- ++begin;
- }
-
- list<string>::const_iterator end = begin;
- for (int pos = 0; pos < count; ++pos) {
- ++end;
- }
-
- list<string>* copyScript = new list<string>(begin, end);
- // Is most recent undo block also a MODIFY of the same settings exactly? (and not merged)
- //@TODO: only combine same type of event (type, delete, etc.) this can be done once we
- // add undo descriptions
- if ((type == UNDO_SCRIPTMODIFY) && (undoAvailable())) {
- UndoHeader* header = (UndoHeader*)(undoBuffer + undoPos);
- int prevPos = undoPos - header->prevChunkSize;
- header = (UndoHeader*)(undoBuffer + prevPos);
- if ((header->type == UNDO_SCRIPTMODIFY) && (header->id == id) &&
- (header->xPos == lineNum) && (header->yPos == count) &&
- (header->sourceWindow == (srcWin ? srcWin->getId() : 0)) && (header->item2 == editbox)) {
- // If so, store nothing new; all done!
- delete copyScript;
- return;
- }
- }
- Uint32* buffer = NULL;
-
- try {
- buffer = createUndoSpace(type, id, 0, lineNum, count, sizeof(EditBox::TextPoint[3]) / sizeof(Uint32) + 1, 1, srcWin, 0, 0, 0, copyScript, editbox);
- if (!buffer) {
- delete copyScript;
- return;
- }
- }
- catch (...) {
- delete copyScript;
- throw;
- }
-
- if (editbox) {
- EditBox::TextPoint* tp = (EditBox::TextPoint*)buffer;
- editbox->getCursorData(tp[0], tp[1], tp[2]);
- }
- }
- void UndoBuffer::storeUndoScriptRemove(int id, const list<string>& source, int lineNum, int count, EditBox* editbox, Window* srcWin) throw_Undo { start_func
- storeUndoScript(UNDO_SCRIPTREMOVE, id, source, lineNum, count, editbox, srcWin);
- }
- void UndoBuffer::storeUndoScriptInsert(int id, int lineNum, int count, EditBox* editbox, Window* srcWin) throw_Undo { start_func
- assert(lineNum >= 0);
- assert(count > 0);
- if (!inUndo) debugWrite(DEBUG_UNDO, "undo: store script insert");
- Uint32* buffer = createUndoSpace(UNDO_SCRIPTINSERT, id, 0, lineNum, count, sizeof(EditBox::TextPoint[3]) / sizeof(Uint32) + 1, 1, srcWin, 0, 0, 0, NULL, editbox);
-
- if ((buffer) && (editbox)) {
- EditBox::TextPoint* tp = (EditBox::TextPoint*)buffer;
- editbox->getCursorData(tp[0], tp[1], tp[2]);
- }
- }
- void UndoBuffer::clearUndo() { start_func
- debugWrite(DEBUG_UNDO, "undo: clear all");
- if (undoBuffer) {
- // Scan up to end of any redos
- UndoHeader* header = (UndoHeader*)(undoBuffer + undoPos);
- while (header->type != UNDO_EMPTY) {
- undoPos += header->xSize * header->ySize + undoHeaderSize;
- header = (UndoHeader*)(undoBuffer + undoPos);
- }
-
- // Must clear any allocated objects
- while (undoPos > 0) {
- header = (UndoHeader*)(undoBuffer + undoPos);
- undoPos -= header->prevChunkSize;
- deleteUndoBlock(undoPos);
- }
- delete[] undoBuffer;
- undoBuffer = NULL;
- undoSizeAlloc = 0;
- undoPos = 0;
- }
- }
- void UndoBuffer::deleteUndoBlock(int offset) { start_func
- UndoHeader* header = (UndoHeader*)(undoBuffer + offset);
- int type = header->type & ~UNDO_MERGE;
-
- switch (type) {
- case UNDO_SCRIPTREMOVE:
- case UNDO_SCRIPTMODIFY:
- delete (list<string>*)header->item1;
- break;
- case UNDO_TILEDELETE:
- delete (TileSetEdit*)header->item1;
- break;
- case UNDO_SCENEDELETE:
- delete (SceneEdit*)header->item1;
- break;
- case UNDO_ANIMGROUPDELETE:
- delete (AnimGroupEdit*)header->item1;
- break;
- case UNDO_FOLDERDELETE:
- delete (FolderEdit*)header->item1;
- break;
- case UNDO_SCRIPTDELETE:
- delete (ScriptEdit*)header->item1;
- break;
- case UNDO_LAYERDELETE:
- delete (LayerEdit*)header->item1;
- break;
-
- case UNDO_SPAWNDELETE:
- delete (SpawnEdit*)header->item1;
- break;
-
- case UNDO_TILESIZE:
- if (header->item1) SDL_FreeSurface((SDL_Surface*)header->item1);
- delete[] (Uint32*)header->item2;
- delete[] (Uint32*)header->item3;
- break;
-
- case UNDO_LAYERSIZE:
- delete[] (Uint32*)header->item1;
- delete[] (Uint32*)header->item2;
- delete[] (Uint32*)header->item3;
- break;
-
- case UNDO_LAYERSPAWNSEL:
- delete (set<int>*)header->item2;
- break;
- }
-
- header->item1 = NULL;
- header->item2 = NULL;
- header->item3 = NULL;
- }
- void UndoBuffer::warnUser() throw_Undo { start_func
- if (guiConfirmBox("The undo buffer cannot store this action and will be cleared- continue without ability to undo?", "Confirm Permanent Action")) {
- clearUndo();
- // (cancel any further pieces of a current block)
- if (undoBlock) undoNonUndoable = 1;
- return;
- }
- debugWrite(DEBUG_UNDO, "undo: cancel undo");
-
- // Undo anything done as part of a block
- if (undoBlock) {
- if (undoBlockDidFirst) {
- undoPerform();
- }
- undoBlock = 0;
- // Delete any redo chunks here or later
- clearRedo();
- }
- throw UndoException();
- }
- void UndoBuffer::clearRedo() { start_func
- if (undoBuffer) {
- int redoPos = undoPos;
- UndoHeader* header = (UndoHeader*)(undoBuffer + redoPos);
- while (header->type != UNDO_EMPTY) {
- deleteUndoBlock(redoPos);
- redoPos += header->xSize * header->ySize + undoHeaderSize;
-
- // Actually delete each block from buffer
- header->sourceWindow = 0;
- header->type = UNDO_EMPTY;
- header->id = 0;
- header->xSize = 0;
- header->ySize = 0;
-
- header = (UndoHeader*)(undoBuffer + redoPos);
- }
- }
- }
- int UndoBuffer::undoAvailable() const { start_func
- // If undo pos isn't pointing at first item, there's undo available
- if ((undoBuffer) && (undoPos > 0)) return 1;
- return 0;
- }
- int UndoBuffer::redoAvailable() const { start_func
- // If undo pos isn't pointing at an empty item, there's redo available
- if (undoBuffer) {
- UndoHeader* header = (UndoHeader*)(undoBuffer + undoPos);
- if (header->type != UNDO_EMPTY) return 1;
- }
- return 0;
- }
- void UndoBuffer::redoPerform() { start_func
- undoPerform(1);
- }
- void UndoBuffer::undoPerform(int doRedo) { start_func
- UndoHeader* header = (UndoHeader*)(undoBuffer + undoPos);
- int didWindowFocus = 0;
- int didWindowQuery = 0;
- int windowDidntExist = 0;
- // Repeat undo/redo?
- int isMerged = 1;
- int cancelUndo = 0;
- inUndo = 1;
- while (isMerged) {
- if (doRedo) {
- debugWrite(DEBUG_UNDO, "undo: perform redo");
- if (!redoAvailable()) {
- inUndo = 0;
- return;
- }
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: perform undo");
- if (!undoAvailable()) {
- inUndo = 0;
- return;
- }
-
- // Move undo pos backward an item
- undoPos -= header->prevChunkSize;
- header = (UndoHeader*)(undoBuffer + undoPos);
- isMerged = header->type & UNDO_MERGE;
- }
-
- const int type = header->type & ~UNDO_MERGE;
- // Verify target window exists, but don't query user if a selection action
- if (!header->sourceWindow) windowDidntExist = 1;
- else if (!didWindowQuery) {
- // Verify existence
- if (!desktop->verifyWindow(header->sourceWindow)) {
- windowDidntExist = 1;
- if ((type != UNDO_LAYERTILETMP) && (type != UNDO_LAYERTILECUR) &&
- (type != UNDO_LAYERTILESEL) && (type != UNDO_PAINTSEL) &&
- (type != UNDO_LAYERSPAWNSEL)) {
- if (!guiConfirmBox("You have already closed the window the original action occurred in- still undo?", "Confirm Undo")) cancelUndo = 1;
- }
- }
- }
- didWindowQuery = 1;
- // Used later in notify
- const int id = header->id;
- const int subid = header->subid;
- const int w = header->xSize;
- const int h = header->ySize;
- const int x = header->xPos;
- const int y = header->yPos;
- const int d1 = header->data1;
- const int d2 = header->data2;
- const int d3 = header->data3;
- Uint32* buffer = undoBuffer + undoPos + undoHeaderSize;
- Uint32* temp = NULL;
-
- if (!cancelUndo) {
- try {
- // Type of item?
- switch (type) {
- case UNDO_SCRIPTMODIFY:
- case UNDO_SCRIPTREMOVE:
- case UNDO_SCRIPTINSERT: {
- ScriptEdit* script = dynamic_cast<ScriptEdit*>(world->findScript(id));
- assert(script);
- script->markLock(); // Exception point
- list<string>::iterator cPos = script->getSource().begin();
- for (int pos = 0; pos < x; ++pos) {
- ++cPos;
- }
- EditBox* edit = (EditBox*)header->item2;
- EditBox::TextPoint ins, selBegin, selEnd;
- if ((edit) && (!windowDidntExist)) {
- EditBox::TextPoint* tp = (EditBox::TextPoint*)buffer;
- edit->getCursorData(ins, selBegin, selEnd);
- swap(ins, tp[0]);
- swap(selBegin, tp[1]);
- swap(selEnd, tp[2]);
- }
- if (type == UNDO_SCRIPTMODIFY) {
- debugWrite(DEBUG_UNDO, "undo: modify script modify");
- script->codeModifiedPre(ScriptEdit::SCRIPT_MODIFY_LINE, x, y);
- script->codeModifiedPre(ScriptEdit::SCRIPT_MODIFY_DONE, 0, 0);
- list<string>::iterator mPos = ((list<string>*)header->item1)->begin();
- for (int pos = 0; pos < y; ++pos) {
- swap(*cPos, *mPos);
- ++cPos;
- ++mPos;
- }
- script->codeModifiedPost(ScriptEdit::SCRIPT_MODIFY_LINE, x, y);
- }
- else if (type == UNDO_SCRIPTINSERT) {
- debugWrite(DEBUG_UNDO, "undo: modify script insert");
- script->codeModifiedPre(ScriptEdit::SCRIPT_REMOVE_LINES, x, y);
- script->codeModifiedPre(ScriptEdit::SCRIPT_MODIFY_DONE, 0, 0);
- list<string>::iterator cEnd = cPos;
- for (int pos = 0; pos < y; ++pos) {
- ++cEnd;
- }
- list<string>* inserted = new list<string>(cPos, cEnd);
- script->getSource().erase(cPos, cEnd);
- header->type = (UndoType)(UNDO_SCRIPTREMOVE | (header->type & UNDO_MERGE));
- header->item1 = inserted;
- script->codeModifiedPost(ScriptEdit::SCRIPT_REMOVE_LINES, x, y);
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: modify script remove");
- script->codeModifiedPre(ScriptEdit::SCRIPT_INSERT_LINES, x, y);
- script->codeModifiedPre(ScriptEdit::SCRIPT_MODIFY_DONE, 0, 0);
- list<string>* deleted = (list<string>*)header->item1;
- assert((int)deleted->size() == y);
- script->getSource().splice(cPos, *deleted);
- delete deleted;
- header->type = (UndoType)(UNDO_SCRIPTINSERT | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- script->codeModifiedPost(ScriptEdit::SCRIPT_INSERT_LINES, x, y);
- }
-
- script->codeModifiedPost(ScriptEdit::SCRIPT_MODIFY_DONE, 0, 0);
- if ((edit) && (!windowDidntExist)) edit->setCursorData(ins, selBegin, selEnd);
- script->markUnlock();
- break;
- }
- case UNDO_LAYERTILESET: {
- debugWrite(DEBUG_UNDO, "undo: modify layer tileset");
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- layer->markLock(); // Exception point
- // (have to account for 0 tileset)
- TileSet* tileset = layer->getTileSet();
- if (tileset) header->xPos = tileset->getId();
- else header->xPos = 0;
- try {
- layer->setTileset(x); // Another exception point
- }
- catch (...) {
- layer->markUnlock();
- throw;
- }
- layer->markUnlock();
- break;
- }
-
- case UNDO_LAYERSIZE: {
- debugWrite(DEBUG_UNDO, "undo: modify layer size");
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- layer->markLock(); // Exception point
-
- header->xPos = layer->getXSize();
- header->yPos = layer->getYSize();
- layer->setSize(x, y, NULL, NULL, (Uint32**)(&header->item1), (Uint32**)(&header->item2), (Uint32**)(&header->item3));
-
- layer->markUnlock();
- break;
- }
- case UNDO_LAYERFX: {
- debugWrite(DEBUG_UNDO, "undo: modify layer effects flag");
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- layer->markLock(); // Exception point
- header->xPos = layer->getUsesFx();
- layer->setEffects(x);
- layer->markUnlock();
- break;
- }
- case UNDO_LAYEREXT: {
- debugWrite(DEBUG_UNDO, "undo: modify layer extended data flag");
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- layer->markLock(); // Exception point
- header->xPos = layer->getUsesExt();
- layer->setExtended(x);
- layer->markUnlock();
- break;
- }
-
- case UNDO_LAYERTYPE: {
- debugWrite(DEBUG_UNDO, "undo: modify layer type");
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- layer->markLock(); // Exception point
- header->xPos = layer->getType();
- layer->setType((Layer::LayerType)x);
- layer->markUnlock();
- break;
- }
-
- case UNDO_LAYERTILECUR: {
- // Verify window still exists (if not, this is a "no op"
- if (desktop->verifyWindow(header->sourceWindow)) {
- debugWrite(DEBUG_UNDO, "undo: modify layer affected/view");
- // Ensure proper layers are selected
- SceneEditLayer* win = dynamic_cast<SceneEditLayer*>(dynamic_cast<FrameWindow*>(desktop->verifyWindow(header->sourceWindow))->getClient());
- win->refreshLayers(header->data1, header->data2, header->data3);
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: skip layer affected/view (window closed)");
- }
- break;
- }
-
- case UNDO_LAYERTILETMP: {
- // Verify window still exists (if not, this is a "no op"
- if (desktop->verifyWindow(header->sourceWindow)) {
- debugWrite(DEBUG_UNDO, "undo: modify layer floating selection");
- // Swap data here with data in layer storage
- Uint32* data = *((Uint32**)(header->item1));
- data += y * subid + x;
- // Temp storage for swapping
- temp = new Uint32[w];
- int lineSize = w * 4;
-
- for (int pos = h; pos > 0; --pos) {
- memcpy(temp, data, lineSize);
- memcpy(data, buffer, lineSize);
- memcpy(buffer, temp, lineSize);
- data += subid;
- buffer += w;
- }
- if (header->item3) {
- Uint32* dataExt = *((Uint32**)(header->item3));
- dataExt += y * subid + x;
- for (int pos = h; pos > 0; --pos) {
- memcpy(temp, dataExt, lineSize);
- memcpy(dataExt, buffer, lineSize);
- memcpy(buffer, temp, lineSize);
- dataExt += subid;
- buffer += w;
- }
- }
-
- if (header->item2) {
- Uint32* dataFx = *((Uint32**)(header->item2));
- dataFx += y * subid + x;
- for (int pos = h; pos > 0; --pos) {
- memcpy(temp, dataFx, lineSize);
- memcpy(dataFx, buffer, lineSize);
- memcpy(buffer, temp, lineSize);
- dataFx += subid;
- buffer += w;
- }
- }
- // UNDO_LAYERTILESEL will alert window
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: skip layer floating selection (window closed)");
- }
- break;
- }
-
- case UNDO_LAYERTILESEL: {
- // Verify window still exists (if not, this is a "no op"
- if (desktop->verifyWindow(header->sourceWindow)) {
- debugWrite(DEBUG_UNDO, "undo: modify layer selection");
- // Swap data here with data in selection surface
- Uint8* selData = *((Uint8**)(header->item1));
-
- // Temp storage for swapping
- temp = new Uint32[w];
- Uint8* dest = selData + y * subid + x;
-
- for (int pos = h; pos > 0; --pos) {
- memcpy(temp, dest, id);
- memcpy(dest, buffer, id);
- memcpy(buffer, temp, id);
- buffer += w;
- dest += subid;
- }
-
- // alert window and swap selection parameters
- SceneEditLayer* win = dynamic_cast<SceneEditLayer*>(dynamic_cast<FrameWindow*>(desktop->verifyWindow(header->sourceWindow))->getClient());
- win->swapSelectionParameters(header->data1, header->data2, header->data3);
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: skip layer selection (window closed)");
- }
- break;
- }
-
- case UNDO_LAYERSPAWNSEL: {
- // Verify window still exists (if not, this is a "no op"
- if (desktop->verifyWindow(header->sourceWindow)) {
- debugWrite(DEBUG_UNDO, "undo: modify spawn selection");
- // Swap data here with data in selection surface
- set<int>* liveData = (set<int>*)(header->item1);
- set<int>* undoData = (set<int>*)(header->item2);
- liveData->swap(*undoData);
-
- // alert window
- SceneEditLayer* win = dynamic_cast<SceneEditLayer*>(dynamic_cast<FrameWindow*>(desktop->verifyWindow(header->sourceWindow))->getClient());
- win->updatedSpawnSelection();
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: skip spawn selection (window closed)");
- }
- break;
- }
-
- case UNDO_PAINTSEL: {
- // Verify window still exists (if not, this is a "no op"
- if (desktop->verifyWindow(header->sourceWindow)) {
- debugWrite(DEBUG_UNDO, "undo: modify tile selection");
- // Swap data here with data in selection surface
- SDL_Surface* surface = *((SDL_Surface**)(header->item1));
-
- // Temp storage for swapping
- temp = new Uint32[w];
-
- // @TODO: reallocate surface if not large enough
- int pitch = surface->pitch;
- int lineSize = w * 4;
- Uint8* dest = ((Uint8*)surface->pixels) + y * pitch + x * 4;
-
- for (int pos = h; pos > 0; --pos) {
- memcpy(temp, dest, lineSize);
- memcpy(dest, buffer, lineSize);
- memcpy(buffer, temp, lineSize);
- buffer += w;
- dest += pitch;
- }
-
- // alert window and swap selection parameters
- TilePaint* win = dynamic_cast<TilePaint*>(dynamic_cast<FrameWindow*>(desktop->verifyWindow(header->sourceWindow))->getClient());
- win->swapSelectionParameters(header->data1, header->data2, header->data3);
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: skip tile selection (window closed)");
- }
- break;
- }
-
- case UNDO_LAYERTILE:
- case UNDO_LAYERTILEEXT:
- case UNDO_LAYERTILEFX: {
- // Swap data here with data in layer
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- layer->markLock(); // Exception point
-
- Rect rect = { x, y, w, h };
- if (type == UNDO_LAYERTILE) {
- debugWrite(DEBUG_UNDO, "undo: modify layer tiles");
- layer->swapLayer(buffer, NULL, NULL, w, rect);
- }
- else if (type == UNDO_LAYERTILEFX) {
- debugWrite(DEBUG_UNDO, "undo: modify layer effects");
- layer->swapLayer(NULL, NULL, buffer, w, rect);
- }
- else {
- debugWrite(DEBUG_UNDO, "undo: modify layer extended data");
- layer->swapLayer(NULL, buffer, NULL, w, rect);
- }
-
- layer->markUnlock();
- break;
- }
- case UNDO_TILE: {
- debugWrite(DEBUG_UNDO, "undo: modify tile");
- // Swap data here with data on tile
- TileSetEdit* tiles = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tiles);
- tiles->markLock(); // Exception point
- int pitch = tiles->viewTilePitch();
- int lineSize = w * 4;
- Uint8* dest = tiles->editTileData(subid) + y * pitch + x * 4;
- // Temp storage for swapping
- temp = new Uint32[w];
- for (int pos = h; pos > 0; --pos) {
- memcpy(temp, dest, lineSize);
- memcpy(dest, buffer, lineSize);
- memcpy(buffer, temp, lineSize);
- buffer += w;
- dest += pitch;
- }
- // Store change and notify
- tiles->editTileDone(subid);
- tiles->markUnlock();
- break;
- }
- case UNDO_TILECOLL: {
- debugWrite(DEBUG_UNDO, "undo: modify collision map");
- // Swap data here with data on map
- TileSetEdit* tiles = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tiles);
- tiles->markLock(); // Exception point
- int pitch = tiles->getHeight();
- Uint32* dest = tiles->editCollData(subid) + y * pitch + x * 4;
- // Temp storage for swapping
- temp = new Uint32[w];
-
- for (int pos = w; pos > 0; --pos) {
- memcpy(temp, dest, h);
- memcpy(dest, buffer, h);
- memcpy(buffer, temp, h);
- buffer += h;
- dest += pitch;
- }
- // Store change and notify
- tiles->editCollDone(subid);
- tiles->markUnlock();
- break;
- }
-
- case UNDO_TILEGLYPH: {
- debugWrite(DEBUG_UNDO, "undo: modify tile glyph");
- TileSetEdit* tiles = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tiles);
- tiles->markLock(); // Exception point
-
- header->xPos = tiles->getGlyphWidth(subid);
- tiles->setGlyphWidth(subid, x, NULL);
-
- tiles->markUnlock();
- break;
- }
-
- case UNDO_WORLDNAME:
- case UNDO_LAYERNAME:
- case UNDO_TILENAME:
- case UNDO_SCENENAME:
- case UNDO_ANIMGROUPNAME:
- case UNDO_SCRIPTNAME:
- case UNDO_SPAWNNAME:
- case UNDO_FOLDERNAME: {
- debugWrite(DEBUG_UNDO, "undo: modify name");
- string tempName;
- string newName((char*)buffer, x);
-
- // Grab current and new names
- // Store change and notify
- if (type == UNDO_LAYERNAME) {
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- tempName = layer->getName();
- layer->setName(newName);
- }
- else if (type == UNDO_SPAWNNAME) {
- SpawnEdit* spawn = world->findSpawn(id);
- assert(spawn);
- tempName = spawn->getName();
- spawn->setName(newName);
- }
- else if (type == UNDO_TILENAME) {
- TileSetEdit* tiles = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tiles);
- tempName = tiles->getName();
- tiles->setName(newName);
- }
- else if (type == UNDO_SCENENAME) {
- SceneEdit* scene = dynamic_cast<SceneEdit*>(world->findScene(id));
- assert(scene);
- tempName = scene->getName();
- scene->setName(newName);
- }
- else if (type == UNDO_SCRIPTNAME) {
- ScriptEdit* script = dynamic_cast<ScriptEdit*>(world->findScript(id));
- assert(script);
- tempName = script->getName();
- script->setName(newName);
- }
- else if (type == UNDO_WORLDNAME) {
- tempName = world->getTitle();
- world->setTitle(newName);
- }
- else if (type == UNDO_FOLDERNAME) {
- FolderEdit* folder = dynamic_cast<FolderEdit*>(world->findFolder(id));
- assert(folder);
- tempName = folder->getName();
- folder->setName(newName);
- }
- else {
- assert(type == UNDO_ANIMGROUPNAME);
- AnimGroupEdit* animgroup = dynamic_cast<AnimGroupEdit*>(world->findAnimGroup(id));
- assert(animgroup);
- tempName = animgroup->getName();
- animgroup->setName(newName);
- }
-
- // Place old name back into buffer
- memcpy(buffer, tempName.c_str(), tempName.size());
- header->xPos = tempName.size();
- break;
- }
-
- case UNDO_LAYERMOVE: {
- debugWrite(DEBUG_UNDO, "undo: undo layer swap");
- SceneEdit* scene = dynamic_cast<SceneEdit*>(world->findScene(id));
- assert(scene);
- scene->swapLayer(x, y);
- break;
- }
-
- case UNDO_SPAWNCREATE: {
- debugWrite(DEBUG_UNDO, "undo: undo spawn creation");
- LayerEdit* layer = world->findLayer(subid);
- assert(layer); // @TODO: non-layer spawns (subid == 0)
- layer->markLock(); // Exception point
- try {
- SpawnEdit* spawn = world->findSpawn(id);
- assert(spawn);
- layer->deleteSpawn(spawn);
- header->type = (UndoType)(UNDO_SPAWNDELETE | (header->type & UNDO_MERGE));
- header->item1 = spawn;
- layer->markUnlock();
- }
- catch (...) {
- layer->markUnlock();
- throw;
- }
- break;
- }
- case UNDO_LAYERCREATE: {
- debugWrite(DEBUG_UNDO, "undo: undo layer creation");
- LayerEdit* layer = world->findLayer(id);
- assert(layer);
- SceneEdit* scene = dynamic_cast<SceneEdit*>(world->findScene(subid));
- assert(scene);
- // (delete shouldn't ever "fail", but if it does, we just cancel)
- if (!scene->deleteLayer(x)) { // Exception point
- cancelUndo = 1;
- break;
- }
- header->type = (UndoType)(UNDO_LAYERDELETE | (header->type & UNDO_MERGE));
- header->item1 = layer;
- break;
- }
- case UNDO_SCENECREATE: {
- debugWrite(DEBUG_UNDO, "undo: undo scene creation");
- SceneEdit* scene = dynamic_cast<SceneEdit*>(world->findScene(id));
- assert(scene);
- // (delete shouldn't ever "fail", but if it does, we just cancel)
- if (!world->deleteScene(scene)) {
- cancelUndo = 1;
- break;
- }
- header->type = (UndoType)(UNDO_SCENEDELETE | (header->type & UNDO_MERGE));
- header->item1 = scene;
- break;
- }
- case UNDO_ANIMGROUPCREATE: {
- debugWrite(DEBUG_UNDO, "undo: undo anim group creation");
- AnimGroupEdit* animgroup = dynamic_cast<AnimGroupEdit*>(world->findAnimGroup(id));
- assert(animgroup);
- // (delete shouldn't ever "fail", but if it does, we just cancel)
- if (!world->deleteAnimGroup(animgroup)) {
- cancelUndo = 1;
- break;
- }
- header->type = (UndoType)(UNDO_ANIMGROUPDELETE | (header->type & UNDO_MERGE));
- header->item1 = animgroup;
- break;
- }
- case UNDO_FOLDERCREATE: {
- debugWrite(DEBUG_UNDO, "undo: undo folder creation");
- FolderEdit* folder = dynamic_cast<FolderEdit*>(world->findFolder(id));
- assert(folder);
- // (delete shouldn't ever "fail", but if it does, we just cancel)
- if (!world->deleteFolder(folder)) {
- cancelUndo = 1;
- break;
- }
- header->type = (UndoType)(UNDO_FOLDERDELETE | (header->type & UNDO_MERGE));
- header->item1 = folder;
- break;
- }
-
- case UNDO_SCRIPTCREATE: {
- debugWrite(DEBUG_UNDO, "undo: undo script creation");
- ScriptEdit* script = dynamic_cast<ScriptEdit*>(world->findScript(id));
- assert(script);
- // (delete shouldn't ever "fail", but if it does, we just cancel)
- if (!world->deleteScript(script)) {
- cancelUndo = 1;
- break;
- }
- header->type = (UndoType)(UNDO_SCRIPTDELETE | (header->type & UNDO_MERGE));
- header->item1 = script;
- break;
- }
-
- case UNDO_TILECREATE: {
- debugWrite(DEBUG_UNDO, "undo: undo tileset creation");
- TileSetEdit* tileset = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tileset);
- // (delete shouldn't ever "fail", but if it does, we just cancel)
- if (!world->deleteTileset(tileset)) {
- cancelUndo = 1;
- break;
- }
- header->type = (UndoType)(UNDO_TILEDELETE | (header->type & UNDO_MERGE));
- header->item1 = tileset;
- break;
- }
- case UNDO_SPAWNDELETE: {
- debugWrite(DEBUG_UNDO, "undo: undo spawn deletion");
- // Undelete
- LayerEdit* layer = world->findLayer(subid);
- assert(layer); // @TODO: non-layer spawns (subid == 0)
- layer->markLock(); // Exception point
- try {
- SpawnEdit* spawn = (SpawnEdit*)header->item1;
- assert(spawn);
- layer->addSpawn(spawn);
- header->type = (UndoType)(UNDO_SPAWNCREATE | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- layer->markUnlock();
- }
- catch (...) {
- layer->markUnlock();
- throw;
- }
- break;
- }
- case UNDO_LAYERDELETE: {
- debugWrite(DEBUG_UNDO, "undo: undo layer deletion");
- // Undelete
- LayerEdit* layer = (LayerEdit*)header->item1;
- assert(layer);
- SceneEdit* scene = dynamic_cast<SceneEdit*>(world->findScene(subid));
- assert(scene);
- scene->insertLayer(layer, x, NULL, NULL, 1);
- header->type = (UndoType)(UNDO_LAYERCREATE | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- break;
- }
- case UNDO_SCENEDELETE: {
- debugWrite(DEBUG_UNDO, "undo: undo scene deletion");
- // Undelete
- SceneEdit* scene = (SceneEdit*)header->item1;
- assert(scene);
- world->readdScene(scene);
- header->type = (UndoType)(UNDO_SCENECREATE | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- break;
- }
- case UNDO_SCRIPTDELETE: {
- debugWrite(DEBUG_UNDO, "undo: undo script deletion");
- // Undelete
- ScriptEdit* script = (ScriptEdit*)header->item1;
- assert(script);
- world->readdScript(script);
- header->type = (UndoType)(UNDO_SCRIPTCREATE | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- break;
- }
- case UNDO_ANIMGROUPDELETE: {
- debugWrite(DEBUG_UNDO, "undo: undo anim group deletion");
- // Undelete
- AnimGroupEdit* animgroup = (AnimGroupEdit*)header->item1;
- assert(animgroup);
- world->readdAnimGroup(animgroup);
- header->type = (UndoType)(UNDO_ANIMGROUPCREATE | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- break;
- }
- case UNDO_FOLDERDELETE: {
- debugWrite(DEBUG_UNDO, "undo: undo folder deletion");
- // Undelete
- FolderEdit* folder = (FolderEdit*)header->item1;
- assert(folder);
- world->readdFolder(folder);
- header->type = (UndoType)(UNDO_FOLDERCREATE | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- break;
- }
- case UNDO_TILEDELETE: {
- debugWrite(DEBUG_UNDO, "undo: undo tileset deletion");
- // Undelete
- TileSetEdit* tileset = (TileSetEdit*)header->item1;
- assert(tileset);
- world->readdTileset(tileset);
- header->type = (UndoType)(UNDO_TILECREATE | (header->type & UNDO_MERGE));
- header->item1 = NULL;
- break;
- }
-
- case UNDO_TILESIZE: {
- debugWrite(DEBUG_UNDO, "undo: modify tileset size");
- TileSetEdit* tileset = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tileset);
- tileset->markLock(); // Exception point
-
- header->xPos = tileset->getWidth();
- header->yPos = tileset->getHeight();
- header->data1 = tileset->getCount();
- header->data2 = tileset->getCollisionCount();
- tileset->setSize(x, y, d1, d2, NULL, NULL, (SDL_Surface**)(&header->item1), (Uint32**)(&header->item2), (Uint32**)(&header->item3));
-
- tileset->markUnlock();
- break;
- }
-
- case UNDO_TILEPERLINE: {
- debugWrite(DEBUG_UNDO, "undo: modify tileset per line");
- TileSetEdit* tileset = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tileset);
- header->xPos = tileset->getTilesPerLine();
- tileset->setTilesPerLine(x);
- break;
- }
-
- case UNDO_TILETRANS: {
- debugWrite(DEBUG_UNDO, "undo: modify tileset transparency flag");
- TileSetEdit* tileset = dynamic_cast<TileSetEdit*>(world->findTileSet(id));
- assert(tileset);
- header->xPos = tileset->getDefaultTransparent();
- tileset->setDefaultTransparent(x);
- break;
- }
- case UNDO_WORLDSTART: {
- debugWrite(DEBUG_UNDO, "undo: modify world starting scene");
- header->xPos = world->getStartScene();
- world->setStartScene(x);
- break;
- }
- case UNDO_SCRIPTDEFAULT: {
- debugWrite(DEBUG_UNDO, "undo: modify script default image");
- ScriptEdit* script = dynamic_cast<ScriptEdit*>(world->findScript(id));
- assert(script);
- if (script->getDefaultAnimgroup()) header->data1 = script->getDefaultAnimgroup()->getId();
- else header->data1 = 0;
- if (script->getDefaultTileset()) header->data2 = script->getDefaultTileset()->getId();
- else header->data2 = 0;
- header->data3 = script->getDefaultId();
- script->setDefault(world->findAnimGroup(d1),
- world->findTileSet(d2),
- d3);
- break;
- }
-
- case UNDO_SPAWNMOVE: {
- debugWrite(DEBUG_UNDO, "undo: modify spawn move");
- LayerEdit* layer = world->findLayer(subid);
- assert(layer); // @TODO: non-layer spawns (subid == 0)
- layer->markLock(); // Exception point
- try {
- SpawnEdit* spawn = world->findSpawn(id);
- assert(spawn);
- header->xPos = spawn->getX();
- header->yPos = spawn->getY();
- spawn->setPos(x, y);
- layer->markUnlock();
- }
- catch (...) {
- layer->markUnlock();
- throw;
- }
- break;
- }
-
- case UNDO_SPAWNSPRITE: {
- debugWrite(DEBUG_UNDO, "undo: modify spawn image");
- LayerEdit* layer = world->findLayer(subid);
- assert(layer); // @TODO: non-layer spawns (subid == 0)
- layer->markLock(); // Exception point
- try {
- SpawnEdit* spawn = world->findSpawn(id);
- assert(spawn);
- if (spawn->getAnimgroup()) header->data1 = spawn->getAnimgroup()->getId();
- else header->data1 = 0;
- if (spawn->getTileset()) header->data2 = spawn->getTileset()->getId();
- else header->data2 = 0;
- header->data3 = spawn->getSubid();
- spawn->setSprite(world->findAnimGroup(d1),
- world->findTileSet(d2),
- d3);
- layer->markUnlock();
- }
- catch (...) {
- layer->markUnlock();
- throw;
- }
- break;
- }
-
- case UNDO_SPAWNSCRIPT: {
- debugWrite(DEBUG_UNDO, "undo: modify spawn script");
- LayerEdit* layer = world->findLayer(subid);
- assert(layer); // @TODO: non-layer spawns (subid == 0)
- layer->markLock(); // Exception point
- try {
- SpawnEdit* spawn = world->findSpawn(id);
- assert(spawn);
- if (spawn->getScript()) header->data1 = spawn->getScript()->getId();
- else header->data1 = 0;
- spawn->setScript(world->findScript(d1));
- layer->markUnlock();
- }
- catch (...) {
- layer->markUnlock();
- throw;
- }
- break;
- }
-
- case UNDO_FOLDERADD: {
- debugWrite(DEBUG_UNDO, "undo: undo folder add");
- FolderEdit* folder = world->findFolder(id);
- assert(folder);
- SaveLoad* item = world->findItem(d1, subid);
- assert(item);
- folder->removeItem(item);
- header->type = (UndoType)(UNDO_FOLDERREMOVE | (header->type & UNDO_MERGE));
- break;
- }
-
- case UNDO_FOLDERREMOVE: {
- debugWrite(DEBUG_UNDO, "undo: undo folder remove");
- FolderEdit* folder = world->findFolder(id);
- assert(folder);
- SaveLoad* item = world->findItem(d1, subid);
- assert(item);
- folder->addItem(item, d2);
- header->type = (UndoType)(UNDO_FOLDERREMOVE | (header->type & UNDO_MERGE));
- break;
- }
- }
- }
- catch (UndoException& e) {
- // Since we were "inUndo", the above code should NEVER throw this
- assert(0);
- }
- catch (FileException& e) {
- // Likely from locking/uncaching
- cancelUndo = 1;
- guiErrorBox(string(e.details), errorTitleFile);
- }
- }
-
- if (cancelUndo) {
- inUndo = 0;
- delete[] temp;
- // Move undo pos back
- if (!doRedo) undoPos += w * h + undoHeaderSize;
- return;
- }
-
- // Notify window
- if ((header->sourceWindow) && (!didWindowFocus)) {
- // Verify existence
- Window* win = desktop->verifyWindow(header->sourceWindow);
- if (win) {
- desktop->bringToTop(win);
- win->undoNotify(type, id, subid, x, y, w, h);
- didWindowFocus = 1;
- }
- }
-
- delete[] temp;
-
- if (doRedo) {
- // Move undo pos forward an item
- undoPos += w * h + undoHeaderSize;
- header = (UndoHeader*)(undoBuffer + undoPos);
- isMerged = header->type & UNDO_MERGE;
- }
- }
- inUndo = 0;
- }
|