diff options
Diffstat (limited to 'app/bin/cundo.c')
-rw-r--r-- | app/bin/cundo.c | 354 |
1 files changed, 248 insertions, 106 deletions
diff --git a/app/bin/cundo.c b/app/bin/cundo.c index 1ed5588..fda012f 100644 --- a/app/bin/cundo.c +++ b/app/bin/cundo.c @@ -20,21 +20,63 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ASSERTs + * 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" +#include "draw.h" #include "cundo.h" +#include "common-ui.h" + +#include <inttypes.h> + +#include <stdint.h> + +#define SLOG_FMT "0x%.12" PRIxPTR /***************************************************************************** @@ -53,10 +95,10 @@ typedef struct { wIndex_t delCnt; wIndex_t trackCount; track_p newTrks; - long undoStart; - long undoEnd; - long redoStart; - long redoEnd; + uintptr_t undoStart; + uintptr_t undoEnd; + uintptr_t redoStart; + uintptr_t redoEnd; BOOL_T needRedo; track_p * oldTail; track_p * newTail; @@ -76,6 +118,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) \ @@ -92,8 +136,8 @@ typedef streamBlocks_t *streamBlocks_p; typedef struct { dynArr_t stream_da; long startBInx; - long end; - long curr; + uintptr_t end; + uintptr_t curr; } stream_t; typedef stream_t *stream_p; static stream_t undoStream; @@ -118,7 +162,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 }; @@ -132,26 +176,26 @@ static void DumpStream( FILE * outf, stream_p stream, char * name ) 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 ); + 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 ); + 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; @@ -167,9 +211,10 @@ static BOOL_T UndoFail( char * cause, long val, char * fileName, int lineNumber 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, "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; @@ -177,15 +222,15 @@ static BOOL_T UndoFail( char * cause, long val, char * fileName, int lineNumber 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", + 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, - (long)us->newTrks, (long)us->oldTail, (long)us->newTail, + (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", + 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:%lx C:%lx SC:%d SM:%d\n", + 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" ); @@ -199,13 +244,13 @@ 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,9 +276,9 @@ 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 ) ) +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; @@ -254,7 +299,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,15 +311,15 @@ 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); + 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) return TRUE; @@ -285,7 +330,7 @@ LOG( log_undo, 3, ( "TrimStream( , %ld )\n", off ) ) 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 ); } - 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; @@ -301,13 +346,14 @@ void ClearStream( stream_p stream ) MyFree( blk ); } stream->stream_da.cnt = 0; - stream->startBInx = stream->end = stream->curr = 0; + 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 ) ) binx = off/BSTREAM_SIZE; @@ -317,7 +363,7 @@ LOG( log_undo, 3, ( "TruncateStream( , %ld )\n", off ) ) 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); + 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) return TRUE; @@ -343,7 +389,12 @@ BOOL_T WriteObject( stream_p stream, char op, track_p trk ) !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 ( !IsTrackDeleted(trk) ) { + StoreTrackData(trk,&buff,&len); + } else { + len = 0; + buff = NULL; + } if (!WriteStream( stream, &len, sizeof len )) return FALSE; if (len) @@ -353,6 +404,13 @@ BOOL_T WriteObject( stream_p stream, char op, track_p trk ) } +/** + * 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; @@ -362,12 +420,17 @@ static BOOL_T ReadObject( stream_p stream, BOOL_T needRedo ) return FALSE; 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 )) return FALSE; + 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, tempTrk.endCnt * sizeof tempTrk.endPt[0] ); else @@ -375,7 +438,7 @@ static BOOL_T ReadObject( stream_p stream, BOOL_T needRedo ) if (!ReadStream( stream, tempTrk.endPt, tempTrk.endCnt * sizeof tempTrk.endPt[0] )) return FALSE; if (tempTrk.extraSize != trk->extraSize) - tempTrk.extraData = MyRealloc( trk->extraData, tempTrk.extraSize ); + tempTrk.extraData = (extraDataBase_t*)MyRealloc( trk->extraData, tempTrk.extraSize ); else tempTrk.extraData = trk->extraData; if (!ReadStream( stream, tempTrk.extraData, tempTrk.extraSize )) @@ -389,24 +452,26 @@ static BOOL_T ReadObject( stream_p stream, BOOL_T needRedo ) tempBuff = MyMalloc(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 ) 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; @@ -422,7 +487,7 @@ static BOOL_T RedrawInStream( stream_p stream, long start, long end, BOOL_T draw if (!ReadStream( stream, &Addsize, sizeof Addsize )) return FALSE; stream->curr += Addsize; - if (!trk->deleted) { + if (!IsTrackDeleted(trk)) { if (draw) DrawNewTrack( trk ); else @@ -433,19 +498,30 @@ static BOOL_T RedrawInStream( stream_p stream, long start, long end, BOOL_T draw } -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 )) 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 )) return FALSE; @@ -455,8 +531,9 @@ LOG( log_undo, 3, ( "DeleteInSteam( , %ld, %ld )\n", start, end ) ) 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 +542,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,14 +557,25 @@ 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; @@ -495,16 +584,19 @@ static BOOL_T SetDeleteOpInStream( stream_p stream, long start, long end, track_ 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 ) ) 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]; long Addsize; if (!ReadStream( stream, &Addsize, sizeof Addsize)) @@ -560,12 +652,21 @@ static track_p * FindParent( track_p trk, int lineNum ) 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, @@ -578,22 +679,18 @@ 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,8 +700,8 @@ 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 ); /* wrapped around stack */ @@ -623,8 +720,9 @@ LOG( log_undo, 1, ( "UndoStart(%s) [%d] d:%d u:%d us:%ld\n", label, undoHead, do us1 = &undoStack[usp]; 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 ); } @@ -659,6 +757,13 @@ LOG( log_undo, 1, ( "UndoStart(%s) [%d] d:%d u:%d us:%ld\n", label, undoHead, do } +/** + * 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; @@ -667,15 +772,15 @@ BOOL_T UndoModify( track_p trk ) if (trk == NULL) return TRUE; UASSERT(undoCount==0, undoCount); UASSERT(undoHead >= 0, undoHead); - UASSERT(!IsTrackDeleted(trk), (long)trk); + 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 ) ) +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 ); + Rprintf( " MOD T%d @ "SLOG_FMT"\n", trk->index, (uintptr_t)trk ); if (!WriteObject( &undoStream, ModifyOp, trk )) return FALSE; us->undoEnd = undoStream.end; @@ -685,32 +790,45 @@ 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 ) ) +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 ); + 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 )) return FALSE; } else if ( !trk->new ) { + 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) us->newTrks = trk->next; 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,22 +836,30 @@ 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) return TRUE; -LOG( log_undo, 2, ( " UndoNew( T%d )\n", trk->index ) ) +LOG( log_undo, 2, ( " UndoNew( T%d @ "SLOG_FMT")\n", trk->index, (uintptr_t)trk ) ) if (recordUndo) - Rprintf( " NEW T%d @%lx\n", trk->index, (long)trk ); + Rprintf( " NEW T%d @"SLOG_FMT"\n", trk->index, (uintptr_t)trk ); UASSERT(undoCount==0, undoCount); UASSERT(undoHead >= 0, undoHead); us = &undoStack[undoHead]; @@ -746,6 +872,9 @@ LOG( log_undo, 2, ( " UndoNew( T%d )\n", trk->index ) ) } +/** + * End of a Undo transaction + */ void UndoEnd( void ) { if (recordUndo) Rprintf( "End[%d] d:%d\n", undoHead, doCount ); @@ -758,6 +887,9 @@ void UndoEnd( void ) } +/** + * Reset the Undo state + */ void UndoClear( void ) { int inx; @@ -775,8 +907,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,13 +927,13 @@ BOOL_T UndoUndo( void ) if (doCount <= 0) { ErrorMessage( MSG_NO_UNDO ); - return FALSE; + return; } - ConfirmReset( FALSE ); + int rc = 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 ) ) +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; @@ -803,22 +946,16 @@ LOG( log_undo, 1, ( " undoUndo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doC 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; + 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; + return; } if (us->needRedo) us->redoEnd = redoStream.end; @@ -847,12 +984,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,14 +1006,14 @@ BOOL_T UndoRedo( void ) if (undoCount <= 0) { ErrorMessage( MSG_NO_REDO ); - return FALSE; + return; } - ConfirmReset( FALSE ); + int rc = 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 ) ) +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; @@ -876,22 +1022,17 @@ LOG( log_undo, 1, ( " undoRedo[%d] d:%d u:%d N:%d M:%d D:%d\n", undoHead, doC 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; + return; } if ( needAttachTrains ) { @@ -919,7 +1060,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; } |