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.c883
1 files changed, 883 insertions, 0 deletions
diff --git a/app/bin/cundo.c b/app/bin/cundo.c
new file mode 100644
index 0000000..1d17503
--- /dev/null
+++ b/app/bin/cundo.c
@@ -0,0 +1,883 @@
+/** \file cundo.c
+ * Undo / redo functions.
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <stdarg.h>
+#include <errno.h>
+#include "track.h"
+#include "trackx.h"
+#include "i18n.h"
+
+/*****************************************************************************
+ *
+ * UNDO
+ *
+ */
+
+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;
+
+static undoStack_t undoStack[UNDO_STACK_SIZE];
+static wIndex_t undoHead = -1;
+static BOOL_T undoActive = FALSE;
+static int doCount = 0;
+static int undoCount = 0;
+
+static char ModifyOp = 1;
+static char DeleteOp = 2;
+
+static BOOL_T recordUndo = 1;
+
+#define UASSERT( ARG, VAL ) \
+ if (!(ARG)) return UndoFail( #ARG, VAL, __FILE__, __LINE__ )
+
+#define INC_UNDO_INX( INX ) {\
+ if (++INX >= UNDO_STACK_SIZE) \
+ INX = 0; \
+ }
+#define DEC_UNDO_INX( INX ) {\
+ if (--INX < 0) \
+ INX = UNDO_STACK_SIZE-1; \
+ }
+
+#define BSTREAM_SIZE (4096)
+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;
+typedef stream_t *stream_p;
+static stream_t undoStream;
+static stream_t redoStream;
+
+static BOOL_T needAttachTrains = FALSE;
+
+void UndoResume( void )
+{
+ LOG( log_undo, 1, ( "UndoResume()\n" ) )
+ undoActive = TRUE;
+}
+
+void UndoSuspend( void )
+{
+ LOG( log_undo, 1, ( "UndoSuspend()\n" ) )
+ undoActive = FALSE;
+}
+
+
+static void DumpStream( FILE * outf, stream_p stream, char * name )
+{
+ long binx;
+ long i, j;
+ long off;
+ streamBlocks_p blk;
+ int zeroCnt;
+ static char zeros[16] = { 0 };
+ fprintf( outf, "Dumping %s\n", name );
+ off = stream->startBInx*BSTREAM_SIZE;
+ zeroCnt = 0;
+ for ( binx=0; binx<stream->stream_da.cnt; binx++ ) {
+ blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx );
+ for ( i=0; i<BSTREAM_SIZE; i+= 16 ) {
+ 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 );
+ zeroCnt = 0;
+ }
+ if ( zeroCnt <= 1 ) {
+ fprintf( outf, "%6.6lx ", 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 );
+ }
+ 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 );
+}
+
+static BOOL_T UndoFail( char * cause, long val, char * fileName, int lineNumber )
+{
+ int inx, cnt;
+ undoStack_p us;
+ FILE * outf;
+ time_t clock;
+ char temp[STR_SIZE];
+ NoticeMessage( MSG_UNDO_ASSERT, _("Ok"), NULL, fileName, lineNumber, val, val, cause );
+ sprintf( temp, "%s%s%s", workingDir, FILE_SEP_CHAR, sUndoF );
+ outf = fopen( temp, "a+" );
+ if ( outf == NULL ) {
+ 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)
+ inx=undoHead;
+ 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 );
+ 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 );
+ DumpStream( outf, &undoStream, "undoStream" );
+ DumpStream( outf, &redoStream, "redoStream" );
+ Rdump(outf);
+ fclose( outf );
+ UndoClear();
+ UndoStart( "undoFail", "undoFail" );
+ return FALSE;
+}
+
+
+BOOL_T ReadStream( stream_t * stream, void * ptr, int size )
+{
+ long binx, boff, brem;
+ streamBlocks_p blk;
+ if ( stream->curr+size > stream->end ) {
+ UndoFail( "Overrun on stream", (long)(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 ) )
+ binx = stream->curr/BSTREAM_SIZE;
+ boff = stream->curr%BSTREAM_SIZE;
+ stream->curr += size;
+ binx -= stream->startBInx;
+ brem = BSTREAM_SIZE - boff;
+ while ( brem < size ) {
+ UASSERT( binx>=0 && binx < stream->stream_da.cnt, binx );
+ blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx );
+ memcpy( ptr, &(*blk)[boff], (size_t)brem );
+ ptr = (char*)ptr + brem;
+ size -= (int)brem;
+ binx++;
+ boff = 0;
+ brem = BSTREAM_SIZE;
+ }
+ if (size) {
+ UASSERT( binx>=0 && binx < stream->stream_da.cnt, binx );
+ blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx );
+ memcpy( ptr, &(*blk)[boff], size );
+ }
+ return TRUE;
+}
+
+BOOL_T WriteStream( stream_p stream, void * ptr, int size )
+{
+ long 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)
+ return TRUE;
+ binx = stream->end/BSTREAM_SIZE;
+ boff = stream->end%BSTREAM_SIZE;
+ stream->end += size;
+ binx -= stream->startBInx;
+ brem = BSTREAM_SIZE - boff;
+ while ( size ) {
+ if (boff==0) {
+ UASSERT( binx == stream->stream_da.cnt, binx );
+ DYNARR_APPEND( streamBlocks_p, stream->stream_da, 10 );
+ blk = (streamBlocks_p)MyMalloc( sizeof *blk );
+ DYNARR_N( streamBlocks_p, stream->stream_da, binx ) = blk;
+ } else {
+ UASSERT( binx == stream->stream_da.cnt-1, binx );
+ blk = DYNARR_N( streamBlocks_p, stream->stream_da, binx );
+ }
+ if (size > brem) {
+ memcpy( &(*blk)[boff], ptr, (size_t)brem );
+ ptr = (char*)ptr + brem;
+ size -= (size_t)brem;
+ binx++;
+ boff = 0;
+ brem = BSTREAM_SIZE;
+ } else {
+ memcpy( &(*blk)[boff], ptr, size );
+ break;
+ }
+ }
+ return TRUE;
+}
+
+BOOL_T TrimStream( stream_p stream, long off )
+{
+ long binx, cnt, inx;
+ streamBlocks_p blk;
+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);
+ UASSERT( cnt >= 0 && cnt <= stream->stream_da.cnt, cnt );
+ 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 );
+ }
+ stream->startBInx = binx;
+ stream->stream_da.cnt -= (wIndex_t)cnt;
+ UASSERT( stream->stream_da.cnt >= 0, stream->stream_da.cnt );
+ return TRUE;
+}
+
+
+void ClearStream( stream_p stream )
+{
+ long inx;
+ streamBlocks_p blk;
+ for (inx=0; inx<stream->stream_da.cnt; inx++) {
+ blk = DYNARR_N( streamBlocks_p, stream->stream_da, inx );
+ MyFree( blk );
+ }
+ stream->stream_da.cnt = 0;
+ stream->startBInx = stream->end = stream->curr = 0;
+}
+
+
+BOOL_T TruncateStream( stream_p stream, long off )
+{
+ long binx, boff, cnt, inx;
+ streamBlocks_p blk;
+LOG( log_undo, 3, ( "TruncateStream( , %ld )\n", off ) )
+ binx = off/BSTREAM_SIZE;
+ boff = off%BSTREAM_SIZE;
+ 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);
+ UASSERT( cnt >= 0 && cnt <= stream->stream_da.cnt, cnt );
+ 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;
+ stream->end = off;
+ UASSERT( stream->stream_da.cnt >= 0, stream->stream_da.cnt );
+ return TRUE;
+}
+
+BOOL_T WriteObject( stream_p stream, char op, track_p trk )
+{
+ 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 ) ) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static BOOL_T ReadObject( stream_p stream, BOOL_T needRedo )
+{
+ track_p trk;
+ track_t tempTrk;
+ char op;
+ if (!ReadStream( stream, &op, sizeof op ))
+ return FALSE;
+ if (!ReadStream( stream, &trk, sizeof trk ))
+ return FALSE;
+ if (needRedo) {
+ if (!WriteObject( &redoStream, op, trk ))
+ return FALSE;
+ }
+ 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
+ tempTrk.endPt = trk->endPt;
+ 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 );
+ else
+ tempTrk.extraData = trk->extraData;
+ if (!ReadStream( stream, tempTrk.extraData, tempTrk.extraSize ))
+ return FALSE;
+ if (recordUndo) Rprintf( "Restore T%D(%d) @ %lx\n", trk->index, tempTrk.index, (long)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)
+ ClrTrkElev( trk );
+ return TRUE;
+}
+
+
+static void RedrawInStream( stream_p stream, long start, long end, BOOL_T draw )
+{
+ char op;
+ track_p trk;
+ track_t tempTrk;
+ stream->curr = start;
+ while (stream->curr < end ) {
+ if (!ReadStream( stream, &op, sizeof op ) ||
+ !ReadStream( stream, &trk, sizeof trk ) ||
+ !ReadStream( stream, &tempTrk, sizeof tempTrk ) )
+ return;
+ stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0];
+ if (!trk->deleted) {
+ if (draw)
+ DrawNewTrack( trk );
+ else
+ UndrawNewTrack( trk );
+ }
+ }
+}
+
+
+static BOOL_T DeleteInStream( stream_p stream, long start, long 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 ) )
+ stream->curr = start;
+ while (stream->curr < end ) {
+ if (!ReadStream( stream, &op, sizeof op ))
+ return FALSE;
+ UASSERT( op == ModifyOp || op == DeleteOp, (long)op );
+ if (!ReadStream( stream, &trk, sizeof trk ) ||
+ !ReadStream( stream, &tempTrk, sizeof tempTrk ) )
+ return FALSE;
+ stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0];
+ if (op == DeleteOp) {
+ if (recordUndo) Rprintf( " Free T%D(%d) @ %lx\n", trk->index, tempTrk.index, (long)trk );
+ UASSERT( IsTrackDeleted(trk), (long)trk );
+ trk->index = -1;
+ delCount++;
+ }
+ }
+ if (delCount) {
+ for (ptrk=&to_first; *ptrk; ) {
+ if ((*ptrk)->index == -1) {
+ trk = *ptrk;
+ UASSERT( IsTrackDeleted(trk), (long)trk );
+ *ptrk = trk->next;
+ FreeTrack(trk);
+ } else {
+ ptrk = &(*ptrk)->next;
+ }
+ }
+ to_last = ptrk;
+ }
+ return TRUE;
+}
+
+
+static BOOL_T SetDeleteOpInStream( stream_p stream, long start, long end, track_p trk0 )
+{
+ char op;
+ track_p trk;
+ track_t tempTrk;
+ long binx, boff;
+ streamBlocks_p blk;
+
+ 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 ))
+ return FALSE;
+ UASSERT( op == ModifyOp || op == DeleteOp, (long)op );
+ if (!ReadStream( stream, &trk, sizeof trk ) )
+ 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 );
+ return TRUE;
+ }
+ if (!ReadStream( stream, &tempTrk, sizeof tempTrk ))
+ return FALSE;
+ stream->curr += tempTrk.extraSize + tempTrk.endCnt*sizeof tempTrk.endPt[0];
+ }
+ UASSERT( "Cannot find undo record to convert to DeleteOp", 0 );
+ return FALSE;
+}
+
+
+static void SetButtons( BOOL_T undoSetting, BOOL_T redoSetting )
+{
+ static BOOL_T undoButtonEnabled = FALSE;
+ static BOOL_T redoButtonEnabled = FALSE;
+ int index;
+ static char undoHelp[STR_SHORT_SIZE];
+ static char redoHelp[STR_SHORT_SIZE];
+
+ if (undoButtonEnabled != undoSetting) {
+ wControlActive( (wControl_p)undoB, undoSetting );
+ undoButtonEnabled = undoSetting;
+ }
+ if (redoButtonEnabled != redoSetting) {
+ wControlActive( (wControl_p)redoB, redoSetting );
+ redoButtonEnabled = redoSetting;
+ }
+ if (undoSetting) {
+ sprintf( undoHelp, _("Undo: %s"), undoStack[undoHead].label );
+ wControlSetBalloonText( (wControl_p)undoB, undoHelp );
+ } else {
+ wControlSetBalloonText( (wControl_p)undoB, _("Undo last command") );
+ }
+ if (redoSetting) {
+ index = undoHead;
+ INC_UNDO_INX(index);
+ sprintf( redoHelp, _("Redo: %s"), undoStack[index].label );
+ wControlSetBalloonText( (wControl_p)redoB, redoHelp );
+ } else {
+ wControlSetBalloonText( (wControl_p)redoB, _("Redo last undo") );
+ }
+}
+
+
+static track_p * FindParent( track_p trk, int lineNum )
+{
+ track_p *ptrk;
+ ptrk = &to_first;
+ while ( 1 ) {
+ if ( *ptrk == trk )
+ return ptrk;
+ if (*ptrk == NULL)
+ break;
+ ptrk = &(*ptrk)->next;
+ }
+ UndoFail( "Cannot find trk on list", (long)trk, "cundo.c", lineNum );
+ return NULL;
+}
+
+
+static int undoIgnoreEmpty = 0;
+void UndoStart(
+ char * label,
+ char * format,
+ ... )
+{
+ static char buff[STR_SIZE];
+ va_list ap;
+ track_p trk, next;
+ undoStack_p us, us1;
+ int inx;
+ int usp;
+
+LOG( log_undo, 1, ( "UndoStart(%s) [%d] d:%d u:%d us:%ld\n", 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 );
+ }
+
+ 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
+ if ( undoIgnoreEmpty ) {
+ us->label = label;
+ return;
+ }
+ }
+ }
+
+ INC_UNDO_INX(undoHead);
+ us = &undoStack[undoHead];
+ changed++;
+ SetWindowTitle();
+ 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 */
+ /* if track saved in undoStream is deleted then really deleted since
+ we can't get it back */
+ if (!DeleteInStream( &undoStream, us->undoStart, us->undoEnd ))
+ return;
+ /* strip off unused head of stream */
+ 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 );
+ /* 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 );
+ for (trk=us1->newTrks; trk; trk=next) {
+ if (recordUndo) Rprintf( " Free T%d @ %lx\n", trk->index, (long)trk );
+ /*ASSERT( IsTrackDeleted(trk) );*/
+ next = trk->next;
+ FreeTrack( trk );
+ }
+ INC_UNDO_INX(usp);
+ }
+ /* strip off unused tail of stream */
+ if (!TruncateStream( &undoStream, us->undoStart ))
+ return;
+ }
+ us->label = label;
+ us->modCnt = 0;
+ us->newCnt = 0;
+ us->delCnt = 0;
+ us->undoStart = us->undoEnd = undoStream.end;
+ ClearStream( &redoStream );
+ for ( inx=0; inx<UNDO_STACK_SIZE; inx++ ) {
+ undoStack[inx].needRedo = TRUE;
+ undoStack[inx].oldTail = NULL;
+ undoStack[inx].newTail = NULL;
+ }
+ us->newTrks = NULL;
+ undoStack[undoHead].trackCount = trackCount;
+ undoCount = 0;
+ undoActive = TRUE;
+ for (trk=to_first; trk; trk=trk->next ) {
+ trk->modified = FALSE;
+ trk->new = FALSE;
+ }
+ if (doCount < UNDO_STACK_SIZE)
+ doCount++;
+ SetButtons( TRUE, FALSE );
+}
+
+
+BOOL_T UndoModify( track_p trk )
+{
+ undoStack_p us;
+
+ 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)
+ 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 )
+ needAttachTrains = TRUE;
+ us = &undoStack[undoHead];
+ if (recordUndo)
+ Rprintf( " MOD T%d @ %lx\n", trk->index, (long)trk );
+ if (!WriteObject( &undoStream, ModifyOp, trk ))
+ return FALSE;
+ us->undoEnd = undoStream.end;
+ trk->modified = TRUE;
+ us->modCnt++;
+ return TRUE;
+}
+
+
+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 )
+ needAttachTrains = TRUE;
+ us = &undoStack[undoHead];
+ if (recordUndo)
+ Rprintf( " DEL T%d @ %lx\n", trk->index, (long)trk );
+ UASSERT( !IsTrackDeleted(trk), (long)trk );
+ if ( trk->modified ) {
+ if (!SetDeleteOpInStream( &undoStream, us->undoStart, us->undoEnd, trk ))
+ return FALSE;
+ } else if ( !trk->new ) {
+ if (!WriteObject( &undoStream, DeleteOp, trk ))
+ return FALSE;
+ us->undoEnd = undoStream.end;
+ } else {
+ 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 );
+ to_last = ptrk;
+ }
+ *ptrk = trk->next;
+ FreeTrack( trk );
+ us->newCnt--;
+ return TRUE;
+ }
+ trk->deleted = TRUE;
+ us->delCnt++;
+ return TRUE;
+}
+
+
+BOOL_T UndoNew( track_p trk )
+{
+ undoStack_p us;
+ if (!undoActive)
+ return TRUE;
+
+LOG( log_undo, 2, ( " UndoNew( T%d )\n", trk->index ) )
+
+ if (recordUndo)
+ Rprintf( " NEW T%d @%lx\n", trk->index, (long)trk );
+ UASSERT(undoCount==0, undoCount);
+ UASSERT(undoHead >= 0, undoHead);
+ us = &undoStack[undoHead];
+ trk->new = TRUE;
+ if (us->newTrks == NULL)
+ us->newTrks = trk;
+ us->newCnt++;
+
+ return TRUE;
+}
+
+
+void UndoEnd( void )
+{
+ if (recordUndo) Rprintf( "End[%d] d:%d\n", undoHead, doCount );
+ /*undoActive = FALSE;*/
+ if ( needAttachTrains ) {
+ AttachTrains();
+ needAttachTrains = FALSE;
+ }
+ UpdateAllElevations();
+}
+
+
+void UndoClear( void )
+{
+ int inx;
+LOG( log_undo, 2, ( " UndoClear()\n" ) )
+ undoActive = FALSE;
+ undoHead = -1;
+ undoCount = 0;
+ doCount = 0;
+ ClearStream( &undoStream );
+ ClearStream( &redoStream );
+ for (inx=0; inx<UNDO_STACK_SIZE; inx++) {
+ undoStack[inx].undoStart = undoStack[inx].undoEnd = 0;
+ }
+ SetButtons( FALSE, FALSE );
+}
+
+
+BOOL_T UndoUndo( void )
+{
+ undoStack_p us;
+ track_p trk;
+ wIndex_t oldCount;
+ BOOL_T redrawAll;
+
+ if (doCount <= 0) {
+ ErrorMessage( MSG_NO_UNDO );
+ return FALSE;
+ }
+
+ 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 );
+
+ //redrawAll = (us->newCnt+us->modCnt) > incrementalDrawLimit;
+ redrawAll = TRUE;
+ if (!redrawAll) {
+ for (trk=us->newTrks; trk; trk=trk->next )
+ UndrawNewTrack( trk );
+ RedrawInStream( &undoStream, us->undoStart, us->undoEnd, FALSE );
+ }
+
+ 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;
+ 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 (us->needRedo)
+ us->redoEnd = redoStream.end;
+ us->needRedo = FALSE;
+
+ if ( needAttachTrains ) {
+ AttachTrains();
+ needAttachTrains = FALSE;
+ }
+ UpdateAllElevations();
+ if (!redrawAll)
+ RedrawInStream( &undoStream, us->undoStart, us->undoEnd, TRUE );
+ else
+ DoRedraw();
+
+ oldCount = trackCount;
+ trackCount = us->trackCount;
+ us->trackCount = oldCount;
+ InfoCount( trackCount );
+
+ doCount--;
+ undoCount++;
+ DEC_UNDO_INX( undoHead );
+ AuditTracks( "undoUndo" );
+ SelectRecount();
+ SetButtons( doCount>0, TRUE );
+ wBalloonHelpUpdate();
+ wDrawDelayUpdate( mainD.d, FALSE );
+ return TRUE;
+}
+
+
+BOOL_T UndoRedo( void )
+{
+ undoStack_p us;
+ wIndex_t oldCount;
+ BOOL_T redrawAll;
+ track_p trk;
+
+ if (undoCount <= 0) {
+ ErrorMessage( MSG_NO_REDO );
+ return FALSE;
+ }
+
+ 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 );
+
+ //redrawAll = (us->newCnt+us->modCnt) > incrementalDrawLimit;
+ 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 );
+ *to_last = us->newTrks;
+ to_last = us->newTail;
+ UASSERT( (*to_last) == NULL, (long)*to_last );
+ RenumberTracks();
+
+ needAttachTrains = FALSE;
+ redoStream.curr = us->redoStart;
+ while ( redoStream.curr < us->redoEnd ) {
+ if (!ReadObject( &redoStream, FALSE ))
+ return FALSE;
+ }
+
+ if ( needAttachTrains ) {
+ AttachTrains();
+ needAttachTrains = FALSE;
+ }
+ UpdateAllElevations();
+ if (!redrawAll) {
+ for (trk=us->newTrks; trk; trk=trk->next )
+ DrawNewTrack( trk );
+ RedrawInStream( &redoStream, us->redoStart, us->redoEnd, TRUE );
+ } else
+ DoRedraw();
+
+ oldCount = trackCount;
+ trackCount = us->trackCount;
+ us->trackCount = oldCount;
+ InfoCount( trackCount );
+
+ undoCount--;
+ doCount++;
+
+ AuditTracks( "undoRedo" );
+ SelectRecount();
+ SetButtons( TRUE, undoCount>0 );
+ wBalloonHelpUpdate();
+ wDrawDelayUpdate( mainD.d, FALSE );
+ return TRUE;
+}
+
+
+void InitCmdUndo( void )
+{
+ log_undo = LogFindIndex( "undo" );
+}