diff options
Diffstat (limited to 'app/bin/cundo.c')
-rw-r--r-- | app/bin/cundo.c | 745 |
1 files changed, 519 insertions, 226 deletions
diff --git a/app/bin/cundo.c b/app/bin/cundo.c index 1ed5588..b58d541 100644 --- a/app/bin/cundo.c +++ b/app/bin/cundo.c @@ -1,5 +1,5 @@ /** \file cundo.c - * Undo / redo functions. + * Undo / redo functions. */ /* XTrkCad - Model Railroad CAD @@ -17,25 +17,116 @@ * * 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-1307, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> -#include <time.h> -#include <stdarg.h> -#include <errno.h> -#include <string.h> +/* + * Implements Undo/Redo + * + * Each action/change (New/Modify/Delete) is recorded by UndoNew(), UndoModify(), UndoDelete() within an undo transaction (initiated by UndoStart(). + * + * New tracks are added to the end of the tracklist (headed by to_first). + * + * Modify/Delete generate an undo record (WriteObject) in undoStream. + * Each record contains: + * - op (ModifyOp or DeleteOp) + * - address for the existing track + * - copy of the track + * - endpts, extradata, extra-extradata + * + * Undo pulls records from the undoStream (ReadObject) for the current transation + * to recreate modified and deleted tracks. + * New tracks are snipped from the tracklist + * + * Undone records can be copied to redoStream for susequent redo ops + * + * The undo transactions are stored in a circular buffer (undoStack). + * When this buffer wraps around, the old transaction is recycled, + * At this point, any DeleteOp records in the old transaction are processed + * (DeleteInStream) and the deleted track is Free'd + * + * The streams are expandable ring buffers. + * When the transaction buffer wraps, the unreferenced start of the undoStreams is trimmed. + * THe redoStream is purged for every transaction. + * + * + * Note on Delete + * + * UndoDelete does 2 things: + * 1 Marks the track's transaction record with DeleteOp + * When the transaction record is recycled, the old track object will be Free'd. + * 2 Sets the .delete flag in the track object + * For the most part (except dcar.c and cundo.c) IsTrackDeleted() is used in CHECKs + * There are a few cases where we have to deal with deleted track. + * In general, we do not need to look inside a deleted track and + * GET_EXTRA_DATA will complain if we try (FreeTrack is the exception) + */ #include "cselect.h" #include "custom.h" #include "fileio.h" -#include "i18n.h" -#include "messages.h" #include "paths.h" #include "track.h" -#include "trackx.h" +// We need to fiddle with the track list +#include "trackx.h" // tempTrk, to_first, to_last +#include "trkendpt.h" +#include "draw.h" #include "cundo.h" +#include "common-ui.h" +#include "ctrain.h" +#include <inttypes.h> + +#include <stdint.h> + +#define SLOG_FMT "0x%.12" PRIxPTR + + +/**************************************************************************** + * + * RPRINTF + * + */ + + +#define RBUFF_SIZE (8192) +static char rbuff[RBUFF_SIZE+1]; +static int roff; +static int rbuff_record = 0; + +EXPORT void Rdump( FILE * outf ) +{ + fprintf( outf, "Record Buffer:\n" ); + rbuff[RBUFF_SIZE] = '\0'; + fprintf( outf, "%s", rbuff+roff ); + rbuff[roff] = '\0'; + fprintf( outf, "%s", rbuff ); + memset( rbuff, 0, sizeof rbuff ); + roff = 0; +} + + +static void Rprintf( + char * format, + ... ) +{ + static char buff[STR_SIZE]; + char * cp; + va_list ap; + va_start( ap, format ); + vsprintf( buff, format, ap ); + va_end( ap ); + if (rbuff_record >= 1) { + lprintf( buff ); + } + for ( cp=buff; *cp; cp++ ) { + rbuff[roff] = *cp; + roff++; + if (roff>=RBUFF_SIZE) { + roff=0; + } + } +} /***************************************************************************** * @@ -48,20 +139,20 @@ static int log_undo = 0; /**< loglevel, can only be set at compile time */ #define UNDO_STACK_SIZE (10) typedef struct { - wIndex_t modCnt; - wIndex_t newCnt; - wIndex_t delCnt; - wIndex_t trackCount; - track_p newTrks; - long undoStart; - long undoEnd; - long redoStart; - long redoEnd; - BOOL_T needRedo; - track_p * oldTail; - track_p * newTail; - char * label; - } undoStack_t, *undoStack_p; + wIndex_t modCnt; + wIndex_t newCnt; + wIndex_t delCnt; + wIndex_t trackCount; + track_p newTrks; + uintptr_t undoStart; + uintptr_t undoEnd; + uintptr_t redoStart; + uintptr_t redoEnd; + BOOL_T needRedo; + track_p * oldTail; + track_p * newTail; + char * label; +} undoStack_t, *undoStack_p; static undoStack_t undoStack[UNDO_STACK_SIZE]; static wIndex_t undoHead = -1; @@ -76,6 +167,8 @@ static BOOL_T recordUndo = 1; #define UASSERT( ARG, VAL ) \ if (!(ARG)) return UndoFail( #ARG, VAL, __FILE__, __LINE__ ) +#define UASSERT2( ARG, VAL ) \ + if (!(ARG)) { UndoFail( #ARG, VAL, __FILE__, __LINE__ ); return; } #define INC_UNDO_INX( INX ) {\ if (++INX >= UNDO_STACK_SIZE) \ @@ -90,11 +183,11 @@ static BOOL_T recordUndo = 1; typedef char streamBlocks_t[BSTREAM_SIZE]; typedef streamBlocks_t *streamBlocks_p; typedef struct { - dynArr_t stream_da; - long startBInx; - long end; - long curr; - } stream_t; + dynArr_t stream_da; + long startBInx; + uintptr_t end; + uintptr_t curr; +} stream_t; typedef stream_t *stream_p; static stream_t undoStream; static stream_t redoStream; @@ -118,7 +211,7 @@ static void DumpStream( FILE * outf, stream_p stream, char * name ) { long binx; long i, j; - long off; + uintptr_t off; streamBlocks_p blk; int zeroCnt; static char zeros[16] = { 0 }; @@ -131,62 +224,78 @@ static void DumpStream( FILE * outf, stream_p stream, char * name ) if ( memcmp( &((*blk)[i]), zeros, 16 ) == 0 ) { zeroCnt++; } else { - if ( zeroCnt == 2 ) - fprintf( outf, "%6.6lx 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n", off-16 ); + if ( zeroCnt == 2 ) { + fprintf( outf, "%6.6lx 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n", + (unsigned long)off-16 ); + } zeroCnt = 0; } if ( zeroCnt <= 1 ) { - fprintf( outf, "%6.6lx ", off ); + fprintf( outf, SLOG_FMT" ", off ); for ( j=0; j<16; j++ ) { fprintf( outf, "%2.2x ", (unsigned char)((*blk)[i+j]) ); } fprintf( outf, "\n" ); } else if ( zeroCnt == 3 ) { - fprintf( outf, "%6.6lx .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..\n", off ); + fprintf( outf, SLOG_FMT" .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..\n", + off ); } off += 16; } } - if ( zeroCnt > 2 ) - fprintf( outf, "%6.6lx 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n", off-16 ); + if ( zeroCnt > 2 ) { + fprintf( outf, SLOG_FMT" 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n", + off-16 ); + } } -static BOOL_T UndoFail( char * cause, long val, char * fileName, int lineNumber ) +static BOOL_T UndoFail( char * cause, uintptr_t val, char * fileName, + int lineNumber ) { int inx, cnt; undoStack_p us; FILE * outf; time_t clock; - char *temp; - NoticeMessage( MSG_UNDO_ASSERT, _("Ok"), NULL, fileName, lineNumber, val, val, cause ); + char *temp; + NoticeMessage( MSG_UNDO_ASSERT, _("Ok"), NULL, fileName, lineNumber, val, val, + cause ); MakeFullpath(&temp, workingDir, sUndoF, NULL); outf = fopen( temp, "a+" ); free(temp); if ( outf == NULL ) { - NoticeMessage( MSG_OPEN_FAIL, _("Ok"), NULL, _("Undo Trace"), temp, strerror(errno) ); + NoticeMessage( MSG_OPEN_FAIL, _("Ok"), NULL, _("Undo Trace"), temp, + strerror(errno) ); return FALSE; } time( &clock ); - fprintf(outf, "\nUndo Assert: %s @ %s:%d (%s)\n", cause, fileName, lineNumber, ctime(&clock) ); - fprintf(outf, "Val = %ld(%lx)\n", val, val ); - fprintf(outf, "to_first=%lx, to_last=%lx\n", (long)to_first, (long)to_last ); - fprintf(outf, "undoHead=%d, doCount=%d, undoCount=%d\n", undoHead, doCount, undoCount ); - if (undoHead >= 0 && undoHead < UNDO_STACK_SIZE) + + fprintf(outf, "\nUndo Assert: %s @ %s:%d (%s)\n", cause, fileName, lineNumber, + ctime(&clock) ); + fprintf(outf, "Val = %lld(" SLOG_FMT ")\n", (long long)val, val ); + fprintf(outf, "to_first="SLOG_FMT", to_last="SLOG_FMT"\n", (uintptr_t)to_first, + (uintptr_t)to_last ); + fprintf(outf, "undoHead=%d, doCount=%d, undoCount=%d\n", undoHead, doCount, + undoCount ); + if (undoHead >= 0 && undoHead < UNDO_STACK_SIZE) { inx=undoHead; - else + } else { inx = 0; + } for (cnt=0; cnt<UNDO_STACK_SIZE; cnt++) { us = &undoStack[inx]; - fprintf( outf, "US[%d]: M:%d N:%d D:%d TC:%d NT:%lx OT:%lx NT:%lx US:%lx UE:%lx RS:%lx RE:%lx NR:%d\n", - inx, us->modCnt, us->newCnt, us->delCnt, us->trackCount, - (long)us->newTrks, (long)us->oldTail, (long)us->newTail, - us->undoStart, us->undoEnd, us->redoStart, us->redoEnd, us->needRedo ); + fprintf( outf, + "US[%d]: M:%d N:%d D:%d TC:%d NT:"SLOG_FMT" OT:"SLOG_FMT" NT:"SLOG_FMT" US:"SLOG_FMT" UE:"SLOG_FMT" RS:"SLOG_FMT" RE:"SLOG_FMT" NR:%d\n", + inx, us->modCnt, us->newCnt, us->delCnt, us->trackCount, + (uintptr_t)us->newTrks, (uintptr_t)us->oldTail, (uintptr_t)us->newTail, + us->undoStart, us->undoEnd, us->redoStart, us->redoEnd, us->needRedo ); INC_UNDO_INX(inx); } - fprintf( outf, "Undo: SBI:%ld E:%lx C:%lx SC:%d SM:%d\n", - undoStream.startBInx, undoStream.end, undoStream.curr, undoStream.stream_da.cnt, undoStream.stream_da.max ); - fprintf( outf, "Redo: SBI:%ld E:%lx C:%lx SC:%d SM:%d\n", - redoStream.startBInx, redoStream.end, redoStream.curr, redoStream.stream_da.cnt, redoStream.stream_da.max ); + fprintf( outf, "Undo: SBI:%ld E:"SLOG_FMT" C:"SLOG_FMT" SC:%d SM:%d\n", + undoStream.startBInx, undoStream.end, undoStream.curr, undoStream.stream_da.cnt, + undoStream.stream_da.max ); + fprintf( outf, "Redo: SBI:%ld E:"SLOG_FMT" C:"SLOG_FMT" SC:%d SM:%d\n", + redoStream.startBInx, redoStream.end, redoStream.curr, redoStream.stream_da.cnt, + redoStream.stream_da.max ); DumpStream( outf, &undoStream, "undoStream" ); DumpStream( outf, &redoStream, "redoStream" ); Rdump(outf); @@ -199,13 +308,15 @@ static BOOL_T UndoFail( char * cause, long val, char * fileName, int lineNumber BOOL_T ReadStream( stream_t * stream, void * ptr, int size ) { - long binx, boff, brem; + size_t binx, boff, brem; streamBlocks_p blk; if ( stream->curr+size > stream->end ) { - UndoFail( "Overrun on stream", (long)(stream->curr+size), __FILE__, __LINE__ ); + UndoFail( "Overrun on stream", (uintptr_t)(stream->curr+size), __FILE__, + __LINE__ ); return FALSE; } -LOG( log_undo, 5, ( "ReadStream( , %lx, %d ) %ld %ld %ld\n", (long)ptr, size, stream->startBInx, stream->curr, stream->end ) ) + LOG( log_undo, 5, ( "ReadStream( , "SLOG_FMT", %d ) %ld %ld %ld\n", + (uintptr_t)ptr, size, stream->startBInx, stream->curr, stream->end ) ) binx = stream->curr/BSTREAM_SIZE; boff = stream->curr%BSTREAM_SIZE; stream->curr += size; @@ -231,11 +342,14 @@ LOG( log_undo, 5, ( "ReadStream( , %lx, %d ) %ld %ld %ld\n", (long)ptr, size, st BOOL_T WriteStream( stream_p stream, void * ptr, int size ) { - long binx, boff, brem; + size_t binx, boff, brem; streamBlocks_p blk; -LOG( log_undo, 5, ( "WriteStream( , %lx, %d ) %ld %ld %ld\n", (long)ptr, size, stream->startBInx, stream->curr, stream->end ) ) - if (size == 0) + LOG( log_undo, 5, + ( "WriteStream( , "SLOG_FMT", %d ) %ld "SLOG_FMT" "SLOG_FMT"\n", (uintptr_t)ptr, + size, stream->startBInx, stream->curr, stream->end ) ) + if (size == 0) { return TRUE; + } binx = stream->end/BSTREAM_SIZE; boff = stream->end%BSTREAM_SIZE; stream->end += size; @@ -254,7 +368,7 @@ LOG( log_undo, 5, ( "WriteStream( , %lx, %d ) %ld %ld %ld\n", (long)ptr, size, s if (size > brem) { memcpy( &(*blk)[boff], ptr, (size_t)brem ); ptr = (char*)ptr + brem; - size -= (size_t)brem; + size -= (int)brem; binx++; boff = 0; brem = BSTREAM_SIZE; @@ -266,26 +380,30 @@ LOG( log_undo, 5, ( "WriteStream( , %lx, %d ) %ld %ld %ld\n", (long)ptr, size, s return TRUE; } -BOOL_T TrimStream( stream_p stream, long off ) +BOOL_T TrimStream( stream_p stream, uintptr_t off ) { - long binx, cnt, inx; + size_t binx, cnt, inx; streamBlocks_p blk; -LOG( log_undo, 3, ( "TrimStream( , %ld )\n", off ) ) + LOG( log_undo, 3, ( " TrimStream( , %ld )\n", off ) ) binx = off/BSTREAM_SIZE; cnt = binx-stream->startBInx; - if (recordUndo) - Rprintf("Trim(%ld) %ld blocks (out of %d)\n", off, cnt, stream->stream_da.cnt); + if (recordUndo) { + Rprintf("Trim("SLOG_FMT") %ld blocks (out of %d)\n", off, cnt, + stream->stream_da.cnt); + } UASSERT( cnt >= 0 && cnt <= stream->stream_da.cnt, cnt ); - if (cnt == 0) + if (cnt == 0) { return TRUE; + } for (inx=0; inx<cnt; inx++) { blk = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); MyFree( blk ); } for (inx=cnt; inx<stream->stream_da.cnt; inx++ ) { - DYNARR_N( streamBlocks_p, stream->stream_da, inx-cnt ) = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); + DYNARR_N( streamBlocks_p, stream->stream_da, + inx-cnt ) = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); } - stream->startBInx = binx; + stream->startBInx =(long)binx; stream->stream_da.cnt -= (wIndex_t)cnt; UASSERT( stream->stream_da.cnt >= 0, stream->stream_da.cnt ); return TRUE; @@ -300,32 +418,37 @@ void ClearStream( stream_p stream ) blk = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); MyFree( blk ); } - stream->stream_da.cnt = 0; - stream->startBInx = stream->end = stream->curr = 0; + DYNARR_RESET( streamBlocks_p, stream->stream_da ); + stream->startBInx = 0; + stream->end = stream->curr = 0; } -BOOL_T TruncateStream( stream_p stream, long off ) +BOOL_T TruncateStream( stream_p stream, uintptr_t off ) { - long binx, boff, cnt, inx; + size_t binx, boff, cnt, inx; streamBlocks_p blk; -LOG( log_undo, 3, ( "TruncateStream( , %ld )\n", off ) ) + LOG( log_undo, 3, ( "TruncateStream( , %ld )\n", off ) ) binx = off/BSTREAM_SIZE; boff = off%BSTREAM_SIZE; - if (boff!=0) + if (boff!=0) { binx++; + } binx -= stream->startBInx; cnt = stream->stream_da.cnt-binx; - if (recordUndo) - Rprintf("Truncate(%ld) %ld blocks (out of %d)\n", off, cnt, stream->stream_da.cnt); + if (recordUndo) { + Rprintf("Truncate("SLOG_FMT") %ld blocks (out of %d)\n", off, cnt, + stream->stream_da.cnt); + } UASSERT( cnt >= 0 && cnt <= stream->stream_da.cnt, cnt ); - if (cnt == 0) + if (cnt == 0) { return TRUE; + } for (inx=binx; inx<stream->stream_da.cnt; inx++) { blk = DYNARR_N( streamBlocks_p, stream->stream_da, inx ); MyFree( blk ); } - stream->stream_da.cnt = (wIndex_t)binx; + DYNARR_SET( streamBlocks_p, stream->stream_da, (wIndex_t)binx ); stream->end = off; UASSERT( stream->stream_da.cnt >= 0, stream->stream_da.cnt ); return TRUE; @@ -337,76 +460,115 @@ BOOL_T WriteObject( stream_p stream, char op, track_p trk ) void * buff = NULL; long len = 0; if (!WriteStream( stream, &op, sizeof op ) || - !WriteStream( stream, &trk, sizeof trk ) || - !WriteStream( stream, trk, sizeof *trk ) || - !WriteStream( stream, trk->endPt, trk->endCnt * sizeof trk->endPt[0] ) || - !WriteStream( stream, trk->extraData, trk->extraSize )) + !WriteStream( stream, &trk, sizeof trk ) || + !WriteStream( stream, trk, sizeof *trk ) || + !WriteStream( stream, trk->endPt, EndPtSize(trk->endCnt) ) || + !WriteStream( stream, trk->extraData, trk->extraSize )) { return FALSE; + } /* Add a copy of the any type specific data before it is tampered with, for example */ - StoreTrackData(trk,&buff,&len); - if (!WriteStream( stream, &len, sizeof len )) + if ( !IsTrackDeleted(trk) ) { + StoreTrackData(trk,&buff,&len); + } else { + len = 0; + buff = NULL; + } + if (!WriteStream( stream, &len, sizeof len )) { return FALSE; + } if (len) - if (!WriteStream( stream, buff, len )) + if (!WriteStream( stream, buff, len )) { return FALSE; + } return TRUE; } +/** + * Read an object from a stream + * + * \param stream + * \param needRedo copy current object to redoStream + * + */ static BOOL_T ReadObject( stream_p stream, BOOL_T needRedo ) { track_p trk; track_t tempTrk; char op; - if (!ReadStream( stream, &op, sizeof op )) + if (!ReadStream( stream, &op, sizeof op )) { return FALSE; - if (!ReadStream( stream, &trk, sizeof trk )) + } + if (!ReadStream( stream, &trk, sizeof trk )) { return FALSE; + } + LOG( log_undo, 4, ( " @ " SLOG_FMT " %s\n", stream->curr-1, + op==ModifyOp?"Mod":"Del" ) ); if (needRedo) { - if (!WriteObject( &redoStream, op, trk )) + if (!WriteObject( &redoStream, op, trk )) { return FALSE; + } } - if (!ReadStream( stream, &tempTrk, sizeof tempTrk )) + if (!ReadStream( stream, &tempTrk, sizeof tempTrk )) { return FALSE; - if (tempTrk.endCnt != trk->endCnt) - tempTrk.endPt = MyRealloc( trk->endPt, tempTrk.endCnt * sizeof tempTrk.endPt[0] ); - else + } + if (op == ModifyOp) { + UASSERT( (op==ModifyOp) && !IsTrackDeleted(&tempTrk), GetTrkIndex(&tempTrk) ); + } + // op==DeleteOp doesnot imply that tmpTrk.delete == TRUE: SetDeleteOpInStream + if (tempTrk.endCnt != trk->endCnt) { + tempTrk.endPt = MyRealloc( trk->endPt, EndPtSize(tempTrk.endCnt) ); + } else { tempTrk.endPt = trk->endPt; - if (!ReadStream( stream, tempTrk.endPt, tempTrk.endCnt * sizeof tempTrk.endPt[0] )) + } + if (!ReadStream( stream, tempTrk.endPt, EndPtSize(tempTrk.endCnt) )) { return FALSE; - if (tempTrk.extraSize != trk->extraSize) - tempTrk.extraData = MyRealloc( trk->extraData, tempTrk.extraSize ); - else + } + if (tempTrk.extraSize != trk->extraSize) { + tempTrk.extraData = (extraDataBase_t*)MyRealloc( trk->extraData, + tempTrk.extraSize ); + } else { tempTrk.extraData = trk->extraData; - if (!ReadStream( stream, tempTrk.extraData, tempTrk.extraSize )) + } + if (!ReadStream( stream, tempTrk.extraData, tempTrk.extraSize )) { return FALSE; + } long Addsize; void * tempBuff; /* Fix up pts to be as big as it was before -> because it may have changed since */ - if (!ReadStream (stream, &Addsize, sizeof Addsize)) + if (!ReadStream (stream, &Addsize, sizeof Addsize)) { return FALSE; + } if (Addsize) { tempBuff = MyMalloc(Addsize); - if (!ReadStream( stream, tempBuff, Addsize )) + if (!ReadStream( stream, tempBuff, Addsize )) { return FALSE; - ReplayTrackData(&tempTrk, tempBuff, Addsize); + } + if ( ! IsTrackDeleted(&tempTrk) ) { + ReplayTrackData(&tempTrk, tempBuff, Addsize); + } MyFree(tempBuff); } - RebuildTrackSegs(&tempTrk); //If we had an array of Segs - recreate it - if (recordUndo) Rprintf( "Restore T%D(%d) @ %lx\n", trk->index, tempTrk.index, (long)trk ); + if ( ! IsTrackDeleted(&tempTrk) ) { + RebuildTrackSegs(&tempTrk); //If we had an array of Segs - recreate it + } + if (recordUndo) { Rprintf( "Restore T%D(%d) @ "SLOG_FMT"\n", trk->index, tempTrk.index, (uintptr_t)trk ); } tempTrk.index = trk->index; tempTrk.next = trk->next; - if ( (tempTrk.bits&TB_CARATTACHED) != 0 ) + if ( (tempTrk.bits&TB_CARATTACHED) != 0 ) { needAttachTrains = TRUE; + } tempTrk.bits &= ~TB_TEMPBITS; *trk = tempTrk; - if (!trk->deleted) + if (!IsTrackDeleted(trk)) { ClrTrkElev( trk ); + } return TRUE; } -static BOOL_T RedrawInStream( stream_p stream, long start, long end, BOOL_T draw ) +static BOOL_T RedrawInStream( stream_p stream, uintptr_t start, uintptr_t end, + BOOL_T draw ) { char op; track_p trk; @@ -414,49 +576,70 @@ static BOOL_T RedrawInStream( stream_p stream, long start, long end, BOOL_T draw stream->curr = start; while (stream->curr < end ) { if (!ReadStream( stream, &op, sizeof op ) || - !ReadStream( stream, &trk, sizeof trk ) || - !ReadStream( stream, &tempTrk, sizeof tempTrk ) ) + !ReadStream( stream, &trk, sizeof trk ) || + !ReadStream( stream, &tempTrk, sizeof tempTrk ) ) { return FALSE; - stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0];; + } + stream->curr += tempTrk.extraSize + EndPtSize(tempTrk.endCnt); long Addsize; - if (!ReadStream( stream, &Addsize, sizeof Addsize )) - return FALSE; + if (!ReadStream( stream, &Addsize, sizeof Addsize )) { + return FALSE; + } stream->curr += Addsize; - if (!trk->deleted) { - if (draw) + if (!IsTrackDeleted(trk)) { + if (draw) { DrawNewTrack( trk ); - else + } else { UndrawNewTrack( trk ); + } } } return TRUE; } -static BOOL_T DeleteInStream( stream_p stream, long start, long end ) +/** + * Delete unreferenced objects from stream + * + * \param stream + * \param start + * \param end + * + * The current transaction is being recycled: + * unlink and free any deleted objects from the old transaction + */ +static BOOL_T DeleteInStream( stream_p stream, uintptr_t start, uintptr_t end ) { char op; track_p trk; track_p *ptrk; track_t tempTrk; int delCount = 0; -LOG( log_undo, 3, ( "DeleteInSteam( , %ld, %ld )\n", start, end ) ) + LOG( log_undo, 3, ( " DeleteInStream( , "SLOG_FMT", "SLOG_FMT" )\n", start, + end ) ) stream->curr = start; while (stream->curr < end ) { - if (!ReadStream( stream, &op, sizeof op )) + if (!ReadStream( stream, &op, sizeof op )) { return FALSE; + } UASSERT( op == ModifyOp || op == DeleteOp, (long)op ); + LOG( log_undo, 4, ( " @ " SLOG_FMT " %s\n", stream->curr-1, + op==ModifyOp?"Mod":"Del" ) ); if (!ReadStream( stream, &trk, sizeof trk ) || - !ReadStream( stream, &tempTrk, sizeof tempTrk )) + !ReadStream( stream, &tempTrk, sizeof tempTrk )) { return FALSE; - stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0]; + } + stream->curr += tempTrk.extraSize + EndPtSize(tempTrk.endCnt); long Addsize; - if (!ReadStream( stream, &Addsize, sizeof Addsize )) + if (!ReadStream( stream, &Addsize, sizeof Addsize )) { return FALSE; + } stream->curr += Addsize; if (op == DeleteOp) { - if (recordUndo) Rprintf( " Free T%D(%d) @ %lx\n", trk->index, tempTrk.index, (long)trk ); - UASSERT( IsTrackDeleted(trk), (long)trk ); + if (recordUndo) { Rprintf( " Free T%D(%d) @ "SLOG_FMT"\n", trk->index, tempTrk.index, (uintptr_t)trk ); } + LOG( log_undo, 3, ( " Free T%d @ "SLOG_FMT"\n", GetTrkIndex(trk), + (uintptr_t)trk ) ); + UASSERT( IsTrackDeleted(trk), GetTrkIndex(trk) ); trk->index = -1; delCount++; } @@ -465,8 +648,9 @@ LOG( log_undo, 3, ( "DeleteInSteam( , %ld, %ld )\n", start, end ) ) if (delCount) { for (ptrk=&to_first; *ptrk; ) { if ((*ptrk)->index == -1) { + // old track to be discarded: Unlink and Free it trk = *ptrk; - UASSERT( IsTrackDeleted(trk), (long)trk ); + UASSERT( IsTrackDeleted(trk), (uintptr_t)trk ); *ptrk = trk->next; FreeTrack(trk); } else { @@ -479,36 +663,57 @@ LOG( log_undo, 3, ( "DeleteInSteam( , %ld, %ld )\n", start, end ) ) } -static BOOL_T SetDeleteOpInStream( stream_p stream, long start, long end, track_p trk0 ) +/** + * Find undo record for 'trk' and change op from Modify to Delete + * + * \param stream + * \param start + * \param end + * \param trk + * + * Note: does not set trk->delete flag + */ +static BOOL_T SetDeleteOpInStream( stream_p stream, uintptr_t start, + uintptr_t end, track_p trk0 ) { char op; track_p trk; track_t tempTrk; - long binx, boff; + size_t binx, boff; streamBlocks_p blk; + LOG( log_undo, 3, ( " SetDeleteOpInStream T%d @ "SLOG_FMT"\n", + GetTrkIndex(trk0), (uintptr_t)trk0) ); stream->curr = start; while (stream->curr < end) { binx = stream->curr/BSTREAM_SIZE; binx -= stream->startBInx; boff = stream->curr%BSTREAM_SIZE; - if (!ReadStream( stream, &op, sizeof op )) + if (!ReadStream( stream, &op, sizeof op )) { return FALSE; + } UASSERT( op == ModifyOp || op == DeleteOp, (long)op ); - if (!ReadStream( stream, &trk, sizeof trk ) ) + LOG( log_undo, 4, ( " @ " SLOG_FMT " %s\n", stream->curr-1, + op==ModifyOp?"Mod":"Del" ) ); + if (!ReadStream( stream, &trk, sizeof trk ) ) { + return FALSE; + } + if (!ReadStream( stream, &tempTrk, sizeof tempTrk )) { return FALSE; + } if (trk == trk0) { UASSERT( op == ModifyOp, (long)op ); blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx ); memcpy( &(*blk)[boff], &DeleteOp, sizeof DeleteOp ); + // Should set .delete flag in stream + LOG( log_undo, 3, ( " -> Delete\n") ); return TRUE; } - if (!ReadStream( stream, &tempTrk, sizeof tempTrk )) - return FALSE; - stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0]; + stream->curr += tempTrk.extraSize + EndPtSize(tempTrk.endCnt); long Addsize; - if (!ReadStream( stream, &Addsize, sizeof Addsize)) - return FALSE; + if (!ReadStream( stream, &Addsize, sizeof Addsize)) { + return FALSE; + } stream->curr += Addsize; } UASSERT( "Cannot find undo record to convert to DeleteOp", 0 ); @@ -553,23 +758,34 @@ static track_p * FindParent( track_p trk, int lineNum ) { track_p *ptrk; ptrk = &to_first; - while ( 1 ) { - if ( *ptrk == trk ) + while ( 1 ) { + if ( *ptrk == trk ) { return ptrk; - if (*ptrk == NULL) + } + if (*ptrk == NULL) { break; + } ptrk = &(*ptrk)->next; } - UndoFail( "Cannot find trk on list", (long)trk, "cundo.c", lineNum ); + UndoFail( "Cannot find trk on list", (uintptr_t)trk, "cundo.c", lineNum ); return NULL; } static int undoIgnoreEmpty = 0; + +/** + * Start an Undo transcation + * + * \param label help text for balloon help + * \param format logging info + * + * + */ void UndoStart( - char * label, - char * format, - ... ) + char * label, + char * format, + ... ) { static char buff[STR_SIZE]; va_list ap; @@ -578,22 +794,21 @@ void UndoStart( int inx; int usp; -LOG( log_undo, 1, ( "UndoStart(%s) [%d] d:%d u:%d us:%ld\n", label, undoHead, doCount, undoCount, undoStream.end ) ) + LOG( log_undo, 1, ( "UndoStart[%d] (%s) d:%d u:%d us:"SLOG_FMT"\n", undoHead, + label, undoHead, doCount, undoCount, undoStream.end ) ) if (recordUndo) { va_start( ap, format ); vsprintf( buff, format, ap ); va_end( ap ); - Rprintf( "Start(%s)[%d] d:%d u:%d us:%ld\n", buff, undoHead, doCount, undoCount, undoStream.end ); + Rprintf( "Start(%s)[%d] d:%d u:%d us:"SLOG_FMT"\n", buff, undoHead, doCount, + undoCount, undoStream.end ); } if ( undoHead >= 0 ) { us = &undoStack[undoHead]; if ( us->modCnt == 0 && us->delCnt == 0 && us->newCnt == 0 ) { -#ifndef WINDOWS -#ifdef DEBUG - printf( "undoStart noop: %s - %s\n", us->label?us->label:"<>", label?label:"<>" ); -#endif -#endif + LOG( log_undo, 1, ( " noop: %s - %s\n", us->label?us->label:"<>", + label?label:"<>" ) ); if ( undoIgnoreEmpty ) { us->label = label; return; @@ -603,36 +818,41 @@ LOG( log_undo, 1, ( "UndoStart(%s) [%d] d:%d u:%d us:%ld\n", label, undoHead, do INC_UNDO_INX(undoHead); us = &undoStack[undoHead]; - changed++; - SetWindowTitle(); + + SetFileChanged(); if (doCount == UNDO_STACK_SIZE) { - if (recordUndo) Rprintf( " Wrapped N:%d M:%d D:%d\n", us->newCnt, us->modCnt, us->delCnt ); + if (recordUndo) { Rprintf( " Wrapped N:%d M:%d D:%d\n", us->newCnt, us->modCnt, us->delCnt ); } /* wrapped around stack */ /* if track saved in undoStream is deleted then really deleted since we can't get it back */ - if (!DeleteInStream( &undoStream, us->undoStart, us->undoEnd )) + if (!DeleteInStream( &undoStream, us->undoStart, us->undoEnd )) { return; + } /* strip off unused head of stream */ - if (!TrimStream( &undoStream, us->undoEnd )) + if (!TrimStream( &undoStream, us->undoEnd )) { return; + } } else if (undoCount != 0) { - if (recordUndo) Rprintf( " Undid N:%d M:%d D:%d\n", us->newCnt, us->modCnt, us->delCnt ); + if (recordUndo) { Rprintf( " Undid N:%d M:%d D:%d\n", us->newCnt, us->modCnt, us->delCnt ); } /* reusing an undid entry */ /* really delete all new tracks since this point */ for( inx=0,usp = undoHead; inx<undoCount; inx++ ) { us1 = &undoStack[usp]; - if (recordUndo) Rprintf(" U[%d] N:%d\n", usp, us1->newCnt ); + if (recordUndo) { Rprintf(" U[%d] N:%d\n", usp, us1->newCnt ); } for (trk=us1->newTrks; trk; trk=next) { - if (recordUndo) Rprintf( " Free T%d @ %lx\n", trk->index, (long)trk ); - /*ASSERT( IsTrackDeleted(trk) );*/ + if (recordUndo) { Rprintf( " Free T%d @ "SLOG_FMT"\n", trk->index, (uintptr_t)trk ); } + // trk->delete may not be TRUE, see SetDeleteOpInStream + LOG( log_undo, 4, (" Free T%d @ "SLOG_FMT"\n", trk->index, + (uintptr_t)trk ) ); next = trk->next; FreeTrack( trk ); } INC_UNDO_INX(usp); } /* strip off unused tail of stream */ - if (!TruncateStream( &undoStream, us->undoStart )) + if (!TruncateStream( &undoStream, us->undoStart )) { return; + } } us->label = label; us->modCnt = 0; @@ -653,31 +873,44 @@ LOG( log_undo, 1, ( "UndoStart(%s) [%d] d:%d u:%d us:%ld\n", label, undoHead, do trk->modified = FALSE; trk->new = FALSE; } - if (doCount < UNDO_STACK_SIZE) + if (doCount < UNDO_STACK_SIZE) { doCount++; + } SetButtons( TRUE, FALSE ); } +/** + * Record Modify'd track for Undo + * \param trk + * + * If track has not been previously recorded in these Undo transaction + * or is not 'new' write the track to the undoStream which a ModifyOp flag + */ BOOL_T UndoModify( track_p trk ) { undoStack_p us; - if ( !undoActive ) return TRUE; - if (trk == NULL) return TRUE; + if ( !undoActive ) { return TRUE; } + if (trk == NULL) { return TRUE; } UASSERT(undoCount==0, undoCount); UASSERT(undoHead >= 0, undoHead); - UASSERT(!IsTrackDeleted(trk), (long)trk); - if (trk->modified || trk->new) + UASSERT(!IsTrackDeleted(trk), GetTrkIndex(trk)); + if (trk->modified || trk->new) { return TRUE; -LOG( log_undo, 2, ( " UndoModify( T%d, E%d, X%ld )\n", trk->index, trk->endCnt, trk->extraSize ) ) - if ( (GetTrkBits(trk)&TB_CARATTACHED)!=0 ) + } + LOG( log_undo, 2, ( " UndoModify( T%d, E%d, X%ld @ "SLOG_FMT"\n", trk->index, + trk->endCnt, trk->extraSize, (uintptr_t)trk ) ) + if ( (GetTrkBits(trk)&TB_CARATTACHED)!=0 ) { needAttachTrains = TRUE; + } us = &undoStack[undoHead]; - if (recordUndo) - Rprintf( " MOD T%d @ %lx\n", trk->index, (long)trk ); - if (!WriteObject( &undoStream, ModifyOp, trk )) + if (recordUndo) { + Rprintf( " MOD T%d @ "SLOG_FMT"\n", trk->index, (uintptr_t)trk ); + } + if (!WriteObject( &undoStream, ModifyOp, trk )) { return FALSE; + } us->undoEnd = undoStream.end; trk->modified = TRUE; us->modCnt++; @@ -685,32 +918,52 @@ LOG( log_undo, 2, ( " UndoModify( T%d, E%d, X%ld )\n", trk->index, trk->endCn } +/** + * Record that the track has been deleted + * + * \param trk + * + * If the track has been Modified, then update undoStream to change op to DeleteOp + * If the track is not New, then write the record to the undoSteam with a DeleteOp + * When this undo transaction is recycled, DeleteOp records will unlinked and freed. + * + * Otherwise, we're deleting a New track: remove it from track list and discard it + */ BOOL_T UndoDelete( track_p trk ) { undoStack_p us; - if ( !undoActive ) return TRUE; -LOG( log_undo, 2, ( " UndoDelete( T%d, E%d, X%ld )\n", trk->index, trk->endCnt, trk->extraSize ) ) - if ( (GetTrkBits(trk)&TB_CARATTACHED)!=0 ) + if ( !undoActive ) { return TRUE; } + LOG( log_undo, 2, ( " UndoDelete( T%d, E%d, X%ld @ "SLOG_FMT" )\n", + trk->index, trk->endCnt, trk->extraSize, (uintptr_t)trk ) ) + if ( (GetTrkBits(trk)&TB_CARATTACHED)!=0 ) { needAttachTrains = TRUE; + } us = &undoStack[undoHead]; - if (recordUndo) - Rprintf( " DEL T%d @ %lx\n", trk->index, (long)trk ); - UASSERT( !IsTrackDeleted(trk), (long)trk ); + if (recordUndo) { + Rprintf( " DEL T%d @ "SLOG_FMT"\n", trk->index, (uintptr_t)trk ); + } + UASSERT( !IsTrackDeleted(trk), trk->index ); if ( trk->modified ) { - if (!SetDeleteOpInStream( &undoStream, us->undoStart, us->undoEnd, trk )) + if (!SetDeleteOpInStream( &undoStream, us->undoStart, us->undoEnd, trk )) { return FALSE; + } } else if ( !trk->new ) { - if (!WriteObject( &undoStream, DeleteOp, trk )) - return FALSE; + LOG( log_undo, 3, ( " Write DeleteOp object\n" ) ); + if (!WriteObject( &undoStream, DeleteOp, trk )) { + return FALSE; + } us->undoEnd = undoStream.end; } else { + LOG( log_undo, 3, ( " Remove New object\n" ) ); track_p * ptrk; - if (us->newTrks == trk) + if (us->newTrks == trk) { us->newTrks = trk->next; - if (!(ptrk = FindParent( trk, __LINE__ ))) + } + if (!(ptrk = FindParent( trk, __LINE__ ))) { return FALSE; + } if (trk->next == NULL) { - UASSERT( to_last == &(*ptrk)->next, (long)&(*ptrk)->next ); + UASSERT( to_last == &(*ptrk)->next, (uintptr_t)&(*ptrk)->next ); to_last = ptrk; } *ptrk = trk->next; @@ -718,37 +971,52 @@ LOG( log_undo, 2, ( " UndoDelete( T%d, E%d, X%ld )\n", trk->index, trk->endCn us->newCnt--; return TRUE; } + ClrTrkBits( trk, TB_SELECTED ); trk->deleted = TRUE; us->delCnt++; return TRUE; } - +/** + * Record a New track for Undo + * + * \param trk + * + * New tracks are added to the end of the Track list + * Save the begining of New tracks in this Undo transaction in us->newTrks + */ BOOL_T UndoNew( track_p trk ) { - undoStack_p us; - if (!undoActive) + undoStack_p us; + if (!undoActive) { return TRUE; + } + + LOG( log_undo, 2, ( " UndoNew( T%d @ "SLOG_FMT")\n", trk->index, + (uintptr_t)trk ) ) -LOG( log_undo, 2, ( " UndoNew( T%d )\n", trk->index ) ) - - if (recordUndo) - Rprintf( " NEW T%d @%lx\n", trk->index, (long)trk ); + if (recordUndo) { + Rprintf( " NEW T%d @"SLOG_FMT"\n", trk->index, (uintptr_t)trk ); + } UASSERT(undoCount==0, undoCount); UASSERT(undoHead >= 0, undoHead); us = &undoStack[undoHead]; trk->new = TRUE; - if (us->newTrks == NULL) + if (us->newTrks == NULL) { us->newTrks = trk; + } us->newCnt++; - + return TRUE; } +/** + * End of a Undo transaction + */ void UndoEnd( void ) { - if (recordUndo) Rprintf( "End[%d] d:%d\n", undoHead, doCount ); + if (recordUndo) { Rprintf( "End[%d] d:%d\n", undoHead, doCount ); } /*undoActive = FALSE;*/ if ( needAttachTrains ) { AttachTrains(); @@ -758,10 +1026,13 @@ void UndoEnd( void ) } +/** + * Reset the Undo state + */ void UndoClear( void ) { int inx; -LOG( log_undo, 2, ( " UndoClear()\n" ) ) + LOG( log_undo, 2, ( " UndoClear()\n" ) ) undoActive = FALSE; undoHead = -1; undoCount = 0; @@ -775,8 +1046,19 @@ LOG( log_undo, 2, ( " UndoClear()\n" ) ) } -BOOL_T UndoUndo( void ) +EXPORT wBool_t undoStatus = TRUE; + +/** + * Undo the last transaction + * + * Move any New tracks from the end of the Track list + * Cut the Track list at us->newTrks + * Read Modified/Deleted tracks from undoSteam + * Cleanup: redraw, update elevs, cars, counts, ... + */ +void UndoUndo( void * unused ) { + undoStatus = FALSE; undoStack_p us; track_p trk; wIndex_t oldCount; @@ -784,44 +1066,45 @@ BOOL_T UndoUndo( void ) if (doCount <= 0) { ErrorMessage( MSG_NO_UNDO ); - return FALSE; + return; } ConfirmReset( FALSE ); wDrawDelayUpdate( mainD.d, TRUE ); us = &undoStack[undoHead]; -LOG( log_undo, 1, ( " undoUndo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ) ) - if (recordUndo) Rprintf( "Undo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ); + LOG( log_undo, 1, ( " UndoUndo[%d] d:%d u:%d N:%d M:%d D:%d %s\n", undoHead, + doCount, undoCount, us->newCnt, us->modCnt, us->delCnt, + us->needRedo?"Redo":"" ) ) + if (recordUndo) { Rprintf( "Undo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ); } //redrawAll = (us->newCnt+us->modCnt) > incrementalDrawLimit; - redrawAll = TRUE; + redrawAll = TRUE; if (!redrawAll) { - for (trk=us->newTrks; trk; trk=trk->next ) + for (trk=us->newTrks; trk; trk=trk->next ) { UndrawNewTrack( trk ); + } RedrawInStream( &undoStream, us->undoStart, us->undoEnd, FALSE ); } - if (us->needRedo) + if (us->needRedo) { us->redoStart = us->redoEnd = redoStream.end; - for (trk=us->newTrks; trk; trk=trk->next ) { - if (recordUndo) Rprintf(" Deleting New Track T%d @ %lx\n", trk->index, (long)trk ); - UASSERT( !IsTrackDeleted(trk), (long)trk ); - trk->deleted = TRUE; } - if (!(us->oldTail=FindParent(us->newTrks,__LINE__))) - return FALSE; + if (!(us->oldTail=FindParent(us->newTrks,__LINE__))) { + return; + } us->newTail = to_last; to_last = us->oldTail; *to_last = NULL; - needAttachTrains = FALSE; undoStream.curr = us->undoStart; while ( undoStream.curr < us->undoEnd ) { - if (!ReadObject( &undoStream, us->needRedo )) - return FALSE; + if (!ReadObject( &undoStream, us->needRedo )) { + return; + } } - if (us->needRedo) + if (us->needRedo) { us->redoEnd = redoStream.end; + } us->needRedo = FALSE; if ( needAttachTrains ) { @@ -829,10 +1112,11 @@ LOG( log_undo, 1, ( " undoUndo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doC needAttachTrains = FALSE; } UpdateAllElevations(); - if (!redrawAll) + if (!redrawAll) { RedrawInStream( &undoStream, us->undoStart, us->undoEnd, TRUE ); - else + } else { DoRedraw(); + } oldCount = trackCount; trackCount = us->trackCount; @@ -847,12 +1131,21 @@ LOG( log_undo, 1, ( " undoUndo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doC SetButtons( doCount>0, TRUE ); wBalloonHelpUpdate(); wDrawDelayUpdate( mainD.d, FALSE ); - return TRUE; + undoStatus = TRUE; + return; } -BOOL_T UndoRedo( void ) +/** + * Undo and last Undo op + * + * Attach the New tracks to the end of the Track list + * Read Modified/Deleted object from redoStream + * Cleanup: redraw, update elevs, cars, counts, ... + */ +void UndoRedo( void * unused ) { + undoStatus = FALSE; undoStack_p us; wIndex_t oldCount; BOOL_T redrawAll; @@ -860,38 +1153,35 @@ BOOL_T UndoRedo( void ) if (undoCount <= 0) { ErrorMessage( MSG_NO_REDO ); - return FALSE; + return; } ConfirmReset( FALSE ); wDrawDelayUpdate( mainD.d, TRUE ); INC_UNDO_INX( undoHead ); us = &undoStack[undoHead]; -LOG( log_undo, 1, ( " undoRedo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ) ) - if (recordUndo) Rprintf( "Redo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ); + LOG( log_undo, 1, ( " UndoRedo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, + doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ) ) + if (recordUndo) { Rprintf( "Redo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doCount, undoCount, us->newCnt, us->modCnt, us->delCnt ); } //redrawAll = (us->newCnt+us->modCnt) > incrementalDrawLimit; - redrawAll = TRUE; + redrawAll = TRUE; if (!redrawAll) { RedrawInStream( &redoStream, us->redoStart, us->redoEnd, FALSE ); } - for (trk=us->newTrks; trk; trk=trk->next ) { - if (recordUndo) Rprintf(" Undeleting New Track T%d @ %lx\n", trk->index, (long)trk ); - UASSERT( IsTrackDeleted(trk), (long)trk ); - trk->deleted = FALSE; - } - UASSERT( us->newTail != NULL, (long)us->newTail ); + UASSERT2( us->newTail != NULL, (uintptr_t)us->newTail ); *to_last = us->newTrks; to_last = us->newTail; - UASSERT( (*to_last) == NULL, (long)*to_last ); + UASSERT2( (*to_last) == NULL, (uintptr_t)*to_last ); RenumberTracks(); needAttachTrains = FALSE; redoStream.curr = us->redoStart; while ( redoStream.curr < us->redoEnd ) { - if (!ReadObject( &redoStream, FALSE )) - return FALSE; + if (!ReadObject( &redoStream, FALSE )) { + return; + } } if ( needAttachTrains ) { @@ -900,11 +1190,13 @@ LOG( log_undo, 1, ( " undoRedo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doC } UpdateAllElevations(); if (!redrawAll) { - for (trk=us->newTrks; trk; trk=trk->next ) + for (trk=us->newTrks; trk; trk=trk->next ) { DrawNewTrack( trk ); + } RedrawInStream( &redoStream, us->redoStart, us->redoEnd, TRUE ); - } else + } else { DoRedraw(); + } oldCount = trackCount; trackCount = us->trackCount; @@ -919,7 +1211,8 @@ LOG( log_undo, 1, ( " undoRedo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doC SetButtons( TRUE, undoCount>0 ); wBalloonHelpUpdate(); wDrawDelayUpdate( mainD.d, FALSE ); - return TRUE; + undoStatus = TRUE; + return; } |