summaryrefslogtreecommitdiff
path: root/app/bin/cundo.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/bin/cundo.c')
-rw-r--r--app/bin/cundo.c354
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;
}