Logo Search packages:      
Sourcecode: xcircuit version File versions

libraries.c

/*-------------------------------------------------------------------------*/
/* libraries.c --- xcircuit routines for the builtin and user libraries    */
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University               */
/*-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/
/*      written by Tim Edwards, 8/13/93                              */
/*-------------------------------------------------------------------------*/

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

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#ifdef TCL_WRAPPER 
#include <tk.h>
#else
#include "Xw/Xw.h"
#endif

#include <math.h>

/*-------------------------------------------------------------------------*/
/* Local includes                                        */
/*-------------------------------------------------------------------------*/

#include "colordefs.h"
#include "xcircuit.h"

/*----------------------------------------------------------------------*/
/* Function prototype declarations                                      */
/*----------------------------------------------------------------------*/
#include "prototypes.h"

/*-------------------------------------------------------------------------*/
/* Global Variable definitions                                       */
/*-------------------------------------------------------------------------*/

extern short   eventmode;     /* keep track of the mode the screen is in */
extern Display    *dpy;   /* Works well to make this globally accessible */
extern int *appcolors;
extern Cursor     appcursors[NUM_CURSORS];
extern Globaldata xobjs;
extern Clientdata areastruct;
extern char _STR[150];
extern short fontcount;
extern fontinfo *fonts;

/*---------------------------------------------------------*/
/* Find the Helvetica font for use in labeling the objects */
/*---------------------------------------------------------*/

short findhelvetica()
{
   short fval;

   for (fval = 0; fval < fontcount; fval++)
      if (!strcmp(fonts[fval].psname, "Helvetica"))
       break; 

   /* If not there, use the first Helvetica font */

   if (fval == fontcount) {
      for (fval = 0; fval < fontcount; fval++)
         if (!strcmp(fonts[fval].family, "Helvetica"))
          break; 
   }

   /* If still not there, use the first non-Symbol font */
   /* If this doesn't work, then the libraries are probably misplaced. . .*/

   if (fval == fontcount) {
      for (fval = 0; fval < fontcount; fval++)
         if (strcmp(fonts[fval].family, "Symbol"))
          break; 
   }

   return fval;
}

/*-------------------------------------------*/
/* Return to drawing window from the library */
/*-------------------------------------------*/

void catreturn()
{
   /* Pop the object being edited from the push stack. */

   popobject(NULL, NULL, NULL);
}

/*------------------------------------------------------*/
/* Find page number from cursor position        */
/* Mode = 0:  Look for exact corresponding page number  */
/*   and return -1 if out-of-bounds             */
/* Mode = 1:  Look for position between pages, return */
/*   page number of page to the right.          */
/*------------------------------------------------------*/

int pageposition(short libmode, XButtonEvent *event, int mode)
{
   int xin, yin, bpage, pages;
   int gxsize, gysize, xdel, ydel;

   pages = (libmode == PAGELIB) ? xobjs.pages : xobjs.numlibs;
   computespacing(libmode, &gxsize, &gysize, &xdel, &ydel);
   window_to_user(event->x, event->y, &areastruct.save);

   if (mode == 0) {     /* On-page */
      if (areastruct.save.x >= 0 && areastruct.save.y <= 0) {
         xin = areastruct.save.x / xdel;
         yin = areastruct.save.y / ydel; 
         if (xin < gxsize && yin > -gysize) {
            bpage = (xin % gxsize) - (yin * gxsize);
            if (bpage < pages)
             return bpage;
         }
      }
      return -1;
   }
   else {         /* Between-pages */
      xin = (areastruct.save.x + (xdel >> 1)) / xdel;
      if (xin > gxsize) xin = gxsize;
      if (xin < 0) xin = 0;
      yin = areastruct.save.y  / ydel; 
      if (yin > 0) yin = 0;
      if (yin < -gysize) yin = -gysize;
      bpage = (xin % (gxsize + 1)) + 1 - (yin * gxsize);
      if (bpage > pages + 1) bpage = pages + 1;
      return bpage;
   }
}

/*------------------------------------------------------*/
/* Find the number of other pages linked to the       */
/* indicated page (having the same filename, and      */
/* ignoring empty pages).  result is the total number */
/* of pages in the output file.                       */
/*------------------------------------------------------*/

short pagelinks(int page)
{
   int i;
   short count = 0;

   for (i = 0; i < xobjs.pages; i++)
      if (xobjs.pagelist[i]->pageinst != NULL)
       if (xobjs.pagelist[i]->pageinst->thisobject->parts > 0)
          if ((i == page) || (!strcmp(xobjs.pagelist[i]->filename,
                  xobjs.pagelist[page]->filename)))
             count++;

   return count;
}

/*------------------------------------------------------*/
/* This is an expanded version of pagelinks() (above),      */
/* to deal with the separate issues of independent top-     */
/* level schematics and subcircuits.  For the indicated     */
/* page, return a list of pages depending on the mode:      */
/*                                        */
/* mode = INDEPENDENT: independent top-level pages    */
/* mode = DEPENDENT: dependent pages (subcircuits)    */
/* mode = PAGE_DEPEND: subcircuits of the current page,     */
/* mode = TOTAL_PAGES: independent pages + subcircuits  */
/* mode = ALL_PAGES: all pages in xcircuit            */
/*                                        */
/* The list is the size of the number of pages, and   */
/* entries corresponding to the requested mode are set      */
/* nonzero (the actual number indicates the number of */
/* references to the page, which may or may not be    */
/* useful to know).                             */
/*                                        */
/* It is the responsibility of the calling routine to */
/* free the memory allocated for the returned list.   */
/*------------------------------------------------------*/

short *pagetotals(int page, short mode)
{
   int i;
   short *counts, *icount, *result;

   if (xobjs.pagelist[page]->pageinst == NULL) return;

   counts = (short *)malloc(xobjs.pages * sizeof(short));
   icount = (short *)malloc(xobjs.pages * sizeof(short));
   for (i = 0; i < xobjs.pages; i++) {
      *(counts + i) = 0;
      *(icount + i) = 0;
   }

   /* Find all the subcircuits of this page */

   if (mode != ALL_PAGES)
      findsubschems(page, xobjs.pagelist[page]->pageinst->thisobject, 0, counts);

   /* Check independent entries (top-level pages which are not    */
   /* subcircuits of another page).  Set the counts entry to -1   */
   /* to mark each independent page.                        */

   if (mode != PAGE_DEPEND)
      for (i = 0; i < xobjs.pages; i++)
         if (xobjs.pagelist[i]->pageinst != NULL)
          if (xobjs.pagelist[i]->pageinst->thisobject->parts > 0)
          {
             if (mode == ALL_PAGES)
              (*(counts + i)) = 1;
             else
             {
                if ((i == page) || (!strcmp(xobjs.pagelist[i]->filename,
                  xobjs.pagelist[page]->filename)))
                   if (*(counts + i) == 0)
                    (*(icount + i))++;
             }
          }

   /* Check other dependent entries (top-level pages which are    */
   /* subcircuits of any independent page).                 */

   if ((mode == DEPENDENT) || (mode == TOTAL_PAGES))
   {
      for (i = 0; i < xobjs.pages; i++)
       if ((i != page) && (*(icount + i) > 0))
          findsubschems(i, xobjs.pagelist[i]->pageinst->thisobject, 0, counts);
   }

   if (mode == INDEPENDENT)
   {
      free((char *)counts);
      return icount;
   }
   else
   {
      if (mode == TOTAL_PAGES) {    /* merge dependent and independent */
       for (i = 0; i < xobjs.pages; i++)
          if (*(icount + i) > 0)
             (*(counts + i))++;
      }
      free((char *)icount);
      return counts;
   }
}

/*------------------------------------------------------*/
/* Test whether an object is a page, and return the   */
/* page number if it is.  Otherwise, return -1.       */
/*------------------------------------------------------*/

int is_page(objectptr thisobj)
{
   int i;
   
   for (i = 0; i < xobjs.pages; i++)
      if (xobjs.pagelist[i]->pageinst != NULL)
         if (xobjs.pagelist[i]->pageinst->thisobject == thisobj) return i;

   return -1;
}

/*------------------------------------------------------*/
/* Test whether an object is a library, and return the      */
/* library number if it is.  Otherwise, return -1.    */
/*------------------------------------------------------*/

int is_library(objectptr thisobj)
{
   int i;
   
   for (i = 0; i < xobjs.numlibs; i++)
      if (xobjs.libtop[i + LIBRARY]->thisobject == thisobj) return i;

   return -1;
}

/*------------------------------------------------------*/
/* ButtonPress handler during page catalog viewing mode */
/*------------------------------------------------------*/

void pagecatbutton(XButtonEvent *event)
{
   int bpage;
   short mode;

   for (mode = 0; mode < LIBRARY; mode++) {
      if (areastruct.topinstance == xobjs.libtop[mode]) break;
   }
   if (mode == LIBRARY) return;  /* Something went wrong if this happens */

   if (event->button == Button1 || event->button == Button2) {
      if ((bpage = pageposition(mode, event, 0)) >= 0) {
              
#ifdef SCHEMA
       if (eventmode == ASSOC_MODE) {
          if (mode == PAGELIB) {
             /* using changepage() allows use of new page for schematic */
             changepage(bpage);
             /* associate the new schematic */
             schemassoc(topobject, areastruct.stack->thisinst->thisobject);
             /* pop back to calling (symbol) page */
             catreturn();
             eventmode = PRESS_MODE;
          }
          else {
             startcatalog(NULL, (pointertype)(LIBRARY + bpage), NULL);
          }
          return;
         }
       else
#endif
       if (event->button == Button1) {

          /* like catreturn(), but don't actually go to the popped page */
          objectdeselect();
          eventmode = NORMAL_MODE;
          if (mode == PAGELIB) {
             newpage(bpage);
                   eventmode = PRESS_MODE;
          }
          else {
             startcatalog(NULL, (pointertype)(LIBRARY + bpage), NULL);
          }
          return;
       }
       else {  /* Button2 */
          if (mode == PAGELIB)    /* No such method for LIBLIB is defined. */
             objectselect(OBJECT);
       }
      }
   }
   else {
      eventmode = NORMAL_MODE;
      catreturn();
   }
}

/*------------------------------------------------------------------------------*/
/* Subroutine to find the correct scale and position of the object instance   */
/* representing an entire page in the page directory.                   */
/*------------------------------------------------------------------------------*/

void pageinstpos(short mode, short tpage, objinstptr drawinst, int gxsize,
      int gysize, int xdel, int ydel)
{
   objectptr libobj = drawinst->thisobject;
   float scalex, scaley;

   drawinst->position.x = (tpage % gxsize) * xdel;
   drawinst->position.y = -(tpage / gxsize + 1) * ydel;

   /* center the object on its page bounding box */

   if (drawinst->bbox.width == 0 || drawinst->bbox.height == 0) {
      drawinst->scale = 0.45 * libobj->viewscale;
      drawinst->position.x += 0.05 * xdel - libobj->pcorner.x * drawinst->scale;
      drawinst->position.y += 0.05 * ydel - libobj->pcorner.y * drawinst->scale;
   }
   else {
      scalex = (0.9 * xdel) / drawinst->bbox.width;
      scaley = (0.9 * ydel) / drawinst->bbox.height;
      if (scalex > scaley) {
         drawinst->scale = scaley;
       drawinst->position.x -= (drawinst->bbox.lowerleft.x * scaley);
         drawinst->position.x += (xdel - (drawinst->bbox.width * scaley)) / 2;
         drawinst->position.y += 0.05 * ydel - drawinst->bbox.lowerleft.y
                  * drawinst->scale;
      }
      else {
         drawinst->scale = scalex;
       drawinst->position.y -= (drawinst->bbox.lowerleft.y * scalex);
         drawinst->position.y += (ydel - (drawinst->bbox.height * scalex)) / 2;
         drawinst->position.x += 0.05 * xdel - drawinst->bbox.lowerleft.x
                  * drawinst->scale;
      }
   }
}

/*--------------------------------------------------------------*/
/* Make a new instance for inserting into the page directory      */
/*--------------------------------------------------------------*/

objinstptr newpageinst(objectptr pageobj)
{
   objinstptr newinst = (objinstptr) malloc(sizeof(objinst));
   objectdefaults(newinst, pageobj, 0, 0);
   newinst->type = OBJECT;
   newinst->color = DEFAULTCOLOR;
   return newinst;
}

/*-----------------------------------------------------------*/
/* Find spacing of objects for pages in the page directories */
/*-----------------------------------------------------------*/

void computespacing(short mode, int *gxsize, int *gysize, int *xdel, int *ydel)
{
   int pages = (mode == PAGELIB) ? xobjs.pages : xobjs.numlibs;

   *gxsize = (int)sqrt((double)pages) + 1;
   *gysize = 1 + pages / (*gxsize);

   /* 0.5 is the default vscale;  g#size is the number of pages per line */

   *xdel = areastruct.width / (0.5 * (*gxsize));
   *ydel = areastruct.height / (0.5 * (*gysize));
}

/*-------------------------------------------------------------------*/
/* Draw the catalog of page ordering or the library master directory */
/*-------------------------------------------------------------------*/

void composepagelib(short mode)
{
   genericptr *pgen;
   objinstptr drawinst;
   objectptr libobj, directory = xobjs.libtop[mode]->thisobject;
   short i;
   polyptr *drawbox;
   labelptr *pagelabel;
   stringpart *strptr;
   pointlist pointptr;
   int margin, xdel, ydel, gxsize, gysize;
   int pages = (mode == PAGELIB) ? xobjs.pages : xobjs.numlibs;
   short fval = findhelvetica();

   /* Like the normal libraries, instances come from a special list, so  */
   /* they should not be destroyed, but will be null'd out and retrieved */
   /* from the list.                                         */

   for (pgen = directory->plist; pgen < directory->plist + directory->parts; pgen++)
      if ((*pgen)->type == OBJECT) *pgen = NULL;

   reset(directory, NORMAL);

   /* generate the list of object instances */

   directory->plist = (genericptr *)malloc(sizeof(genericptr));
   directory->parts = 0;

   computespacing(mode, &gxsize, &gysize, &xdel, &ydel);
   margin = xdel / 40;  /* margin between pages */

   for (i = 0; i < pages; i++) {
      drawinst = (mode == PAGELIB) ? xobjs.pagelist[i]->pageinst :
            xobjs.libtop[i + LIBRARY];
      if (drawinst != NULL) {
       libobj = drawinst->thisobject;

       /* This is a stop-gap measure. . . should be recalculating the bounds of */
       /* the instance on every action, not just before arranging the library.  */
       drawinst->bbox.lowerleft.x = libobj->bbox.lowerleft.x;
       drawinst->bbox.lowerleft.y = libobj->bbox.lowerleft.y;
       drawinst->bbox.width = libobj->bbox.width;
       drawinst->bbox.height = libobj->bbox.height;
       /* End stop-gap measure */

       PLIST_INCR(directory);
       *(directory->plist + directory->parts) = (genericptr)drawinst;
       directory->parts++;
         pageinstpos(mode, i, drawinst, gxsize, gysize, xdel, ydel);
      }

      /* separate pages (including empty ones) with bounding boxes */

      NEW_POLY(drawbox, directory);
      (*drawbox)->color = LOCALPINCOLOR;   /* default red */
      (*drawbox)->style = NORMAL;            /* CLOSED */
      (*drawbox)->width = 1.0;
      (*drawbox)->number = 4;
      (*drawbox)->points = (pointlist) malloc(4 * sizeof(XPoint));
      (*drawbox)->num_params = 0;
      (*drawbox)->passed = NULL;
      pointptr = (*drawbox)->points;
      pointptr->x = (i % gxsize) * xdel + margin;
      pointptr->y = -(i / gxsize) * ydel - margin;
      pointptr = (*drawbox)->points + 1;
      pointptr->x = ((i % gxsize) + 1) * xdel - margin;
      pointptr->y = -(i / gxsize) * ydel - margin;
      pointptr = (*drawbox)->points + 2;
      pointptr->x = ((i % gxsize) + 1) * xdel - margin;
      pointptr->y = -((i / gxsize) + 1) * ydel + margin;
      pointptr = (*drawbox)->points + 3;
      pointptr->x = (i % gxsize) * xdel + margin;
      pointptr->y = -((i / gxsize) + 1) * ydel + margin;
      directory->parts++;

      /* each page gets its name printed at the bottom */

      if (drawinst != NULL) {
       NEW_LABEL(pagelabel, directory);
       labeldefaults(*pagelabel, False, (pointptr->x + (pointptr-1)->x) / 2,
                  pointptr->y - 5);
       (*pagelabel)->color = DEFAULTCOLOR;
       (*pagelabel)->scale = 0.75;
       (*pagelabel)->string->data.font = fval;
         (*pagelabel)->num_params = 0;
         (*pagelabel)->passed = NULL;
       strptr = makesegment(&((*pagelabel)->string), NULL);
       strptr->type = TEXT_STRING;
       strptr->data.string = (char *) malloc(1 + strlen(libobj->name));
       strcpy(strptr->data.string, libobj->name);
       (*pagelabel)->justify = TOP | NOTBOTTOM | NOTLEFT;
       directory->parts++;
      }
   }

   /* calculate a bounding box for this display */
   /* and center it in its window */

   calcbbox(xobjs.libtop[mode]);
   centerview(xobjs.libtop[mode]);
}

/*------------------------------------------------------------*/
/* Update the page or library directory based on new bounding */
/* box information for the page or library passed in "tpage". */
/*------------------------------------------------------------*/

void updatepagelib(short mode, short tpage)
{
   objectptr compobj, libinst = xobjs.libtop[mode]->thisobject;
   objinstptr pinst;
   genericptr *gelem;
   int i, xdel, ydel, gxsize, gysize, lpage;

   /* lpage is the number of the page as found on the directory page */
   lpage = (mode == PAGELIB) ? tpage : tpage - LIBRARY;
   compobj = (mode == PAGELIB) ? xobjs.pagelist[tpage]->pageinst->thisobject
            : xobjs.libtop[tpage]->thisobject;

   computespacing(mode, &gxsize, &gysize, &xdel, &ydel);

   for (i = 0; i < libinst->parts; i++) {
      gelem = libinst->plist + i;
      if ((*gelem)->type == OBJECT) {
       pinst = TOOBJINST(gelem);
         if (pinst->thisobject == compobj) {
          /* recalculate scale and position of the object instance */
            pageinstpos(mode, lpage, pinst, gxsize, gysize, xdel, ydel);
          break;
       }
      }
   }

   /* if there is no instance for this page, then recompose the whole library */

   if (i == libinst->parts) composelib(mode);
}

/*----------------------*/
/* Rearrange pages      */
/*----------------------*/

void pagecatmove(XKeyEvent *event)
{
   int bpage;
   objinstptr exchobj;
   Pagedata *ipage, **testpage, **tpage2;

   if (areastruct.selects == 0) return;
   else if (areastruct.selects > 2) {
      Wprintf("Select maximum of two objects.");
      return;
   }

   /* Get the page corresponding to the first selected object */

   exchobj = SELTOOBJINST(areastruct.selectlist);
   for (testpage = xobjs.pagelist; testpage < xobjs.pagelist + xobjs.pages; testpage++)
      if (*testpage != NULL && (*testpage)->pageinst == exchobj)
       break;

   /* If two objects are selected, then exchange their order */

   if (areastruct.selects == 2) {
      exchobj = SELTOOBJINST(areastruct.selectlist + 1);
      for (tpage2 = xobjs.pagelist; tpage2 < xobjs.pagelist + xobjs.pages; tpage2++)
       if (*tpage2 != NULL && (*tpage2)->pageinst == exchobj)
          break;

      ipage = *testpage;
      *testpage = *tpage2;
      *tpage2 = ipage;
   }

   /* If one object selected; find place to put from cursor position */

   else if ((bpage = pageposition(PAGELIB, (XButtonEvent *)event, 1)) >= 0) {
      int k, epage;
      Pagedata *eptr;

      /* Find page number of the original page */

      epage = (int)(testpage - xobjs.pagelist);
      eptr = *(xobjs.pagelist + epage);

      /* move page (epage) to position between current pages */
      /* (bpage - 2) and (bpage - 1) by shifting pointers.   */

      if ((bpage - 1) < epage) {
       for (k = epage - 1; k >= bpage - 1; k--) {
          *(xobjs.pagelist + k + 1) = *(xobjs.pagelist + k);
          renamepage(k + 1);
       }
       *(xobjs.pagelist + bpage - 1) = eptr;
       renamepage(bpage - 1);
      }
      else if ((bpage - 2) > epage) {
       for (k = epage + 1; k <= bpage - 2; k++) {
          *(xobjs.pagelist + k - 1) = *(xobjs.pagelist + k);
          renamepage(k - 1);
       }
       *(xobjs.pagelist + bpage - 2) = eptr;
       renamepage(bpage - 2);
      }
   }

   objectdeselect();
   composelib(PAGELIB);
   drawarea(NULL, NULL, NULL);
}

/*-----------------------------------------*/
/* Draw the catalog of predefined elements */
/*-----------------------------------------*/

void composelib(short mode)
{
   genericptr *pgen;
   objinstptr drawinst;
   labelptr *drawname;
   objectptr libobj, libpage = xobjs.libtop[mode]->thisobject;
   liblistptr spec;
   int xpos = 0, ypos = areastruct.height << 1;
   int nypos = 220, nxpos;
   short fval;
   short llx, lly, width, height;

   /* Also make composelib() a wrapper for composepagelib() */
   if ((mode > FONTLIB) && (mode < LIBRARY)) {
      composepagelib(mode);
      return;
   }

   /* The instances on the library page come from the library's         */
   /* "instlist".  So that we don't destroy the actual instance when we */
   /* call reset(), we find the pointer to the instance and NULL it.    */
   
   for (pgen = libpage->plist; pgen < libpage->plist + libpage->parts; pgen++)
      if ((*pgen)->type == OBJECT) *pgen = NULL;

   reset(libpage, NORMAL);

   /* Return if library defines no objects */

   if (xobjs.userlibs[mode - LIBRARY].number == 0) return;

   /* Find the Helvetica font for use in labeling the objects */

   fval = findhelvetica();

   /* generate the list of object instances and their labels */

   for (spec = xobjs.userlibs[mode - LIBRARY].instlist; spec != NULL;
            spec = spec->next) {
      libobj = spec->thisinst->thisobject;

      /* "Hidden" objects are not drawn */
      if (libobj->hidden == True) continue;

      drawinst = spec->thisinst;
      drawinst->position.x = 0;
      drawinst->position.y = 0;

      /* Get the bounding box of the instance in the page's coordinate system */
      calcinstbbox((genericptr *)(&drawinst), &llx, &lly, &width, &height);
      width -= llx;  /* convert urx to width */
      height -= lly; /* convert ury to height */

      /* Determine the area needed on the page to draw the object */

      nxpos = xpos + ((width > 170) ? width + 30 : 200);
      if ((nxpos > (areastruct.width << 1)) && (xpos > 0)) {
       nxpos -= xpos; 
       xpos = 0;
       ypos -= nypos;
       nypos = 200;
      }
      /* extra space of 20 is to leave room for the label */

      if (height > (nypos - 50)) nypos = height + 50;

      drawinst->position.x = xpos - llx;
      drawinst->position.y = ypos - (height + lly);
      if (width <= 170) drawinst->position.x += ((170 - width) >> 1);
      if (height <= 170) drawinst->position.y -= ((170 - height) >> 1);
      drawinst->color = DEFAULTCOLOR;

      PLIST_INCR(libpage); 
      *(libpage->plist + libpage->parts) = (genericptr)drawinst;
      libpage->parts++;

      if (fval < fontcount) {
       stringpart *strptr;

       NEW_LABEL(drawname, libpage);
         libpage->parts++;
       labeldefaults(*drawname, False, 0, 0);
       (*drawname)->color = (spec->virtual) ?
                  OFFBUTTONCOLOR : DEFAULTCOLOR;
         (*drawname)->scale = 0.75;
       (*drawname)->string->data.font = fval;
       strptr = makesegment(&((*drawname)->string), NULL);
       strptr->type = TEXT_STRING;
         strptr->data.string = strdup(libobj->name);
         (*drawname)->justify = TOP | NOTBOTTOM | NOTLEFT;

         if (width > 170)
            (*drawname)->position.x = xpos + (width >> 1);
         else
            (*drawname)->position.x = xpos + 85;

         if (height > 170)
            (*drawname)->position.y = drawinst->position.y + lly - 10;
         else
            (*drawname)->position.y = ypos - 180;
      }
      xpos = nxpos;
   }

   /* Compute the bounding box of the library page */
   calcbbox(xobjs.libtop[mode]);
   centerview(xobjs.libtop[mode]);

   /* Update the library directory */
   updatepagelib(LIBLIB, mode);
}

/*----------------------------------------------------------------*/
/* Find any dependencies on an object.                        */
/*   Return values:  0 = no dependency, 1 = dependency on page,     */
/*    2 = dependency in another library object.         */
/*   Object/Page with dependency (if any) returned in "compobjp". */
/*----------------------------------------------------------------*/

short finddepend(objinstptr libobj, objectptr **compobjp)
{
   genericptr *testobj;
   short page, i, j;
   objectptr *compobj;
  
   for (i = 0; i < xobjs.numlibs; i++) {
      for (j = 0; j < xobjs.userlibs[i].number; j++) {
       compobj = xobjs.userlibs[i].library + j;
         *compobjp = compobj;
                 
         for (testobj = (*compobj)->plist; testobj < (*compobj)->plist
                + (*compobj)->parts; testobj++) {
          if ((*testobj)->type == OBJECT) {
             if (TOOBJINST(testobj)->thisobject == libobj->thisobject) return 2;
          }
       }
      }
   }

   /* also look in the xobjs.pagelist */

   for (page = 0; page < xobjs.pages; page++) {
      if (xobjs.pagelist[page]->pageinst == NULL) continue;
      compobj = &(xobjs.pagelist[page]->pageinst->thisobject);
      *compobjp = compobj;
      for (testobj = (*compobj)->plist; testobj < (*compobj)->plist
             + (*compobj)->parts; testobj++) {
       if ((*testobj)->type == OBJECT) {
          if (TOOBJINST(testobj)->thisobject == libobj->thisobject) return 1;
       }
      }
   }
   return 0;
}

/*--------------------------------------------------------------*/
/* Virtual copy:  Make a separate copy of an object on the same   */
/* library page as the original, representing an instance of      */
/* the object with different parameters.  The object must have    */
/* parameters for this to make sense, so check for parameters     */
/* before allowing the virtual copy.                        */
/*--------------------------------------------------------------*/

void catvirtualcopy()
{
   short i, *newselect;
   objinstptr libobj, libinst;

   if (areastruct.selects == 0) return;
   else if ((i = is_library(topobject)) < 0) return;

   /* Check for existance of parameters in the object for each */
   /* selected instance */

   for (newselect = areastruct.selectlist; newselect < areastruct.selectlist
        + areastruct.selects; newselect++) {
      libobj = SELTOOBJINST(newselect);

      if (libobj->thisobject->num_params == 0) {
       Wprintf("Virtual copy allowed only on objects with paramters.");
       /* (because currently there's no way to adjust rotation/scale    */
       /* of a non-parameterized instance from inside the library,      */
       /* even though it can be specified in the library itself)  */
      }
      else {            /* All's clear to make a virtual copy */
       libinst = addtoinstlist(i, libobj->thisobject, TRUE);
       instcopy(libinst, libobj);
      }
   }

   clearselects();
   composelib(LIBRARY + i);
   drawarea(NULL, NULL, NULL);
}

/*----------------------------------------------------------------*/
/* "Hide" an object (must have a dependency or else it disappears)*/
/*----------------------------------------------------------------*/

void cathide()
{
   int i;
   short *newselect;
   objectptr *compobj;
   objinstptr libobj;

   if (areastruct.selects == 0) return;

   /* Can only hide objects which are instances in other objects; */
   /* Otherwise, object would be "lost".                */

   for (newselect = areastruct.selectlist; newselect < areastruct.selectlist
        + areastruct.selects; newselect++) {
      libobj = SELTOOBJINST(newselect);

      if (finddepend(libobj, &compobj) == 0) {
       sprintf(_STR, "Cannot hide: no dependencies");
       Wprintf(_STR);
      }
      else {            /* All's clear to hide. */
       libobj->thisobject->hidden = True;
      }
   }

   clearselects();

   if ((i = is_library(topobject)) >= 0) composelib(LIBRARY + i);

   drawarea(NULL, NULL, NULL);
}

/*----------------------------------------------------------------*/
/* Delete an object from the library if there are no dependencies */
/*----------------------------------------------------------------*/

void catdelete()
{
   short *newselect, *libpobjs;
   int i;
   genericptr *testobj, *tobj;
   objinstptr libobj;
   liblistptr ilist, llist;
   objectptr *libpage, *compobj, *tlib, *slib;

   if (areastruct.selects == 0) return;

   if ((i = is_library(topobject)) >= 0) {
      libpage = xobjs.userlibs[i].library;
      libpobjs = &xobjs.userlibs[i].number;
   }

   for (newselect = areastruct.selectlist; newselect < areastruct.selectlist
        + areastruct.selects; newselect++) {
      libobj = SELTOOBJINST(newselect);

      /* If this is just a "virtual copy", simply remove it from the list */

      llist = NULL;
      for (ilist = xobjs.userlibs[i].instlist; ilist != NULL;
            llist = ilist, ilist = ilist->next) {
       if ((ilist->thisinst == libobj) && (ilist->virtual == TRUE)) {
          if (llist == NULL)
             xobjs.userlibs[i].instlist = ilist->next;
          else
             llist->next = ilist->next;
          break;
       }
      }
      if (ilist != NULL) {
         free(ilist);
       continue;
      }

      /* Cannot delete an object if another object uses an instance of it, */
      /* or if the object is used on a page.                         */

      if (finddepend(libobj, &compobj)) {
       sprintf(_STR, "Cannot delete: dependency in \"%s\"", (*compobj)->name);
       Wprintf(_STR);
      }
      else {            /* All's clear to delete.                    */

         /* First remove any instances of this object in the delete buffer */

         for (compobj = xobjs.delbuffer.library; compobj != xobjs.delbuffer.library +
                xobjs.delbuffer.number; compobj++) {
          for (testobj = (*compobj)->plist; testobj < (*compobj)->plist
                + (*compobj)->parts; testobj++) {
             if ((*testobj)->type == OBJECT) {
                if (TOOBJINST(testobj)->thisobject == libobj->thisobject) {
                 free(TOOBJINST(testobj));
                 for (tobj = testobj; tobj < (*compobj)->plist
                     + (*compobj)->parts - 1; tobj++) {
                    (*tobj) = (*(tobj + 1));
                 }
                 (*compobj)->parts--;
              }
             }
          }
         }

       for (tlib = libpage; tlib < libpage + *libpobjs; tlib++)
          if ((*tlib) == libobj->thisobject) {
             for (slib = tlib; slib < libpage + *libpobjs - 1; slib++)
              (*slib) = (*(slib + 1));
             (*libpobjs)--;
             break;
          }
       
       /* Next, remove all instances of the object on the library page. */
        
         llist = NULL;
         for (ilist = xobjs.userlibs[i].instlist; ilist != NULL;
            llist = ilist, ilist = ilist->next) {
          if (ilist->thisinst->thisobject == libobj->thisobject) {
             if (llist == NULL) {
                xobjs.userlibs[i].instlist = ilist->next;
                free(ilist);
                if (!(ilist = xobjs.userlibs[i].instlist)) break;
             }
             else {
                llist->next = ilist->next;
                free(ilist);
                if (!(ilist = llist)) break;
             }
          }
       }

       /* Finally, delete the object (permanent---no undoing this!) */
       reset(libobj->thisobject, DESTROY);
      }
   }

   clearselects();

   if ((i = is_library(topobject)) >= 0) composelib(LIBRARY + i);

   drawarea(NULL, NULL, NULL);
}

/*------------------------------------------------------*/
/* Linked list rearrangement routines                 */
/*------------------------------------------------------*/

void linkedlistswap(liblistptr *spec, int o1, int o2)
{
   liblistptr s1, s1m, s2, s2m, stmp;
   int j;

   if (o1 == o2) return;

   s1m = NULL;
   s1 = *spec;
   for (j = 0; j < o1; j++) {
      s1m = s1;
      s1 = s1->next;
   }

   s2m = NULL;
   s2 = *spec;
   for (j = 0; j < o2; j++) {
      s2m = s2;
      s2 = s2->next;
   }

   if (s2m)
      s2m->next = s1;
   else
      *spec = s1;

   if (s1m)
      s1m->next = s2;
   else
      *spec = s2;

   stmp = s1->next;
   s1->next = s2->next;
   s2->next = stmp;
}

/*------------------------------------------------------*/

void linkedlistinsertafter(liblistptr *spec, int o1, int o2)
{
   liblistptr s1, s1m, s2, s2m, stmp;
   int j;

   if ((o1 == o2) || (o1 == (o2 + 1))) return;

   s1m = NULL;
   s1 = *spec;
   for (j = 0; j < o1; j++) {
      s1m = s1;
      s1 = s1->next;
   }

   s2 = *spec;
   for (j = 0; j < o2; j++)
      s2 = s2->next;

   if (s1m)
      s1m->next = s1->next;
   else
      *spec = s1->next;

   if (o2 == -1) {   /* move s1 to front */
      s1->next = *spec;
      *spec = s1;
   }
   else {
      s1->next = s2->next;
      s2->next = s1;
   }
}

/*------------------------------------------------------*/
/* Rearrange objects in the library             */
/*------------------------------------------------------*/

void catmove(XKeyEvent *event)
{
   int i, j, s1, s2, ocentx, ocenty, rangex, rangey;
   liblistptr spec;
   objinstptr exchobj, lobj;

   /* make catmove() a wrapper for pagecatmove() */

   if ((i = is_library(topobject)) < 0) {
      pagecatmove(event);
      return;
   }

   if (areastruct.selects == 0) return;
   else if (areastruct.selects > 2) {
      Wprintf("Select maximum of two objects.");
      return;
   }

   exchobj = SELTOOBJINST(areastruct.selectlist);
   s1 = -1;
   for (j = 0, spec = xobjs.userlibs[i].instlist; spec != NULL;
            spec = spec->next, j++) {
      if (spec->thisinst == exchobj) {
       s1 = j;
       break;
      }
   }

   /* If two objects are selected, then exchange their order */

   if (areastruct.selects == 2) {
      lobj = SELTOOBJINST(areastruct.selectlist + 1);

      s2 = -1;
      for (j = 0, spec = xobjs.userlibs[i].instlist; spec != NULL;
            spec = spec->next, j++) {
       if (spec->thisinst == lobj) {
          s2 = j;
          break;
       }
      }
      /* Exchange numbers s1 and s2 */
      linkedlistswap(&(xobjs.userlibs[i].instlist), s1, s2);
   }
   else {   /* one object selected; find place to put from cursor position */

      window_to_user(event->x, event->y, &areastruct.save);

      s2 = -1;
      for (j = 0, spec = xobjs.userlibs[i].instlist; spec != NULL;
            spec = spec->next, j++) {
       lobj = spec->thisinst;

       ocentx = lobj->position.x + lobj->bbox.lowerleft.x
              + (lobj->bbox.width >> 1);
       ocenty = lobj->position.y + lobj->bbox.lowerleft.y
              + (lobj->bbox.height >> 1);
       rangey = (lobj->bbox.height > 200) ? 
              (lobj->bbox.height >> 1) : 100;

       if ((areastruct.save.y < ocenty + rangey) && (areastruct.save.y 
               > ocenty - rangey)) {
          s2 = j - 1;
          if (areastruct.save.x < ocentx) break;
          else s2 = j;
       }
      }
      if ((s2 == -1) && (spec == NULL)) {
       if (areastruct.save.y <
            xobjs.libtop[i + LIBRARY]->thisobject->bbox.lowerleft.y)
          s2 = j - 1;
       else if (areastruct.save.y <=
            xobjs.libtop[i + LIBRARY]->thisobject->bbox.lowerleft.y +
            xobjs.libtop[i + LIBRARY]->thisobject->bbox.height) {
          objectdeselect();   
          Wprintf("Could not find appropriate place to insert object");
          return;
       }
      }

      /* Move number s1, insert it after s2 */
      linkedlistinsertafter(&(xobjs.userlibs[i].instlist), s1, s2);
   }

   objectdeselect();
   if ((i = is_library(topobject)) >= 0) composelib(LIBRARY + i);

   drawarea(NULL, NULL, NULL);
}

/*--------------------------------------------------------*/
/* Make a duplicate of an object, put in the User Library */
/*--------------------------------------------------------*/

void copycat()
{
   short *newselect;
   objectptr *newobj, *curlib, oldobj;
   objinstptr libobj;
   int i, libnum = USERLIB - LIBRARY ;

   for (newselect = areastruct.selectlist; newselect < areastruct.selectlist
        + areastruct.selects; newselect++) {

      libobj = SELTOOBJINST(newselect);
      oldobj = libobj->thisobject;

      /* generate new entry in user library */

      curlib = (objectptr *) realloc(xobjs.userlibs[libnum].library,
       (xobjs.userlibs[libnum].number + 1) * sizeof(objectptr));
      xobjs.userlibs[libnum].library = curlib;
      newobj = xobjs.userlibs[libnum].library + xobjs.userlibs[libnum].number;
      *newobj = (objectptr) malloc(sizeof(object));
      xobjs.userlibs[libnum].number++;

      /* give the new object a unique name */

      sprintf((*newobj)->name, "_%s", oldobj->name);
      checkname(*newobj);

      /* copy other object properties */

      (*newobj)->bbox.width = oldobj->bbox.width;
      (*newobj)->bbox.height = oldobj->bbox.height;
      (*newobj)->bbox.lowerleft.x = oldobj->bbox.lowerleft.x;
      (*newobj)->bbox.lowerleft.y = oldobj->bbox.lowerleft.y;
      (*newobj)->pcorner.x = oldobj->pcorner.x;
      (*newobj)->pcorner.y = oldobj->pcorner.y;
      (*newobj)->viewscale = oldobj->viewscale;
#ifdef SCHEMA
      /* don't attach the same schematic. . . */
      (*newobj)->symschem = NULL;

      /* Copy the parameter structure */
      (*newobj)->num_params = oldobj->num_params;
      (*newobj)->params = (oparamptr *)malloc(oldobj->num_params *
            sizeof(oparamptr));
      for (i = 0; i < oldobj->num_params; i++) {
       (*newobj)->params[i] = (oparamptr) malloc(sizeof(oparam));
       (*newobj)->params[i]->type = oldobj->params[i]->type;
       switch (oldobj->params[i]->type) {
          case XC_INT:
             (*newobj)->params[i]->parameter.ivalue =
                  oldobj->params[i]->parameter.ivalue;
             break;
          case XC_FLOAT:
             (*newobj)->params[i]->parameter.fvalue =
                  oldobj->params[i]->parameter.fvalue;
             break;
          case XC_STRING:
             (*newobj)->params[i]->parameter.string =
                  stringcopy(oldobj->params[i]->parameter.string);
             break;
       }
      }

      (*newobj)->schemtype = oldobj->schemtype;
      (*newobj)->netlist = NULL;
      (*newobj)->portlist = NULL;
      (*newobj)->calllist = NULL;
      (*newobj)->valid = False;
      (*newobj)->traversed = False;
      (*newobj)->devname = NULL;
#endif
      (*newobj)->hidden = False;

      /* copy over all the elements of the original object */

      (*newobj)->parts = 0;
      (*newobj)->plist = (genericptr *)malloc(sizeof(genericptr));

      for (i = 0; i < oldobj->parts; i++) {
       switch((*(oldobj->plist + i))->type) {
          case(PATH): {
             register pathptr *npath, cpath = TOPATH(oldobj->plist + i);
             register genericptr *gpart;
             NEW_PATH(npath, (*newobj));
             (*npath)->style = cpath->style;
             (*npath)->width = cpath->width;
             (*npath)->parts = 0;
             (*npath)->plist = (genericptr *)malloc(cpath->parts *
                  sizeof(genericptr));
               (*npath)->num_params = 0;
               (*npath)->passed = NULL;
             for (gpart = cpath->plist; gpart < cpath->plist + cpath->parts;
                  gpart++) {
              switch((*gpart)->type){
                 case ARC: {
                  arcptr copyarc = TOARC(gpart);
                  arcptr *newarc;
                  NEW_ARC(newarc, (*npath));
                  arccopy(*newarc, copyarc);
                  (*npath)->parts++;
                  } break;
                 case POLYGON: {
                        polyptr copypoly = TOPOLY(gpart);
                        polyptr *newpoly;
                        NEW_POLY(newpoly, (*npath));
                        polycopy(*newpoly, copypoly);
                        (*npath)->parts++;
                        } break;
                     case SPLINE: {
                        splineptr copyspline = TOSPLINE(gpart);
                        splineptr *newspline;
                        NEW_SPLINE(newspline, (*npath));
                        splinecopy(*newspline, copyspline);
                        (*npath)->parts++;
                        } break;
              }
             }    
             } break;
          case(ARC): {
             register arcptr *narc, carc = TOARC(oldobj->plist + i);

             NEW_ARC(narc, (*newobj));
             arccopy(*narc, carc);
               } break;
      
          case(POLYGON): {
             register polyptr *npoly, cpoly;

             cpoly = TOPOLY(oldobj->plist + i);
             NEW_POLY(npoly, (*newobj));
             polycopy(*npoly, cpoly);
               } break;

          case(SPLINE): {
             register splineptr *nspl, cspl;

             cspl = TOSPLINE(oldobj->plist + i);
             NEW_SPLINE(nspl, (*newobj));
             splinecopy(*nspl, cspl);
                   } break;

          case(LABEL): {
             register labelptr *nlabel, clabel;

             clabel = TOLABEL(oldobj->plist + i);
             NEW_LABEL(nlabel, (*newobj));
             (*nlabel)->rotation = clabel->rotation;
             (*nlabel)->color = clabel->color;
             (*nlabel)->position.x = clabel->position.x;
             (*nlabel)->position.y = clabel->position.y;
             (*nlabel)->scale = clabel->scale;
             (*nlabel)->justify = clabel->justify;
             (*nlabel)->string = stringcopy(clabel->string);
             (*nlabel)->num_params = 0;
             (*nlabel)->passed = NULL;
#ifdef SCHEMA
             (*nlabel)->pin = clabel->pin;
#endif
               } break;
      
          case(OBJECT): {
             register objinstptr *ninst, cinst;

             cinst = TOOBJINST(oldobj->plist + i);
             NEW_OBJINST(ninst, (*newobj));
             (*ninst)->rotation = cinst->rotation;
             (*ninst)->color = cinst->color;
             (*ninst)->position.x = cinst->position.x;
             (*ninst)->position.y = cinst->position.y;
             (*ninst)->scale = cinst->scale;
             (*ninst)->thisobject = cinst->thisobject;
             (*ninst)->num_params = 0;
             (*ninst)->passed = NULL;

             /* Library instance's parameter list is always default (null) */
             (*ninst)->params = NULL;
             (*ninst)->bbox.lowerleft.x = cinst->thisobject->bbox.lowerleft.x;
             (*ninst)->bbox.lowerleft.y = cinst->thisobject->bbox.lowerleft.y;
             (*ninst)->bbox.width = cinst->thisobject->bbox.width;
             (*ninst)->bbox.height = cinst->thisobject->bbox.height;

#ifdef SCHEMA
             (*ninst)->schembbox = NULL;

             /* here---deal with attached schematics */
#endif
               } break;
         }
       (*newobj)->parts++;
      }
   }

   /* make instance for library and measure its bounding box */
   addtoinstlist(USERLIB - LIBRARY, *newobj, FALSE);

   composelib(USERLIB);
   objectdeselect();

   if (areastruct.topinstance == xobjs.libtop[USERLIB])
      drawarea(NULL, NULL, NULL);
   else startcatalog(NULL, (pointertype)USERLIB, NULL);
}

/*--------------------------------------------------------*/
/* ButtonPress handler during normal catalog viewing mode */
/*--------------------------------------------------------*/

void catbutton(u_int mode, XButtonEvent *event)
{
   short *newselect;
   objinstptr *newobject, *libobj;
   objectptr libpage = topobject;
   short ocentx, ocenty, rangex, rangey, xdiff, ydiff, flag = 0;
   XPoint oldpos;

   /* Make catbutton() a wrapper for pagecatbutton() */

   if (is_library(topobject) < 0) {
      pagecatbutton(event);
      return;
   }

   if (event->button == Button1 || event->button == Button2) {

      window_to_user(event->x, event->y, &areastruct.save);
      
      for (libobj = (objinstptr *)topobject->plist; libobj <
            (objinstptr *)topobject->plist + topobject->parts; libobj++) {
       if ((*libobj)->type == OBJECT) {

          ocentx = (*libobj)->position.x + (*libobj)->bbox.lowerleft.x
              + ((*libobj)->bbox.width >> 1);
          ocenty = (*libobj)->position.y + (*libobj)->bbox.lowerleft.y
              + ((*libobj)->bbox.height >> 1);

          rangex = ((*libobj)->bbox.width > 200) ? 
              ((*libobj)->bbox.width >> 1) : 100;
          rangey = ((*libobj)->bbox.height > 200) ? 
              ((*libobj)->bbox.height >> 1) : 100;

          if (areastruct.save.x > ocentx - rangex && areastruct.save.x <
               ocentx + rangex && areastruct.save.y < ocenty + rangey
               && areastruct.save.y > ocenty - rangey) {

             /* setup to move object around and draw the selected object */

#ifdef SCHEMA
             if (eventmode == ASSOC_MODE) {

                /* revert to old page */
                areastruct.topinstance = (areastruct.stack == NULL) ?
                     xobjs.pagelist[areastruct.page]->pageinst
                     : areastruct.stack->thisinst;
              /* associate the new object */
              schemassoc(topobject, (*libobj)->thisobject); 
                setpage(TRUE);
              catreturn();
              eventmode = PRESS_MODE;
             }
             else
#endif
               if (event->button == Button1) {

                /* revert to old page */

                areastruct.topinstance = (areastruct.stack == NULL) ?
                     xobjs.pagelist[areastruct.page]->pageinst
                     : areastruct.stack->thisinst;

                /* retrieve drawing window state and set position of object to
                   be correct in reference to that window */

                snap(event->x, event->y, &oldpos);

                setpage(FALSE);
          
                snap(event->x, event->y, &areastruct.save);
                xdiff = areastruct.save.x - oldpos.x;
                ydiff = areastruct.save.y - oldpos.y;

                  /* collect all of the selected items */

                for (newselect = areastruct.selectlist; newselect <
                 areastruct.selectlist + areastruct.selects; newselect++) {
                 NEW_OBJINST(newobject, topobject);
                 topobject->parts++;
                 instcopy(*newobject, TOOBJINST(libpage->plist + *newselect));
                 /* color should be recast as current color */
                 (*newobject)->color = areastruct.color;
                 /* position modified by (xdiff, ydiff) */
                 (*newobject)->position.x += xdiff;
                 (*newobject)->position.y += ydiff;

                   u2u_snap(&((*newobject)->position));
                 *newselect = (short)(newobject - (objinstptr *)topobject->plist);
                 if ((*newobject)->thisobject == (*libobj)->thisobject)
                    flag = 1;
                }

                  /* add final object to the list of object instances */

                if (!flag) {
                 NEW_OBJINST(newobject, topobject);
                     topobject->parts++;
                 instcopy(*newobject, *libobj);
                 (*newobject)->color = areastruct.color;
                     (*newobject)->position.x = areastruct.save.x;
                   (*newobject)->position.y = areastruct.save.y; 

                   /* add this object to the list of selected items */

                   newselect = allocselect();
                     *newselect = (short)(newobject - (objinstptr *)topobject->plist);

                }
                if ((void *)mode == (void *)(1)) {

                 /* key "c" pressed for "copy" */

                     XDefineCursor(dpy, areastruct.areawin, COPYCURSOR);
                     eventmode = COPY2_MODE;
#ifndef TCL_WRAPPER
                     xcAddEventHandler(areastruct.area, PointerMotionMask, False,
                    (xcEventHandler)drag, NULL);
#endif
                }
                else {
                     eventmode = PRESS_MODE;
                }
#ifdef TCL_WRAPPER
                  Tk_CreateEventHandler(areastruct.area, PointerMotionMask,
                  (Tk_EventProc *)xctk_drag, NULL);
#endif
                  catreturn();
               }

               /* If button2 was pressed, select and stay in the catalog. */
             /* Could just do "objectselect" here, but would not cover   */
             /* the entire area in the directory surrounding the object. */

             else {
              short newinst = (short)(libobj - (objinstptr *)topobject->plist);
              /* (ignore this object if it is already in the list of selects) */
              for (newselect = areastruct.selectlist; newselect <
                  areastruct.selectlist + areastruct.selects; newselect++)
                 if (*newselect == newinst) break;
              if (newselect == areastruct.selectlist + areastruct.selects) {
                 newselect = allocselect();
                 *newselect = newinst;
                 XcSetFunction(GXcopy);
                 XcSetForeground(SELECTCOLOR);
                 UDrawObject(*libobj, SINGLE, SELECTCOLOR, NULL);
              }
             }
             break;
          }
       }
      }
   }
   /* If button3 was pressed, return without a selection. */

   else {
      eventmode = NORMAL_MODE;
      catreturn();
   }
}

/*------------------------------*/
/* Switch to the next catalog */
/*------------------------------*/

void changecat()
{
   int i, j;

   if ((i = is_library(topobject)) < 0) {
      if (areastruct.lastlibrary >= xobjs.numlibs) areastruct.lastlibrary = 0;
      j = areastruct.lastlibrary;
      eventmode = CATALOG_MODE;
   }
   else {
      j = (i + 1) % xobjs.numlibs;
      if (j == i) {
       Wprintf("This is the only library.");
       return;
      }
      areastruct.lastlibrary = j;
   }
   startcatalog(NULL, (pointertype)(j + LIBRARY), NULL);
}

/*--------------------------------------*/
/* Begin catalog viewing mode       */
/*--------------------------------------*/

void startcatalog(xcWidget w, pointertype libmod, caddr_t nulldata)
{
   if (xobjs.libtop == NULL) return;      /* No libraries defined */

   if ((xobjs.libtop[libmod]->thisobject == NULL) ||
            (areastruct.topinstance == xobjs.libtop[libmod])) return;

   if (libmod == FONTLIB) {
      XDefineCursor (dpy, areastruct.areawin, CROSS);
      if (eventmode == TEXT2_MODE)
         eventmode = FONTCAT_MODE;
      else
         eventmode = FONTCAT2_MODE;
   }
#ifdef SCHEMA
   else if (eventmode == ASSOC_MODE) {
      XDefineCursor (dpy, areastruct.areawin, CROSS);
   }
#endif
   else if (libmod == PAGELIB || libmod == LIBLIB) {
      XDefineCursor (dpy, areastruct.areawin, CROSS);
      eventmode = CATALOG_MODE;
   }
   else
      eventmode = CATALOG_MODE;

   /* Push the current page onto the push stack, unless     we're going */
   /* to a library from the library directory or vice versa, or       */
   /* library to library.                                 */


   if (!(((is_library(topobject) >= 0)
            || (areastruct.topinstance == xobjs.libtop[LIBLIB])
            || (areastruct.topinstance == xobjs.libtop[PAGELIB]))
            && libmod >= PAGELIB)) {
      push_stack(&areastruct.stack, areastruct.topinstance);
   }

   /* set library as new object */

   areastruct.topinstance = xobjs.libtop[libmod];
   setpage(TRUE);

   /* draw the new screen */

   refresh(NULL, NULL, NULL);
}

/*-------------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index