/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    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.

    Last edit by hansen on Fri May  7 13:19:09 2004
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include "tkgate.h"

#define DEBUG_REDISPLAY 0

#define MAX_STACKDEPTH 256

extern int expertuser;

extern int baderp;

extern int GlobalElementCount;
extern int expertuser;
extern int RedrawRequested;
extern int baderp,startrekp;

void SetErrorPosition(int x,int y);
void search_setPosition(GModuleDef *M,GCElement *g,GNet *n);

void XGate_setOrigin(int x,int y)
{
#if 0
  printf("ORIGIN SET (%d, %d)\n",x,y);
#endif
  if (XGate.circuit->org_x != x || XGate.circuit->org_y != y) {
    ob_touch(XGate.circuit);
    XGate.circuit->org_x = x;
    XGate.circuit->org_y = y;
  }
  XGate.idle_ev.scroll_new_x = XGate.circuit->org_x;
  XGate.idle_ev.scroll_new_y = XGate.circuit->org_y;
  editstate_saveOrig(XGate.circuit->es);
}

/* Draws a box */
void box(int x,int y,int w,int h)
{
  ZDrawRectangle(XGate.D,XGate.W,XGate.toolGC,ctow_x(x),ctow_y(y),w,h);
}

GModuleDef *env_findModule(const char *name)
{
  return (GModuleDef*) SHash_find(XGate.circuit->moduleTable,name);
}

void env_insertModule(GModuleDef *M)
{
  char *mflags = "";
  
  if (M->is_top)
    mflags = "+";

  SHash_insert(XGate.circuit->moduleTable,M->Name,M);

  if (M->is_lib)
    DoTcl("tkg_blockListAdd \"(%s)%s\"\n",M->Name,mflags);
  else
    DoTcl("tkg_blockListAdd %s%s\n",M->Name,mflags);

  if (!M->is_top)
    modint_setInterface(M,0);
}

GModuleDef *env_defineModule(const char *name,int isMain)
{
  GModuleDef *M;

  M = new_GModuleDef(name,isMain);
  env_insertModule(M);

  return M;
}

GModuleDef *env_findAdd(const char *name,int isMain)
{
  GModuleDef *M;

  if (!(M = env_findModule(name))) {
    M = env_defineModule(name,isMain);
  }
  return M;
}

GCElement *env_blockdescript(GCElement *g)
{
  GModuleDef *M = env_findModule(g->u.block.BlockFunction);
  if (M)
    return M->blockdescript;
  else
    return 0;
}

void env_checkname(GCElement *g)
{
  if (g->typeinfo->Code != BLOCK) {
    printf("Huh?  checkname gets non-block\n");
    exit(1);
  }

  env_findAdd(g->u.block.BlockFunction,0);
}

/*
 * Free memory from all gates in a module (used to delete a module).
 */
void freegatelist(GModuleDef *mdef)
{
  HashElem *gl;
  GCElement **gates;
  int i,N;

  if (Hash_numElems(mdef->gates) > 0) {
    N = 0;
    gates = (GCElement**)ob_calloc(Hash_numElems(mdef->gates),sizeof(GCElement*),"GCElement*[]");
    for (gl = Hash_first(mdef->gates);gl;gl = Hash_next(mdef->gates,gl)) {
      GCElement *g = (GCElement*) HashElem_obj(gl);
      if (g->typeinfo->Code != JOINT)
	gates[N++] = g;
    }

    for (i = 0;i < N;i++)
      gate_delete(gates[i],mdef,0);
    ob_free(gates);
  }

  SHash_flush(mdef->gates);
}

void freewirelist(GModuleDef *mdef)
{
  GWireList *l = mdef->wires;

  while (l) {
    GWireList *n = l->cdr;
    if (l->car->driver == l->car)
      freenodelist(l->car->nodes);
    ob_free(l->car);
    l = n;
    ob_free(l);
  }
}

/*
   Remove all gates, wires and nets from a module.
*/
void env_clear(GModuleDef *M)
{
  if (!M) return;

  freegatelist(M);
  freewirelist(M);
}

void env_clearmark()
{
  HashElem *E;

  for (E = Hash_first(XGate.circuit->moduleTable);E;E = Hash_next(XGate.circuit->moduleTable,E)) {
    GModuleDef *M = (GModuleDef*) HashElem_obj(E);
    ob_touch(M);
    M->used = 0;
  }
}

/*
   Mark this module and all children of it.
*/
void env_markenv(GModuleDef *env)
{
  HashElem *gl;

  if (!env->used) {
    ob_touch(env);
    env->used = 1;
    for (gl = Hash_first(env->gates);gl;gl = Hash_next(env->gates,gl)) {
      GCElement *g = HashElem_obj(gl);

      if (g->typeinfo->Code == BLOCK) {
	GModuleDef *M = env_findAdd(g->u.block.BlockFunction,0);
	env_markenv(M);
      }
    }
  }
}

void editstate_saveOrig(EditState *es)
{
  if (es->save_x != XGate.circuit->org_x || es->save_y != XGate.circuit->org_y) {
    ob_touch(es);
    es->save_x = XGate.circuit->org_x;
    es->save_y = XGate.circuit->org_y;
  }
}

/*
   Purge all modules that can not be reached from the current 
   root module.
*/
void env_purge(EditState *es)
{
  env_clearmark();

  while (es->parent) es = es->parent;
  env_markenv(es->env);

#if 0
  env_clear(l->env);
  if (l->blockdescript)
    block_free(l->blockdescript);
#endif

  printf("env_purge: not supported\n");
}

GModuleDef *env_removeModule(const char *name)
{
  GModuleDef *M;

  M = (GModuleDef*) SHash_find(XGate.circuit->moduleTable,name);
  if (!M) return 0;

  DoTcl("tkg_blockListRemove %s\n",name);
  SHash_remove(XGate.circuit->moduleTable,name);

  modint_deleteInterface(M);

  return M;
}

/*
   Delete the specified block
*/
int env_delete(EditState *es,const char *name)
{
  while (es) {
    if (strcmp(es->env->Name,name) == 0) {
      message(1,msgLookup("err.badopendel"));			/* "Can't delete open module." */
      return -1;
    }
    es = es->parent;
  }

  if (!env_removeModule(name))
    return -1;

  FlagRedraw();
  return 0;
}


/*
    Renames a logic definition.
*/
void env_rename(const char *old,const char *new)
{
  GModuleDef *M;

  M = env_findModule(old);
  if (!M) return;

  SHash_remove(XGate.circuit->moduleTable,old);
  SHash_insert(XGate.circuit->moduleTable,new,M);
  ob_touch(M);
  ob_free(M->Name);
  M->Name = ob_strdup(new);

  modint_renameInterface(M);

  FlagRedraw();
}

/*
 * Copy the contents of module 'src' to a new module 'dst'.
 */
void env_copy(EditState *es,const char *src,const char *dst)
{
  GModuleDef *S,*D;

  S = env_findModule(src);
  D = env_findModule(dst);

  if (!S) {
    message(1,msgLookup("err.nosrcmod"),src);		/* "Source module '%s' not found" */
    return;
  }

  if (D) {
    char buf[1024];

    sprintf(buf,msgLookup("msg.modoverwt"),dst);	/* Destination module '%s' already exists.  Overwrite? */
    DoTcl("confirmMsg \"%s\" ",buf);
    if (*XGate.tcl->result != '1')
      return;
    env_removeModule(dst);
    if (env_findModule(dst))
      return;
  }
#if 0
  printf("defining block |%s|\n",dst);
#endif
  D = env_defineModule(dst,0);

  GModuleDef_copyInto(D,S,0,0,0,0);
}

void editstate_Init(EditState *es)
{
  ob_touch(es);
  es->isInterface = 0;
  es->save_x = es->save_y = 0;
  es->parent = 0;
  es->env = 0;
  es->smod = 0;
  es->inst = 0;
}

EditState *new_EditState()
{
  struct editstate *es;
    
  es = (EditState*) ob_malloc(sizeof(EditState),"EditState");
    
  editstate_Init(es);
    
  return es;
}

void delete_editstate(EditState *es)
{
  ob_free(es);
}

void editstate_update(EditState *es)
{
#if 0
  ob_touch(XGate.circuit);
#endif

#if 1
  /* XGate.ed not subject to undo */
  XGate.ed->min_x = 0x7fffffff;
  XGate.ed->min_y = 0x7fffffff;
  XGate.ed->max_x = -0x7fffffff;
  XGate.ed->max_y = -0x7fffffff;
#endif

  mark_redraw();

  if (es->clip.isActive) {
    XRectangle cr;
    cr.x = es->clip.xmin;
    cr.y = es->clip.ymin;
    cr.width = es->clip.xmax - es->clip.xmin;
    cr.height = es->clip.ymax - es->clip.ymin;
    XSetClipRectangles(XGate.D,XGate.instGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.moduleGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.modportGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.frameGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.commentGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.hyperlinkGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.wireGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.busGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.selWireGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.selBusGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.toolGC,0,0,&cr,1,YXSorted);
    XSetClipRectangles(XGate.D,XGate.cpathGC,0,0,&cr,1,YXSorted);
#if TKGATE_JSUPPORT
    XSetClipRectangles(XGate.D,XGate.kanjiGC,0,0,&cr,1,YXSorted);
#endif
  }

  gate_displayall(es,es->env);

  /*
   * Happens only if the module is empty.
   */
  if (XGate.ed->min_x > XGate.ed->max_x)
    XGate.ed->min_x = XGate.ed->max_x = XGate.ed->min_y = XGate.ed->max_y = 0;


  scrollbar_update();

  /*
   * Draw special mode-specific items 
   */
  switch (tkgate_currentMode()) {
  case MM_SIMULATE :
    SimInterface_drawProbes(&XGate.circuit->simulator);
    break;
  default :
    break;
  }


  if (XGate.ErrorMarkTimeout) {
    DrawErrorPositionMark();
    XGate.ErrorMarkTimeout = 1;
  }

  if (es->clip.isActive) {
    XSetClipMask(XGate.D,XGate.instGC,None);
    XSetClipMask(XGate.D,XGate.moduleGC,None);
    XSetClipMask(XGate.D,XGate.modportGC,None);
    XSetClipMask(XGate.D,XGate.frameGC,None);
    XSetClipMask(XGate.D,XGate.commentGC,None);
    XSetClipMask(XGate.D,XGate.hyperlinkGC,None);
    XSetClipMask(XGate.D,XGate.wireGC,None);
    XSetClipMask(XGate.D,XGate.busGC,None);
    XSetClipMask(XGate.D,XGate.selWireGC,None);
    XSetClipMask(XGate.D,XGate.selBusGC,None);
    XSetClipMask(XGate.D,XGate.toolGC,None);
    XSetClipMask(XGate.D,XGate.cpathGC,None);
#if TKGATE_JSUPPORT
    XSetClipMask(XGate.D,XGate.kanjiGC,None);
#endif
  }
}

void editstate_fullUpdate(EditState *es)
{
#if DEBUG_REDISPLAY
  printf("editstate_fullUpdate(*)\n");
#endif
  wm_GetDimensions(&XGate.width,&XGate.height);
  XClearWindow(XGate.D,XGate.W);
  mark_flush();

  if (!es) return;

#if 0
  /* Not needed */
  ob_touch(es);
#endif

  editstate_saveOrig(es);
  es->clip.isActive = 0;
  es->clip.xmin = 0;
  es->clip.ymin = 0;
  es->clip.xmax = XGate.width;
  es->clip.ymax = XGate.height;
  editstate_update(es);
}

/*
   Do a partial screen update.
*/
void editstate_regionUpdate(EditState *es,int xmin,int ymin,int xmax,int ymax)
{
#if DEBUG_REDISPLAY
  printf("editstate_regionUpdate(*,%d,%d,%d,%d)\n",xmin,ymin,xmax,ymax);
#endif

  wm_GetDimensions(&XGate.width,&XGate.height);
  mark_hide();
  XClearArea(XGate.D,XGate.W,xmin,ymin,xmax-xmin,ymax-ymin,False);

  if (!es) return;

#if 0
  /* Not needed */
  ob_touch(es);
#endif
  editstate_saveOrig(es);
  es->clip.isActive = 1;
  es->clip.xmin = xmin;
  es->clip.ymin = ymin;
  es->clip.xmax = xmax;
  es->clip.ymax = ymax;
  
  editstate_update(es);
}

void editstate_displayPathString(EditState *es)
{
  EditState *estack[MAX_STACKDEPTH];
  char path[STRMAX],*p;
  int esp,i;

  for (esp = 0;esp < MAX_STACKDEPTH && es;es = es->parent)
    estack[esp++] = es;

  p = path;

  for (i = esp-1;i >= 0;i--) {
    if (i != esp-1) *p++ = ':';
    if (estack[i]->inst)
      sprintf(p,"%s(%s)",estack[i]->env->Name,estack[i]->inst->ename);
    else
      strcpy(p,estack[i]->env->Name);
    p = p + strlen(p);
  }

  if (XGate.tcl)
    Tcl_SetVar(XGate.tcl,"tkg_currentBlock",path,TCL_GLOBAL_ONLY);
}

void listPorts(GModuleDef *M)
{
  GCElement *g = M->blockdescript;

  DoTcl("tkg_portListClear");
  if (g) {
    int i;
    GGateInfo *gi = g->typeinfo;
    GWire *w;

    for (i = 0;i < gi->NumPads;i++) {
      for (w = g->wires[i];w;w = w->next) {
	char *type = "";

	switch (gi->Pad[i].Dir) {
	case IN : type = ">"; break;
	case OUT : type = "<"; break;
	case TRI : type = "="; break;
	}
	if (w->net->nbits > 1)
	  DoTcl("tkg_portListAdd \"%s%s\\[%d:0\\]\"",type,w->name,w->net->nbits-1);
	else
	  DoTcl("tkg_portListAdd \"%s%s\"",type,w->name);
      }
    }
  }

  DoTcl("tkg_portListEnd");
}

/*
   Do any special processing when an editstate becomes active
*/
void editstate_setCurrent(EditState *es)
{
  listNets(es->env);
  listPorts(es->env);
  editstate_displayPathString(es);
}

/*
  Check for a module along the specified path.  Returns
  '1' on success, '0' on failure.

  Two types of paths are recognized:

     mod:inst			A module name and an instance in the module
     inst1.inst2.inst3		A path of instances from the root module all
				but the last instance must be a module instance.
 */
int editstate_checkPath(EditState **es,const char *path)
{
  GModuleDef *M = XGate.circuit->root_mod;
  GCElement *g;
  char buf[STRMAX],*p,*q;

  strcpy(buf,path);
  p = buf;

  if ((q = strchr(p,':'))) {
    *q++ = 0;
    M = env_findModule(p);
    if (!M) return 0;
    p = q;
  } else {
    for (;;) {
      q = strchr(p,'.');
      if (q) {					/* Looking for a module instance */
	*q++ = 0;
	g = GModuleDef_findGate(M,p);
	if (!g) return 0;
	if (g->typeinfo->Code != BLOCK) return 0;
	M = env_findModule(g->u.block.BlockFunction);
	if (!M) return 0;
	p = q;
      } else
	break;
    }
  }

  g = GModuleDef_findGate(M,p);
  if (!g && !GModuleDef_findNet(M,p))
    return 0;

  return 1;
}

/*
  Set the current module along the specified path.  Returns
  '1' on success, '0' on failure.
 */
int editstate_setPath(EditState **es,const char *path)
{
  GModuleDef *M = XGate.circuit->root_mod;
  GCElement *g;
  GNet *n;
  char buf[STRMAX],*p,*q;

  strcpy(buf,path);
  p = buf;

  if ((q = strchr(p,':'))) {
    *q++ = 0;
    M = env_findModule(p);
    if (!M) return 0;
    editstate_navigateToModule(es,M);
    p = q;
  } else {
    while ((*es)->parent) editstate_pop(es);

    for (;;) {
      q = strchr(p,'.');
      if (q) {					/* Looking for a module instance */
	*q++ = 0;
	g = GModuleDef_findGate(M,p);
	if (!g) return 0;
	if (g->typeinfo->Code != BLOCK) return 0;
	M = env_findModule(g->u.block.BlockFunction);
	if (!M) return 0;
	p = q;
	editstate_push(es,M,g);
      } else
	break;
    }
  }

  g = GModuleDef_findGate(M,p);
  n = GModuleDef_findNet(M,p);
  if (g) {
    editstate_setCurrent(XGate.circuit->es);
    search_setPosition(M,g,0);
  } else if (n) {
    editstate_setCurrent(XGate.circuit->es);
    search_setPosition(M,0,n);
  } else
    return 0;

  return 1;
}

char *editstate_getPath(EditState *es,char *buf)
{
  if (!es->parent) {
    *buf = 0;
    return buf;
  } else {
    buf = editstate_getPath(es->parent,buf);
    if (es->parent->parent)
      *buf++ = '.';
    if (es->inst)
      strcpy(buf,es->inst->ename);
    else
      strcpy(buf,"-");
    return buf + strlen(buf);
  }
}

void editstate_push(EditState **es,GModuleDef *M,GCElement *g)
{
  EditState *newes;
  GSimModule *old_sm = (es && *es) ? (*es)->smod : 0;

  mark_flush();

  newes = new_EditState();
  ob_touch(newes);

  newes->env = M;
  if (*es && ((*es)->inst || !(*es)->parent))
    newes->inst = g;

  if (g && tkgate_currentMode() == MM_SIMULATE) {
    GSimModule *sM = (GSimModule*) SHash_find((*es)->smod->children,g->ename);
    newes->smod = sM;
  }

  if (*es) {
    ob_touch(*es);
    editstate_saveOrig(*es);
  }

  newes->parent = (*es);
  *es = newes;

  sel_clear(*es);
  XGate_clearSelection();

  if (tkgate_currentMode() == MM_SIMULATE && es && *es)
    SimInterface_changeCurrentModule((*es)->smod,old_sm);

  XGate_setOrigin(0,0);
}

void editstate_pop(EditState **es)
{
  EditState *newes;
  GSimModule *old_sm = (es && *es) ? (*es)->smod : 0;

#if 0
  if (tkgate_currentMode() == MM_SIMULATE && !(*es)->parent) return;
#endif

  if ((*es)->isInterface) {
    modint_update(*es);
  }

  if (!(newes = (*es)->parent)) {
    *es = 0;
    return;
  }

  sel_clear(*es);
  XGate_clearSelection();
  XGate_setOrigin(newes->save_x,newes->save_y);
  mark_flush();
  delete_editstate(*es);
  *es = newes;

  if (tkgate_currentMode() == MM_SIMULATE && es && *es)
    SimInterface_changeCurrentModule((*es)->smod,old_sm);
}

void editstate_navigateToModule(EditState **es,GModuleDef *M)
{
  EditState *xes;

  for (xes = *es;xes && xes->env != M;xes = xes->parent);

  if (xes) {
    /* Move up to block */
    while ((*es)->env != M)
      editstate_pop(es);
  } else {
    /* Push down to block */
    if (!(*es)->inst)
      editstate_pop(es);
    editstate_push(es,M,0);
  }
}

void editstate_makeRootAtTop(EditState **es)
{
  EditState *pes;
  int need_reroot = 0;

  unselectAll(*es);

  pes = *es;
  while (pes->parent) {
    if (!pes->inst) {
      need_reroot = 1;
      break;
    }
    pes = pes->parent;
  }

  if (pes->env != XGate.circuit->root_mod)
    need_reroot = 1;

  if (need_reroot) {
    while (*es)
      editstate_pop(es);
    editstate_push(es,XGate.circuit->root_mod,0);
    editstate_setCurrent(*es);
    ClearErrorMark();
    net_unselect(0);
    FlagRedraw();
  }

}

/*
   Delete all modules
*/
void editstate_flushModules(EditState **es)
{
  HashElem *E;

  Circuit_clearProbes(XGate.circuit);

  while (*es)
    editstate_pop(es);

  sel_clear(*es);

  for (E = Hash_first(XGate.circuit->moduleTable);E;E = Hash_next(XGate.circuit->moduleTable,E)) {
    GModuleDef *M = (GModuleDef*) HashElem_obj(E);
    delete_GModuleDef(M);
  }
  SHash_flush(XGate.circuit->moduleTable);
  DoTcl("tkg_blockListClear\n");
  DoTcl("gat_setCircProp -clearscripts");
  env_clear(XGate.circuit->mid_mod);
}

void search_setPosition(GModuleDef *M,GCElement *g,GNet *n)
{
  int x,y;

  x = y = 0;

  if (XGate.circuit->es->env != M) {
    ob_touch(XGate.circuit);
    editstate_navigateToModule(&XGate.circuit->es,M);
    editstate_setCurrent(XGate.circuit->es);
    ClearErrorMark();
  }

  if (g) {
    int minx,miny,maxx,maxy;

    gate_getbbx(g,&minx,&miny,&maxx,&maxy);
    x = (minx+maxx)/2;
    y = (miny+maxy)/2;

    XGate_clearSelection();
    sel_clear(XGate.circuit->es);
    sel_appendGate(XGate.circuit->es,g);
    sel_finish(XGate.circuit->es);

    message(0,msgLookup("msg.foundgate"),g->ename);		/* "Found gate named '%s'" */
  } else if (n) {
    GWireNode *wn1,*wn2;

    wn1 = n->driver->nodes;
    wn2 = wn1->out ? wn1->out : wn1->in;

    x = (wn1->x + wn2->x)/2;
    y = (wn1->y + wn2->y)/2;

    XGate_clearSelection();
    sel_clear(XGate.circuit->es);
    DoTcl("tkg_netSelect %s",n->signame);

    message(0,msgLookup("msg.foundwire"),n->signame);		/* "Found wire named '%s'" */
  }


  XGate_setOrigin(XGate.width/2 - x,XGate.height/2 - y);
  ob_touch(XGate.circuit->es);
  editstate_saveOrig(XGate.circuit->es);

  SetErrorPosition(x,y);
  FlagRedraw();
}

void search_init(GSearchContext *S)
{
  ob_touch(S);
  S->mode = 0;
  S->target = 0;
  S->m_elem = 0;
  S->g_elem = 0;
  S->n_elem = 0;
}


GSearchContext *new_GSearchContext()
{
  GSearchContext *S = (GSearchContext *) ob_malloc(sizeof(GSearchContext),"GSearchContext");
  search_init(S);

  return S;
}


void search_clear(GSearchContext *S)
{
  ob_touch(S);
  S->mode = 0;
  if (S->target) ob_free(S->target);
  S->target = 0;
  S->m_elem = 0;
  S->g_elem = 0;
  S->n_elem = 0;
}


void search_find(GSearchContext *S,const char *target,int mode)
{
  GModuleDef *M = 0;

  if (!*target) {
    search_clear(S);
    return;
  }

  if (S->mode != mode || !S->target || strcmp(S->target,target) != 0)
    search_clear(S);

  ob_touch(S);
  S->target = ob_strdup(target);
  S->mode = mode;

  if (!S->m_elem) {
    S->m_elem = Hash_first(XGate.circuit->moduleTable);
    if (S->m_elem) {
      M = (GModuleDef *) HashElem_obj(S->m_elem);
      if ((mode & 2)) S->g_elem = Hash_first(M->gates);
      if ((mode & 1)) S->n_elem = Hash_first(M->gates);
    }
  } else
    M = (GModuleDef *) HashElem_obj(S->m_elem);

  while (S->m_elem) {
    if (S->g_elem) {
      GCElement *g = (GCElement*) HashElem_obj(S->g_elem);
      S->g_elem = Hash_next(M->gates,S->g_elem);

      if (g->typeinfo->Code == JOINT) continue;

      if (strstr(g->ename,S->target)) {
	search_setPosition(M,g,0);
	return;
      }
    } else if (S->n_elem) {
      GNet *n = (GNet*) HashElem_obj(S->n_elem);
      S->n_elem = Hash_next(M->nets,S->n_elem);
      if (strstr(n->signame,S->target)) {
	search_setPosition(M,0,n);
	return;
      }
    } else {
      S->m_elem = Hash_next(XGate.circuit->moduleTable,S->m_elem);
      if (S->m_elem) {
	M = (GModuleDef *) HashElem_obj(S->m_elem);
	if ((mode & 2)) S->g_elem = Hash_first(M->gates);
	if ((mode & 1)) S->n_elem = Hash_first(M->nets);
      }
    }
  }
  message(1,msgLookup("msg.searchagn"),S->target);	/* Target string '%s' not found.  Hit 'find' to restart search again. */
}

/*
 * Called before and after an undo or redo to synchronize the tcl/tk interface
 * elements with the new internal state.
 */
void undo_sync(int is_after)
{
  char buf[STRMAX];
  static int old_mode;

  if (!is_after) {
    /* BEFORE UNDO/REDO */
    old_mode = XGate.circuit->mode;
  } else {
    /* AFTER UNDO/REDO */

    /*
     * Update list of nets/ports
     */
    editstate_setCurrent(XGate.circuit->es);

    /*
     * Update module name list.
     */
    DoTcl("gat_listBlocks -u");


    /*
     * Update tcl-side "mode" variable
     */
    sprintf(buf,"%d",XGate.circuit->mode);
    Tcl_SetVar(XGate.tcl,"mode",buf,TCL_GLOBAL_ONLY);

    /*
     * Update tcl-side "rot" variable
     */
    sprintf(buf,"%d",XGate.circuit->rot);
    Tcl_SetVar(XGate.tcl,"rot",buf,TCL_GLOBAL_ONLY);

    /*
     * Reset correct cursor for mode.
     */
    if (old_mode != XGate.circuit->mode)
      setEditCursor(XGate.circuit->mode);
  }

  SyncModified();
}
