/*
 * DBWundo.c --
 *
 *	Procedures for undoing/redoing operations associated
 *	with the dbwind module.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/dbwind/DBWundo.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/main.h"
#include "dbwind/dbwind.h"
#include "utils/undo.h"
#include "textio/textio.h"
#include "commands/commands.h"

/*
 * Client identifiers returned by the undo package
 * in dbwUndoInit().
 */
UndoType dbwUndoIDOldEdit, dbwUndoIDNewEdit, dbwUndoIDBox;

/*
 * Function to play events forward/backward.
 */
void dbwUndoChangeEdit(), dbwUndoBoxForw(), dbwUndoBoxBack();

/*
 * Structure to hold all the information needed to switch
 * to a new edit cell.  We rely upon the fact that no CellDef
 * is ever deleted, so it is safe to retain pointers to defs.
 * The defs, transform, and use identifier are used to identify
 * uniquely the cell use affected.
 */
typedef struct
{
    Transform	 e_editToRoot;	/* Transform to root coordinates from edit */
    Transform	 e_rootToEdit;	/* Transform to edit coordinates from root */
    CellDef	*e_rootDef;	/* Root def in the edit cell's home window */
    CellDef	*e_editDef;	/* Edit cell def itself */
    CellDef	*e_parentDef;	/* Parent def of the editcell, or NULL if the
				 * edit cell was a root itself.
				 */
    char	 e_useId[4];	/* Use identifier.  This is a place holder
				 * only; the actual structure is allocated to
				 * hold all the bytes in the use id, plus the
				 * null byte.
				 */
} editUE;

#define	editSize(n)	(sizeof (editUE) - 3 + (n))

/* Structure used for undo-ing changes in the box.  It just holds the
 * box's old and new locations.
 */

typedef struct
{
    CellDef *bue_oldDef;
    Rect bue_oldArea;
    CellDef *bue_newDef;
    Rect bue_newArea;
} BoxUndoEvent;


/*
 * ----------------------------------------------------------------------------
 *
 * dbwUndoInit --
 *
 *	Initialize handling of undo for the dbwind module.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Calls the undo package to add several clients.
 *
 * ----------------------------------------------------------------------------
 */

void
dbwUndoInit()
{
    void (*nullProc)() = NULL;

    dbwUndoIDOldEdit = UndoAddClient(nullProc, nullProc,
	    (UndoEvent *(*)()) NULL, (int (*)()) NULL, nullProc,
	    dbwUndoChangeEdit, "change edit cell");

    dbwUndoIDNewEdit = UndoAddClient(nullProc, nullProc,
	    (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbwUndoChangeEdit,
	    nullProc, "change edit cell");

    dbwUndoIDBox = UndoAddClient(nullProc, nullProc,
	    (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbwUndoBoxForw,
	    dbwUndoBoxBack, "box change");
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBWUndoOldEdit --
 * DBWUndoNewEdit --
 *
 *	Record the old and new edit cells when the edit cell changes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Each creates a single undo list entry.
 *
 * ----------------------------------------------------------------------------
 */

void
DBWUndoOldEdit(
    CellUse *editUse,
    CellDef *editRootDef,
    Transform *editToRootTrans,
    Transform *rootToEditTrans)
{
    char *useid = editUse->cu_id;
    editUE *ep;

    ep = (editUE *) UndoNewEvent(dbwUndoIDOldEdit,
	    (unsigned) editSize(strlen(useid)));
    if (ep == (editUE *) NULL)
	return;

    ep->e_editToRoot = *editToRootTrans;
    ep->e_rootToEdit = *rootToEditTrans;
    ep->e_rootDef = editRootDef;
    ep->e_editDef = editUse->cu_def;
    ep->e_parentDef = editUse->cu_parent;
    (void) strcpy(ep->e_useId, useid);
}

void
DBWUndoNewEdit(
    CellUse *editUse,
    CellDef *editRootDef,
    Transform *editToRootTrans,
    Transform *rootToEditTrans)
{
    char *useid = editUse->cu_id;
    editUE *ep;

    ep = (editUE *) UndoNewEvent(dbwUndoIDNewEdit,
	    (unsigned) editSize(strlen(useid)));
    if (ep == (editUE *) NULL)
	return;

    ep->e_editToRoot = *editToRootTrans;
    ep->e_rootToEdit = *rootToEditTrans;
    ep->e_rootDef = editRootDef;
    ep->e_editDef = editUse->cu_def;
    ep->e_parentDef = editUse->cu_parent;
    (void) strcpy(ep->e_useId, useid);
}

/*
 * ----------------------------------------------------------------------------
 *
 * dbwUndoChangeEdit
 *
 *	Change the edit cell.
 *	The UndoEvent passed as an argument contains a pointer to
 *	the root cell def of the new edit cell and the parent def
 *	of the edit cell.  Both pointers are safe because we never
 *	delete a CellDef.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Changes the edit cell.
 *	Redisplays the old edit cell and the new one.
 *
 * ----------------------------------------------------------------------------
 */

void
dbwUndoChangeEdit(
    editUE *ep)
{
    Rect area;
    CellUse *use;
    CellDef *editDef, *parent;
    static Rect origin = {{-1, -1}, {1, 1}};

    /* Redisplay the old edit cell */
    GeoTransRect(&EditToRootTransform, &(EditCellUse->cu_def->cd_bbox), &area);
    DBWAreaChanged(EditRootDef, &area, DBW_ALLWINDOWS, &DBAllButSpaceBits);
    GeoTransRect(&EditToRootTransform, &origin, &area);
    DBWAreaChanged(EditRootDef, &area, DBW_ALLWINDOWS, &DBAllButSpaceBits);

    /* Set up the transforms for the new edit cell */
    EditToRootTransform = ep->e_editToRoot;
    RootToEditTransform = ep->e_rootToEdit;

    /*
     * Search for the use uniquely identified by the parent cell
     * def 'parent' (which may be NULL) and the use identifier
     * 'ep->e_useId'.
     *
     * It's gotta be there.
     */
    EditRootDef = ep->e_rootDef;
    editDef = ep->e_editDef;
    parent = ep->e_parentDef;
    for (use = editDef->cd_parents; use != NULL; use = use->cu_nextuse)
	if (use->cu_parent == parent && strcmp(use->cu_id, ep->e_useId) == 0)
	    break;

    ASSERT(use != (CellUse *) NULL, "dbwUndoChangeEdit");

    TxPrintf("Edit cell is now %s (%s)\n", editDef->cd_name, use->cu_id);
    EditCellUse = use;
    GeoTransRect(&EditToRootTransform, &(EditCellUse->cu_def->cd_bbox), &area);
    DBWAreaChanged(EditRootDef, &area, DBW_ALLWINDOWS, &DBAllButSpaceBits);
    GeoTransRect(&EditToRootTransform, &origin, &area);
    DBWAreaChanged(EditRootDef, &area, DBW_ALLWINDOWS, &DBAllButSpaceBits);
    CmdSetWindCaption(EditCellUse, EditRootDef);
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBWUndoBox --
 *
 * 	Remember a box change for later undo-ing.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	An entry is added to the undo list.
 *
 * ----------------------------------------------------------------------------
 */

void
DBWUndoBox(
    CellDef *oldDef,		/* Celldef containing old box. */
    Rect *oldArea,		/* Area of old box in oldDef coords. */
    CellDef *newDef,		/* Celldef containing new box. */
    Rect *newArea)		/* Area of new box in newDef coords. */
{
    BoxUndoEvent *bue;

    bue = (BoxUndoEvent *) UndoNewEvent(dbwUndoIDBox, sizeof(BoxUndoEvent));
    if (bue == NULL) return;

    bue->bue_oldDef = oldDef;
    bue->bue_oldArea = *oldArea;
    bue->bue_newDef = newDef;
    bue->bue_newArea = *newArea;
}

/*
 * ----------------------------------------------------------------------------
 *
 * dbwUndoBoxForw --
 * dbwUndoBoxBack --
 *
 * 	This routines are called to undo a change to the box.  They
 *	are invoked by the undo package.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The box's location is modified.
 *
 * ----------------------------------------------------------------------------
 */

void
dbwUndoBoxForw(
    BoxUndoEvent *bue)			/* Event to be redone. */
{
    DBWSetBox(bue->bue_newDef, &bue->bue_newArea);
}

void
dbwUndoBoxBack(
    BoxUndoEvent *bue)			/* Event to be undone. */
{
    DBWSetBox(bue->bue_oldDef, &bue->bue_oldArea);
}
