magic/select/selUndo.c

452 lines
14 KiB
C
Raw Normal View History

/* selUndo.c -
*
* This file provides routines for undo-ing and redo-ing the
* the selection. Most of the undo-ing is handled automatically
* by enabling undo-ing when the selection cell is modified.
* All this file does is record things to be redisplayed, since
* the normal undo package won't handle that.
*
* *********************************************************************
* * 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/select/selUndo.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "dbwind/dbwind.h"
#include "utils/undo.h"
#include "commands/commands.h"
#include "textio/textio.h"
#include "select/select.h"
#include "select/selInt.h"
/* Each selection modification causes two records of the following
* format to be added to the undo event list. The first record
* is added BEFORE the modification (sue_before is TRUE), and the
* second is added afterwards. The reason for doubling the events
* is that we can't redisplay selection information until after the
* selection has been modified. This requires events to be in
* different places depending on whether we're undo-ing or redo-ing.
*/
typedef struct
{
CellDef *sue_def; /* Definition in which selection must be
* redisplayed.
*/
Rect sue_area; /* Area of sue_def in which selection info
* must be redisplayed.
*/
bool sue_before; /* TRUE means this entry was made before
* the selection modifications. FALSE
* means afterwards.
*/
} SelUndoEvent;
/* Identifier for selection undo records: */
UndoType SelUndoClientID;
/* Network selection differs from other selection modes in two fundamental
* ways: 1) it is almost always done for informational purposes (that is,
* one does not usually copy, move, or stretch the entire network), and
* 2) it usually involves a LOT of tiles. Consequently, it makes sense
* to define a separate "undo" operation for net selection, in which only
* the information needed to reconstruct the event is retained. This
* eliminates the worst problem with selections (as previously handled
* by the SelUndoClient, above), in which querying the power or ground
* network of a large chip gobbles vast amount of memory as magic
* remembers every single tile paint operation. This memory is kept
* "forever" (at least as far as the undo stack depth, which is pretty
* large), and so continued queries on large networks quickly fills up
* memory. SelUndoNetClient is a much more efficient way to handle
* the problem. Although the "undo" command takes time to regenerate
* the network from scratch, use of "undo" on network selections is
* actually rather rare in practice, and the memory savings far outweighs
* the difference in time to regenerate the net. Eliminating the need
* to malloc and fill giant chunks of memory reduces the time required to
* select the network, anyway.
*
* SelUndoNetClient method added by Nishit, July 7-9, 2004
*/
typedef struct
{
CellDef *sue_def; /* Definition in which net selection
* must be redisplayed
*/
Point sue_startpoint; /* One valid coordinate for the net, to
* use as the startpoint for net regeneration
*/
TileType sue_type; /* Type of tile; in conjunction with the
* startpoint, uniquely identifies the net.
*/
bool sue_less; /* value of "less" passed to SelectNet */
bool sue_before; /* TRUE means this entry was made before
* the net selection. FALSE means it was
* made afterwards.
*/
} SelUndoNetEvent;
/* Identifier for network selection undo records */
UndoType SelUndoNetClientID;
/*
* ----------------------------------------------------------------------------
*
* SelUndoInit --
*
* Adds us as a client to the undo package.
*
* Results:
* None.
*
* Side effects:
* Adds a new client to the undo package, and sets SelUndoClientID.
*
* ----------------------------------------------------------------------------
*/
void
SelUndoInit()
{
extern void SelUndoForw(), SelUndoBack();
extern void SelUndoNetForw(), SelUndoNetBack();
SelUndoClientID = UndoAddClient((void (*)()) NULL, (void (*)()) NULL,
(UndoEvent *(*)()) NULL, (int (*)()) NULL, SelUndoForw,
SelUndoBack, "selection");
if (SelUndoClientID < (UndoType) 0)
TxError("Couldn't add selection as an undo client!\n");
/* Special undo client for net selection (Nishit, July 8, 2004) */
SelUndoNetClientID = UndoAddClient((void (*)()) NULL, (void (*)()) NULL,
(UndoEvent *(*)()) NULL, (int (*)()) NULL, SelUndoNetForw,
SelUndoNetBack, "net selection");
if (SelUndoNetClientID < (UndoType) 0)
TxError("Couldn't add net selection as an undo client!\n");
}
/*
* ----------------------------------------------------------------------------
*
* SelRememberForUndo--
*
* This routine is called twice whenever the selection is modified.
* It must be called once before modifying the selection. In this
* case before is TRUE and the other arguments are arbitrary. It
* must also be called again after the selection is modified. In
* this case before is FALSE and the other fields indicate exactly
* which area was modified.
*
* Results:
* None.
*
* Side effects:
* Adds information to the undo list.
*
* ----------------------------------------------------------------------------
*/
void
SelRememberForUndo(before, def, area)
bool before; /* TRUE means caller is about to modify
* the given area of the selection. FALSE
* means the caller has just modified
* the area.
*/
CellDef *def; /* Root definition on top of whom selection
* information was just modified.
*/
Rect *area; /* The area of def where selection info
* changed. This pointer may be NULL, even
* on the second call, if there's no need
* to do redisplay during undo's. This is
* the case if layout information is being
* modified over the area of the selection:
* when layout is redisplayed, selection info
* will automatically be redisplayed too.
*/
{
SelUndoEvent *sue;
static SelUndoEvent *beforeEvent = NULL;
static Rect nullRect = {{0, 0}, {-1, -1}};
sue = (SelUndoEvent *) UndoNewEvent(SelUndoClientID, sizeof(SelUndoEvent));
if (sue == NULL) return;
/* We don't have complete information when the "before" event is
* created, so save around its address and fill in the event when
* the "after" event is created.
*/
if (before)
{
sue->sue_before = TRUE;
sue->sue_def = NULL;
beforeEvent = sue;
}
else
{
if (area == NULL) area = &nullRect;
sue->sue_def = def;
sue->sue_area = *area;
sue->sue_before = before;
beforeEvent->sue_def = def;
beforeEvent->sue_area = *area;
beforeEvent = NULL;
}
}
/*
* ----------------------------------------------------------------------------
*
* SelUndoForw --
* SelUndoBack --
*
* Called to process undo redisplay events. The two procedures
* are identical except that each one looks at different events.
* The idea is to do the selection redisplay only AFTER the selection
* has actually been modified.
*
* Results:
* None.
*
* Side effects:
* Highlights (including the selection) are redisplayed.
*
* ----------------------------------------------------------------------------
*/
void
SelUndoForw(sue)
SelUndoEvent *sue; /* Event to be redone. */
{
if (sue->sue_before) return;
if (sue->sue_def == NULL) return;
SelSetDisplay(SelectUse, sue->sue_def);
SelectRootDef = sue->sue_def;
DBReComputeBbox(SelectDef);
if (sue->sue_area.r_xbot <= sue->sue_area.r_xtop)
DBWHLRedraw(sue->sue_def, &sue->sue_area, TRUE);
DBWAreaChanged(SelectDef, &sue->sue_area, DBW_ALLWINDOWS,
(TileTypeBitMask *) NULL);
}
void
SelUndoBack(sue)
SelUndoEvent *sue; /* Event to be undone. */
{
if (!sue->sue_before) return;
if (sue->sue_def == NULL) return;
SelSetDisplay(SelectUse, sue->sue_def);
SelectRootDef = sue->sue_def;
DBReComputeBbox(SelectDef);
if (sue->sue_area.r_xbot <= sue->sue_area.r_xtop)
DBWHLRedraw(sue->sue_def, &sue->sue_area, TRUE);
DBWAreaChanged(SelectDef, &sue->sue_area, DBW_ALLWINDOWS,
(TileTypeBitMask *) NULL);
}
/* BY NP */
/* The following methods are created for Net selection undo/redo-ing. */
/*
* ----------------------------------------------------------------------------
*
* SelNetRememberForUndo--
*
* This routine is called once whenever a net selection is created.
* It is called once before the modifications (with "before" TRUE)
* and once afterward ("before" FALSE). In the second case,
* arguments (def, startpoint, type, and less) are ignored.
*
* This routine is called from within a routine (SelectNet) that
* is invoked by the undo mechanism. To avoid corrupting the undo
* stack records, return immediately if selections have been disabled.
*
* Results:
* None.
*
* Side effects:
* Adds information to the undo list.
*
* ----------------------------------------------------------------------------
*/
void
SelNetRememberForUndo(def, startpoint, type, less, before)
CellDef *def; /* Root definition on top of which selection
* information was just modified.
*/
Point *startpoint; /* Starting point, from where we start
* selecting the network.
*/
TileType type; /* Tile type that uniquely identifies the
* network node at the above startpoint.
*/
bool less; /* Value of "less" passed to SelectNet */
bool before; /* Does this mark the beginning or end
* of the net selection.
*/
{
static SelUndoNetEvent *beforeEvent = NULL;
SelUndoNetEvent *sue;
if (!UndoIsEnabled()) return;
sue = (SelUndoNetEvent *) UndoNewEvent(SelUndoNetClientID,
sizeof(SelUndoNetEvent));
if (sue == NULL) return;
if (before)
{
sue->sue_before = TRUE;
sue->sue_def = def;
sue->sue_startpoint = *startpoint;
sue->sue_less = less;
sue->sue_type = type;
ASSERT(beforeEvent == NULL, "Forgot to call SelRememberForUndo after");
beforeEvent = sue;
}
else
{
sue->sue_before = FALSE;
ASSERT(beforeEvent != NULL, "Forgot to call SelRememberForUndo before");
sue->sue_def = beforeEvent->sue_def;
sue->sue_startpoint = beforeEvent->sue_startpoint;
sue->sue_less = beforeEvent->sue_less;
sue->sue_type = beforeEvent->sue_type;
/* Don't set to NULL; it is possible to run through */
/* this code twice in a row if we "undo" back to, but */
/* not over, the SelectNet record, and then start */
/* again. beforeEvent will still be pointing to the */
/* correct event. */
/* beforeEvent = NULL; */
}
}
/*
* ----------------------------------------------------------------------------
*
* SelUndoCreateNet --
*
* ----------------------------------------------------------------------------
*/
void
SelUndoCreateNet(sue)
SelUndoNetEvent *sue; /* Event description */
{
SearchContext scx;
DBWclientRec *crec;
MagWindow *window;
scx.scx_area.r_xbot = sue->sue_startpoint.p_x;
scx.scx_area.r_ybot = sue->sue_startpoint.p_y;
scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
// window = CmdGetRootPoint((Point *) NULL, &scx.scx_area);
window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
if (window == NULL) return;
scx.scx_use = (CellUse *) window->w_surfaceID;
scx.scx_trans = GeoIdentityTransform;
crec = (DBWclientRec *) window->w_clientData;
UndoDisable();
SelectClear(); // Selection should already be clear at this point.
SelectNet(&scx, sue->sue_type, crec->dbw_bitmask, (Rect *) NULL,
sue->sue_less);
UndoEnable();
}
/*
* ----------------------------------------------------------------------------
*
* SelUndoDestroyNet --
*
* ----------------------------------------------------------------------------
*/
void
SelUndoDestroyNet()
{
UndoDisable();
SelectClear();
UndoEnable();
}
/*
* ----------------------------------------------------------------------------
*
* SelUndoNetForw --
* SelUndoNetBack --
*
* Called to process redo(undo) redisplay events. These routines
* are symmetric: SelUndoNetForw creates the net on a "before"
* event and erases it on an "after" event. SelUndoNetBack erases
* the net on a "before" event and creates it on an "after" event.
*
* Results:
* None.
*
* Side effects:
* Highlights (including the selection) are redisplayed.
*
* ----------------------------------------------------------------------------
*/
void
SelUndoNetForw(sue)
SelUndoNetEvent *sue; /* Event to be redone. */
{
if (sue->sue_def == NULL) return;
if (sue->sue_before)
SelUndoCreateNet(sue);
else
SelUndoDestroyNet();
}
void
SelUndoNetBack(sue)
SelUndoNetEvent *sue; /* Event to be redone. */
{
if (sue->sue_def == NULL) return;
if (sue->sue_before)
SelUndoDestroyNet();
else
SelUndoCreateNet(sue);
}