/*
 *                            COPYRIGHT
 *
 *  PCB, interactive printed circuit board design
 *  Copyright (C) 1994,1995,1996 Thomas Nau
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Contact addresses for paper mail and Email:
 *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
 *  Thomas.Nau@rz.uni-ulm.de
 *
 */

static	char	*rcsid = "$Id: undo.c,v 144.1 1996/11/29 07:08:20 nau Exp $";

/* functions used to undo operations
 *
 * Description:
 * There are two lists which hold
 *   - information about a command
 *   - data of removed objects
 * Both lists are organized as first-in-last-out which means that the undo
 * list can always use the last entry of the remove list.
 * A serial number is incremented whenever an operation is completed.
 * An operation itself may consist of several basic instructions.
 * E.g.: removing all selected objects is one operation with exactly one
 * serial number even if the remove function is called several times.
 *
 * a lock flag ensures that no infinite loops occure
 */

#include <memory.h>

#include "global.h"

#include "buffer.h"
#include "change.h"
#include "create.h"
#include "data.h"
#include "dialog.h"
#include "draw.h"
#include "error.h"
#include "insert.h"
#include "mymem.h"
#include "misc.h"
#include "move.h"
#include "remove.h"
#include "rotate.h"
#include "search.h"
#include "set.h"
#include "undo.h"

/* ---------------------------------------------------------------------------
 * some local data types
 */
typedef struct					/* information about a change command */
{
	char			*Name;
} ChangeNameType, *ChangeNameTypePtr;

typedef struct					/* information about a move command */
{
	Position		DX,			/* movement vector */
					DY;
} MoveType, *MoveTypePtr;

typedef struct					/* information about removed polygon points */
{
	PointType		Point;		/* data */
	Cardinal		Index;		/* index in a polygons array of points */
} RemovedPointType, *RemovedPointTypePtr;

typedef struct					/* informstion about rotation */
{
	Position		CenterX,	/* center of rotation */
					CenterY;
	BYTE			Steps;		/* number of steps */
} RotateType, *RotateTypePtr;

typedef struct					/* information about moves between layers */
{
	Cardinal		OriginalLayer;	/* the index of the original layer */
} MoveToLayerType, *MoveToLayerTypePtr;

typedef	struct					/* holds information about an operation */
{
	int				Serial,		/* serial number of operation */
					Type,		/* type of operation */
					ID;			/* object ID */
	union						/* some additional information */
	{
		ChangeNameType		ChangeName;
		MoveType			Move;
		RemovedPointType	RemovedPoint;
		RotateType			Rotate;
		MoveToLayerType		MoveToLayer;
		int			Flags;
		Dimension		Size;
	} Data;
} UndoListType, *UndoListTypePtr;

/* ---------------------------------------------------------------------------
 * some local identifiers
 */
static	DataTypePtr		RemoveList;		/* list of removed objects */
static	UndoListTypePtr	UndoList;		/* list of operations */
static	int				Serial,			/* serial number */
						SavedSerial;
static	size_t			UndoN, RedoN,			/* number of entries */
						UndoMax;
static	Boolean			Locked = False;	/* do not add entries if */
										/* flag is set; prevents from */
										/* infinite loops */

/* ---------------------------------------------------------------------------
 * some local prototypes
 */
static	UndoListTypePtr	GetUndoSlot(int, int);
static	void			DrawRecoveredObject(int, void *, void *, void *);
static	Boolean			UndoRotate(UndoListTypePtr);
static	Boolean			UndoChangeName(UndoListTypePtr);
static	Boolean			UndoCopyOrCreate(UndoListTypePtr);
static	Boolean			UndoMove(UndoListTypePtr);
static	Boolean			UndoRemove(UndoListTypePtr);
static	Boolean			UndoRemovePoint(UndoListTypePtr);
static	Boolean			UndoInsertPoint(UndoListTypePtr);
static	Boolean			UndoMoveToLayer(UndoListTypePtr);
static	Boolean			UndoFlag(UndoListTypePtr);
static	Boolean			UndoChangeSize(UndoListTypePtr);
static	Boolean			UndoChange2ndSize(UndoListTypePtr);

/* ---------------------------------------------------------------------------
 * adds a command plus some data to the undo list
 */
static UndoListTypePtr GetUndoSlot(int CommandType, int ID)
{
	UndoListTypePtr	ptr;
	void	*ptr1, *ptr2, *ptr3;
	int	type;
	static	size_t			limit = UNDO_WARNING_SIZE;

		/* allocate memory */
	if (UndoN >= UndoMax)
	{
		size_t	size;

		UndoMax += STEP_UNDOLIST;
		size = UndoMax *sizeof(UndoListType);
		UndoList = (UndoListTypePtr) MyRealloc(UndoList, size,
			"AddCommandToUndoList()");
		memset(&UndoList[UndoN], 0, STEP_REMOVELIST *sizeof(UndoListType));

			/* ask user to flush the table because of it's size */
		if (size > limit)
		{
			limit = (size /UNDO_WARNING_SIZE +1) *UNDO_WARNING_SIZE;
			Message("size of 'undo-list' exceeds %li kb\n",
				(long) (size >> 10));
		}
	}

		/* free structures from the pruned redo list */

	for (ptr = &UndoList[UndoN]; RedoN; ptr++, RedoN--)
		switch(ptr->Type)
		{
			case UNDO_CHANGENAME:
			{
				SaveFree(ptr->Data.ChangeName.Name);
				break;
			}
			case UNDO_REMOVE:
			{
				type = SearchObjectByID(RemoveList, &ptr1,
					&ptr2, &ptr3, ptr->ID);
				if (type != NO_TYPE)
				{
					DestroyObject(RemoveList, type,
						ptr1, ptr2, ptr3);
				}
				break;
			}
			default:
				break;
		}		

		/* copy typefield and serial number to the list */
	ptr = &UndoList[UndoN++]; 
	ptr->Type = CommandType;
	ptr->ID = ID;
	ptr->Serial = Serial;
	return(ptr);
}

/* ---------------------------------------------------------------------------
 * redraws the recovered object
 */
static void DrawRecoveredObject(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
{
	if (Type & (LINE_TYPE | TEXT_TYPE | POLYGON_TYPE))
	{
		LayerTypePtr	layer;

		layer= &PCB->Data->Layer[GetLayerNumber(RemoveList,(LayerTypePtr)Ptr1)];
		DrawObject(Type, (void *) layer, Ptr2, 0);
	}
	else
		DrawObject(Type, Ptr1, Ptr2, 0);
}

/* ---------------------------------------------------------------------------
 * recovers an object from a 'rotate' operation
 * returns True if anything has been recovered
 */
static Boolean UndoRotate(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type;

		/* lookup entry by it's ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	if (type != NO_TYPE)
	{
		RotateObject(type, ptr1, ptr2, ptr3,
			Entry->Data.Rotate.CenterX, Entry->Data.Rotate.CenterY,
			(4-Entry->Data.Rotate.Steps) & 0x03);
		Entry->Data.Rotate.Steps = (4 - Entry->Data.Rotate.Steps) & 0x03;
		return(True);
	}
	return(False);
}

/* ---------------------------------------------------------------------------
 * recovers an object from a 'change name' operation
 * returns True if anything has been recovered
 */
static Boolean UndoChangeName(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type;

		/* lookup entry by it's ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	if (type != NO_TYPE)
	{
		Entry->Data.ChangeName.Name = 
			(ChangeObjectName(type, ptr1, ptr2, ptr3,
				Entry->Data.ChangeName.Name));
		return(True);
	}
	return(False);
}

/* ---------------------------------------------------------------------------
 * recovers an object from a 2ndSize change operation
 */
static Boolean UndoChange2ndSize(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int	type;
	Dimension	swap;

		/* lookup entry by ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	switch (type)
	{
		case VIA_TYPE:
			swap = ((PinTypePtr) ptr2)->DrillingHole;
			EraseVia((PinTypePtr) ptr2);
			((PinTypePtr) ptr2)->DrillingHole = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawVia((PinTypePtr) ptr2, 0);
			break;
		case PIN_TYPE:
			swap = ((PinTypePtr) ptr2)->DrillingHole;
			ErasePin((PinTypePtr) ptr2);
			((PinTypePtr) ptr2)->DrillingHole = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawPin((PinTypePtr) ptr2, 0);
			break;
	}
	return(type != NO_TYPE);
}

/* ---------------------------------------------------------------------------
 * recovers an object from a Size change operation
 */
static Boolean UndoChangeSize(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int	type;
	Dimension	swap;

		/* lookup entry by ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	switch (type)
	{
		case VIA_TYPE:
			swap = ((PinTypePtr) ptr2)->Thickness;
			EraseVia((PinTypePtr) ptr2);
			((PinTypePtr) ptr2)->Thickness = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawVia((PinTypePtr) ptr2, 0);
			break;
		case PIN_TYPE:
			swap = ((PinTypePtr) ptr2)->Thickness;
			ErasePin((PinTypePtr) ptr2);
			((PinTypePtr) ptr2)->Thickness = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawPin((PinTypePtr) ptr2, 0);
			break;
		case LINE_TYPE:
			swap = ((LineTypePtr) ptr2)->Thickness;
			EraseLine((LineTypePtr) ptr2);
			((LineTypePtr) ptr2)->Thickness = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawLine((LayerTypePtr) ptr1, (LineTypePtr) ptr2, 0);
			break;
		case TEXT_TYPE:
			swap = ((TextTypePtr) ptr2)->Scale;
			EraseText((TextTypePtr) ptr2);
			((TextTypePtr) ptr2)->Scale = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawText((LayerTypePtr) ptr1, (TextTypePtr) ptr2, 0);
			break;
		case ELEMENTNAME_TYPE:
			swap = ((TextTypePtr) ptr2)->Scale;
			EraseElementName((ElementTypePtr) ptr1);
			((TextTypePtr) ptr2)->Scale = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawElementName((ElementTypePtr) ptr1, 0);
			break;
		case PAD_TYPE:
			swap = ((PadTypePtr) ptr2)->Thickness;
			ErasePad((PadTypePtr) ptr2);
			((PadTypePtr) ptr2)->Thickness = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawPad((PadTypePtr) ptr2, 0);
			break;
		case ARC_TYPE:
			swap = ((ArcTypePtr) ptr2)->Thickness;
			EraseElement((ElementTypePtr) ptr1);
			((ArcTypePtr) ptr2)->Thickness = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawElement((ElementTypePtr) ptr1, 0);
			break;
		case ELEMENTLINE_TYPE:
			swap = ((LineTypePtr) ptr2)->Thickness;
			EraseElement((ElementTypePtr) ptr1);
			((LineTypePtr) ptr2)->Thickness = Entry->Data.Size;
			Entry->Data.Size = swap;
			DrawElement((ElementTypePtr) ptr1, 0);
			break;
	}
	return(type != NO_TYPE);
}

/* ---------------------------------------------------------------------------
 * recovers an object from a FLAG change operation
 */
static Boolean UndoFlag(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int	type, swap, tests;


		/* lookup entry by ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	if (type != NO_TYPE)
	{
		swap = tests = ((PinTypePtr) ptr2)->Flags;
		tests ^= Entry->Data.Flags;
		if (tests & (RATFLAG |  SQUAREFLAG | ALLTHERMFLAGS | HIDENAMEFLAG))
			EraseObject(type, ptr2);
		((PinTypePtr) ptr2)->Flags = Entry->Data.Flags;
		Entry->Data.Flags = swap;
		if (tests & (RATFLAG | SELECTEDFLAG | SQUAREFLAG | ALLTHERMFLAGS | HIDENAMEFLAG))
			DrawObject(type, ptr1, ptr2, 0);
		return(True);
	}
	Message("Can't find ID %d\n", Entry->ID);
	return(False);
}

/* ---------------------------------------------------------------------------
 * recovers an object from a 'copy' or 'create' operation
 * returns True if anything has been recovered
 */
static Boolean UndoCopyOrCreate(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type;

		/* lookup entry by it's ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	if (type != NO_TYPE)
	{
		if (!RemoveList)
			RemoveList = CreateNewBuffer();
		EraseObject(type, ptr2);
			/* in order to make this re-doable we move it to the RemoveList */
		MoveObjectToBuffer(RemoveList, PCB->Data, type, ptr1, ptr2, ptr3);
		Entry->Type = UNDO_REMOVE;	
		return(True);
	}
	return(False);
}

/* ---------------------------------------------------------------------------
 * recovers an object from a 'move' operation
 * returns True if anything has been recovered
 */
static Boolean UndoMove(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type;

		/* lookup entry by it's ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	if (type != NO_TYPE)
	{
		MoveObject(type, ptr1, ptr2, ptr3,
			-Entry->Data.Move.DX, -Entry->Data.Move.DY);
		Entry->Data.Move.DX *= -1;
		Entry->Data.Move.DY *= -1;
		return(True);
	}
	return(False);
}

/* ----------------------------------------------------------------------
 * recovers an object from a 'remove' operation
 * returns True if anything has been recovered
 */
static Boolean UndoRemove(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type;

		/* lookup entry by it's ID */
	type = SearchObjectByID(RemoveList, &ptr1, &ptr2, &ptr3, Entry->ID);
	if (type != NO_TYPE)
	{
		DrawRecoveredObject(type, ptr1, ptr2, ptr3);
		MoveObjectToBuffer(PCB->Data, RemoveList, type, ptr1, ptr2, ptr3);
		Entry->Type = UNDO_CREATE;
		return(True);
	}
	return(False);
}

/* ----------------------------------------------------------------------
 * recovers an object from a 'move to another layer' operation
 * returns True if anything has been recovered
 */
static Boolean UndoMoveToLayer(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type, swap;

		/* lookup entry by it's ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	if (type != NO_TYPE)
	{
		swap =	GetLayerNumber(PCB->Data, (LayerTypePtr) ptr1);
		MoveObjectToLayer(type, ptr1, ptr2, ptr3, 
			&PCB->Data->Layer[Entry->Data.MoveToLayer.OriginalLayer], True);
		Entry->Data.MoveToLayer.OriginalLayer = swap;
		return(True);
	}
	return(False);
}

/* ---------------------------------------------------------------------------
 * recovers a removed polygon point
 * returns true on success
 */
static Boolean UndoRemovePoint(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type;

		/* lookup entry (polygon not point was saved) by it's ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	switch(type)
	{
		case POLYGON_TYPE:		/* restore the removed point */
		{
			LayerTypePtr	layer = (LayerTypePtr) ptr1;
			PolygonTypePtr	polygon = (PolygonTypePtr) ptr2;

				/* recover the point */
			if (layer->On)
				ErasePolygon(polygon);
			InsertPointIntoObject(POLYGON_TYPE, layer, polygon,
				&Entry->Data.RemovedPoint.Index,
				Entry->Data.RemovedPoint.Point.X,
				Entry->Data.RemovedPoint.Point.Y);
			if (layer->On)
				DrawPolygon(layer, polygon, 0);
			Entry->Type = UNDO_INSERT_POINT;
			Entry->ID = polygon->Points[Entry->Data.RemovedPoint.Index].ID;
			return(True);
		}

		default:
			return(False);
	}
}

/* ---------------------------------------------------------------------------
 * recovers an inserted polygon point
 * returns true on success
 */
static Boolean UndoInsertPoint(UndoListTypePtr Entry)
{
	void	*ptr1, *ptr2, *ptr3;
	int		type;
	PointTypePtr	pnt;

		/* lookup entry by it's ID */
	type = SearchObjectByID(PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID);
	switch(type)
	{
		case POLYGONPOINT_TYPE:		/* removes an inserted polygon point */
		{
			pnt = (PointTypePtr) ptr3;
			Entry->Data.RemovedPoint.Point.X = pnt->X;
			Entry->Data.RemovedPoint.Point.Y = pnt->Y;
			Entry->ID = ((PolygonTypePtr) ptr2)->ID;
			POLYGONPOINT_LOOP((PolygonTypePtr) ptr2,
				if (pnt == point)
				{
					Entry->Data.RemovedPoint.Index = n;
					break;
				});
			DestroyObject(PCB->Data, POLYGONPOINT_TYPE, (LayerTypePtr) ptr1,
				(PolygonTypePtr) ptr2, (PointTypePtr) ptr3);
			Entry->Type = UNDO_REMOVE_POINT;
			return(True);
		}

		default:
			return(False);
	}
}

/* ---------------------------------------------------------------------------
 * undo of any 'hard to recover' operation
 *
 * returns True if anything is has been done
 */
Boolean Undo(void)
{
	UndoListTypePtr	ptr;
	Boolean			changed = False;

	if (!UndoN)
	{
		Message("Nothing to undo - buffer is empty\n");
		return(False);
	}

		/* lock undo module to prevent from loops
		 * and loop over all entries with the same serial number
		 */
	LockUndo();
	ptr = &UndoList[UndoN -1];
	Serial = ptr->Serial;
	for (; UndoN && ptr->Serial == Serial; ptr--, UndoN--, RedoN++)
		switch(ptr->Type)
		{
			case UNDO_CHANGENAME:
				changed |= UndoChangeName(ptr);
				break;

			case UNDO_CREATE:
			case UNDO_COPY:
				changed |= UndoCopyOrCreate(ptr);
				break;

			case UNDO_MOVE:
				changed |= UndoMove(ptr);
				break;

			case UNDO_REMOVE:
				changed |= UndoRemove(ptr);
				break;

			case UNDO_REMOVE_POINT:
				changed |= UndoRemovePoint(ptr);
				break;

			case UNDO_INSERT_POINT:
				changed |= UndoInsertPoint(ptr);
				break;

			case UNDO_ROTATE:
				changed |= UndoRotate(ptr);
				break;

			case UNDO_MOVETOLAYER:
				changed |= UndoMoveToLayer(ptr);
				break;

			case UNDO_FLAG:
				changed |= UndoFlag(ptr);
				break;

			case UNDO_CHANGESIZE:
				changed |= UndoChangeSize(ptr);
				break;

			case UNDO_CHANGE2NDSIZE:
				changed |= UndoChange2ndSize(ptr);
		}
		/* release lock */
	UnlockUndo();
	return(changed);
}

/* ---------------------------------------------------------------------------
 * redo of any 'hard to recover' operation
 *
 * returns True if anything is has been done
 */
Boolean Redo(void)
{
	UndoListTypePtr	ptr;
	Boolean			changed = False;

	if (!RedoN)
	{
		Message("Nothing to redo. Perhaps changes have been made since last undo\n");
		return(False);
	}

		/* lock undo module to prevent from loops
		 * and loop over all entries with the same serial number
		 */
	LockUndo();
	ptr = &UndoList[UndoN];
	Serial = ptr->Serial;
	for (; RedoN && ptr->Serial == Serial; ptr++, UndoN++, RedoN--)
		switch(ptr->Type)
		{
			case UNDO_CHANGENAME:
				changed |= UndoChangeName(ptr);
				break;

			case UNDO_CREATE:
			case UNDO_COPY:
				changed |= UndoCopyOrCreate(ptr);
				break;

			case UNDO_MOVE:
				changed |= UndoMove(ptr);
				break;

			case UNDO_REMOVE:
				changed |= UndoRemove(ptr);
				break;

			case UNDO_REMOVE_POINT:
				changed |= UndoRemovePoint(ptr);
				break;

			case UNDO_INSERT_POINT:
				changed |= UndoInsertPoint(ptr);
				break;

			case UNDO_ROTATE:
				changed |= UndoRotate(ptr);
				break;

			case UNDO_MOVETOLAYER:
				changed |= UndoMoveToLayer(ptr);
				break;

			case UNDO_FLAG:
				changed |= UndoFlag(ptr);
				break;

			case UNDO_CHANGESIZE:
				changed |= UndoChangeSize(ptr);
				break;

			case UNDO_CHANGE2NDSIZE:
				changed |= UndoChange2ndSize(ptr);
				break;
		}
	/* Make next serial number current in case we take a new branch */
	Serial++;
	UnlockUndo();
	return(changed);
}

/* ---------------------------------------------------------------------------
 * restores the serial number of the undo list
 */
void RestoreUndoSerialNumber(void)
{
		Serial = SavedSerial;
}

/* ---------------------------------------------------------------------------
 * saves the serial number of the undo list
 */
void SaveUndoSerialNumber(void)
{
	SavedSerial = Serial;
}

/* ---------------------------------------------------------------------------
 * increments the serial number of the undo list
 * it's not done automatically because some operations perform more
 * than one request with the same serial #
 */
void IncrementUndoSerialNumber(void)
{
	if (!Locked)
		Serial++;
}

/* ---------------------------------------------------------------------------
 * releases memory of the undo- and remove list
 */
void ClearUndoList(Boolean Force)
{
	UndoListTypePtr		undo;

	if (UndoN && (Force || ConfirmDialog("OK to clear 'undo' buffer ?")))
	{
			/* release memory allocated by objects in undo list */
		for (undo = UndoList; UndoN; undo++, UndoN--)
			if (undo->Type == UNDO_CHANGENAME)
				SaveFree(undo->Data.ChangeName.Name);
		MyFree((char **) &UndoList);
		FreeDataMemory(RemoveList);

			/* reset some counters */
		UndoN = UndoMax = RedoN = 0;
	}

		/* reset counter in any case */
	Serial = 0;
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of rotated objects
 */
void AddObjectToRotateUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3,
	Position CenterX, Position CenterY, BYTE Steps)
{
	UndoListTypePtr	undo;

	if (!Locked)
	{
		undo = GetUndoSlot(UNDO_ROTATE, OBJECT_ID(Ptr3));
		undo->Data.Rotate.CenterX = CenterX;
		undo->Data.Rotate.CenterY = CenterY;
		undo->Data.Rotate.Steps = Steps;
	}
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of removed objects and removes it from
 * the current PCB
 */
void MoveObjectToRemoveUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
{
	UndoListTypePtr	undo;

	if (!Locked)
	{
		if (!RemoveList)
			RemoveList = CreateNewBuffer();

		undo = GetUndoSlot(UNDO_REMOVE, OBJECT_ID(Ptr3));
		MoveObjectToBuffer(RemoveList, PCB->Data, Type, Ptr1, Ptr2, Ptr3);
	}
}
 
/* ---------------------------------------------------------------------------
 * adds an object to the list of removed polygon/... points
 */
void AddObjectToRemovePointUndoList(int Type,
	void *Ptr1, void *Ptr2, Cardinal index)
{
	UndoListTypePtr	undo;
	PolygonTypePtr	polygon = (PolygonTypePtr) Ptr2;

	if (!Locked)
	{
		switch(Type)
		{
			case POLYGONPOINT_TYPE:
			{
				/* save the ID of the parent object; else it will be
				 * impossible to recover the point
				 */
				undo = GetUndoSlot(UNDO_REMOVE_POINT, OBJECT_ID(polygon));
				undo->Data.RemovedPoint.Point = polygon->Points[index];
				undo->Data.RemovedPoint.Index = index;
			}
			break;
		}
	}
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of inserted polygon/... points
 */
void AddObjectToInsertPointUndoList(int Type,
	void *Ptr1, void *Ptr2, void *Ptr3)
{
	UndoListTypePtr	undo;

	if (!Locked)
		undo = GetUndoSlot(UNDO_INSERT_POINT, OBJECT_ID(Ptr3));
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of copied objects
 */
void AddObjectToCopyUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
{
	UndoListTypePtr	undo;

	if (!Locked)
		undo = GetUndoSlot(UNDO_COPY, OBJECT_ID(Ptr3));
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of moved objects
 */
void AddObjectToMoveUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3,
	Position DX, Position DY)
{
	UndoListTypePtr	undo;

	if (!Locked)
	{
		undo = GetUndoSlot(UNDO_MOVE, OBJECT_ID(Ptr3));
		undo->Data.Move.DX = DX;
		undo->Data.Move.DY = DY;
	}
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of objects with changed names
 */
void AddObjectToChangeNameUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3,
	char *OldName)
{
	UndoListTypePtr	undo;

	if (!Locked)
	{
		undo = GetUndoSlot(UNDO_CHANGENAME, OBJECT_ID(Ptr3));
		undo->Data.ChangeName.Name = OldName;
	}
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of objects moved to another layer
 */
void AddObjectToMoveToLayerUndoList(int Type, void *Ptr1, void *Ptr2,
	void *Ptr3)
{
	UndoListTypePtr	undo;

	if (!Locked)
	{
		undo = GetUndoSlot(UNDO_MOVETOLAYER, OBJECT_ID(Ptr3));
		undo->Data.MoveToLayer.OriginalLayer =
			GetLayerNumber(PCB->Data, (LayerTypePtr) Ptr1);
	}
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of created objects
 */
void AddObjectToCreateUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
{
	UndoListTypePtr	undo;

	if (!Locked)
		undo = GetUndoSlot(UNDO_CREATE, OBJECT_ID(Ptr3));
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of objects with flags changed
 */
void AddObjectToFlagUndoList(int Type, void *Ptr1, void *Ptr2, void *Ptr3)
{
	UndoListTypePtr	undo;

	if (!Locked) {
		undo = GetUndoSlot(UNDO_FLAG, OBJECT_ID(Ptr2));
		undo->Data.Flags = ((PinTypePtr) Ptr2)->Flags;
	}
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of objects with Size changes
 */
void AddObjectToSizeUndoList(int Type, void *ptr1, void *ptr2, void *ptr3)
{
	UndoListTypePtr undo;

	if (!Locked)
	{
		undo = GetUndoSlot(UNDO_CHANGESIZE, OBJECT_ID(ptr2));
		switch (Type)
		{
			case PIN_TYPE:
			case VIA_TYPE:
				undo->Data.Size = ((PinTypePtr) ptr2)->Thickness;
				break;
			case LINE_TYPE:
			case ELEMENTLINE_TYPE:
				undo->Data.Size = ((LineTypePtr) ptr2)->Thickness;
				break;
			case TEXT_TYPE:
			case ELEMENTNAME_TYPE:
				undo->Data.Size = ((TextTypePtr) ptr2)->Scale;
				break;
			case PAD_TYPE:
				undo->Data.Size = ((PadTypePtr) ptr2)->Thickness;
				break;
			case ARC_TYPE:
				undo->Data.Size = ((ArcTypePtr) ptr2)->Thickness;
				break;
		}
	}
}

/* ---------------------------------------------------------------------------
 * adds an object to the list of objects with 2ndSize changes
 */
void AddObjectTo2ndSizeUndoList(int Type, void *ptr1, void *ptr2, void *ptr3)
{
	UndoListTypePtr undo;

	if (!Locked)
	{
		undo = GetUndoSlot(UNDO_CHANGE2NDSIZE, OBJECT_ID(ptr2));
		if (Type == PIN_TYPE || Type == VIA_TYPE)
			undo->Data.Size = ((PinTypePtr) ptr2)->DrillingHole;
	}
}

/* ---------------------------------------------------------------------------
 * set lock flag
 */
void LockUndo(void)
{
	Locked = True;
}

/* ---------------------------------------------------------------------------
 * reset lock flag
 */
void UnlockUndo(void)
{
	Locked = False;
}
