452 lines
14 KiB
C
452 lines
14 KiB
C
/* 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);
|
|
}
|