/*----------------------------------------------------------------------*/
/* menucalls.c --- callback routines from the menu buttons		*/
/*		   and associated routines				*/
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	*/
/*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>

#ifndef XC_WIN32
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xutil.h>
#endif
#ifdef TCL_WRAPPER 
#include <tk.h>
#else
#ifndef XC_WIN32
#include "Xw/Xw.h"
#include "Xw/MenuBtn.h"
#include "Xw/PopupMgr.h"
#endif
#endif

#if defined(XC_WIN32) && defined(TCL_WRAPPER)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif

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

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

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

/*----------------------------------------------------------------------*/
/* External Variable definitions					*/
/*----------------------------------------------------------------------*/

extern char	 _STR2[250];
extern char	 _STR[150];          /* Generic multipurpose string */
extern xcWidget	 top;
extern Display   *dpy;
extern short	 popups;	     /* total number of popup windows */
extern xcWidget    menuwidgets[];
extern xcWidget	 toolbar;
extern Globaldata xobjs;
extern Clientdata areastruct;
extern int	  *appcolors;
extern int	  number_colors;
extern colorindex *colorlist;
extern ApplicationData appdata;
extern Cursor	  appcursors[NUM_CURSORS];
extern fontinfo *fonts;
extern short fontcount;
extern short eventmode, textpos, textend;

#ifdef TCL_WRAPPER
extern Tcl_Interp *xcinterp;
#endif

/*----------------------------------------------------------------------*/
/* The rest of the local includes depend on the prototype declarations	*/
/* and some of the external global variable declarations.		*/
/*----------------------------------------------------------------------*/
#ifndef TCL_WRAPPER
#include "menus.h"
#include "menudep.h"
#endif

/*----------------------------------------------------------------------*/
/* Local Variable definitions						*/
/*----------------------------------------------------------------------*/

u_short *fontnumbers;
u_char nfontnumbers;

/*--------------------------------------------------------------*/
/* Enable or Disable the toolbar				*/
/*--------------------------------------------------------------*/

#ifdef HAVE_XPM
void dotoolbar(xcWidget w, caddr_t clientdata, caddr_t calldata)   
{
#ifndef TCL_WRAPPER
   Arg wargs[1];
#endif

#ifndef TCL_WRAPPER
   if (areastruct.toolbar_on) {
      areastruct.toolbar_on = False;
      XtUnmanageChild(toolbar);
      XtSetArg(wargs[0], XtNlabel, "Enable Toolbar");
      XtSetValues(OptionsDisableToolbarButton, wargs, 1);
      XtRemoveCallback(areastruct.area, XtNresize, (XtCallbackProc)resizetoolbar, NULL);
   }
   else {
      areastruct.toolbar_on = True;
      XtManageChild(toolbar);
      XtSetArg(wargs[0], XtNlabel, "Disable Toolbar");
      XtSetValues(OptionsDisableToolbarButton, wargs, 1);
      XtAddCallback(areastruct.area, XtNresize, (XtCallbackProc)resizetoolbar, NULL);
   }
#endif
}  

#ifndef TCL_WRAPPER

/*--------------------------------------------------------------*/
/* Overwrite the toolbar pixmap for color or stipple entries	*/
/*--------------------------------------------------------------*/

#ifndef XC_WIN32
void overdrawpixmap(xcWidget button)
{
   xcWidget pbutton;
   Arg args[3];
   int ltype, color;
   Pixmap stippix;

   if (button == NULL) return;

   XtSetArg(args[0], XtNlabelType, &ltype);
   XtGetValues(button, args, 1);

   if (ltype != XwRECT && button != ColorInheritColorButton) return;

   XtSetArg(args[0], XtNrectColor, &color);
   XtSetArg(args[1], XtNrectStipple, &stippix);
   XtGetValues(button, args, 2);

   if (stippix == (Pixmap)NULL && button != FillBlackButton)
      pbutton = ColorsToolButton;
   else
      pbutton = FillsToolButton;

   if (button == ColorInheritColorButton) {
      XtSetArg(args[0], XtNlabelType, XwIMAGE);
      XtSetValues(pbutton, args, 1);
   }
   else if (button == FillBlackButton) {
      XtSetArg(args[0], XtNlabelType, XwRECT);
      XtSetArg(args[1], XtNrectColor, color);
      XtSetArg(args[2], XtNrectStipple, (Pixmap)NULL);
      XtSetValues(pbutton, args, 3);
   }
   else if (button != FillOpaqueButton) {
      XtSetArg(args[0], XtNlabelType, XwRECT);
      if (stippix == (Pixmap)NULL)
         XtSetArg(args[1], XtNrectColor, color);
      else
         XtSetArg(args[1], XtNrectStipple, stippix);

      XtSetValues(pbutton, args, 2);
   }
}
#endif
#else
#define overdrawpixmap(a)
#endif  /* TCL */
#else
#define overdrawpixmap(a)
#endif	/* XPM */

/*--------------------------------------------------------------*/
/* Generic routine for use by all other data handling routines 	*/
/*--------------------------------------------------------------*/

void getgeneric(buttonsave *saveptr, xcWidget button, void (*getfunction)(),
	void *dataptr)
{
#ifndef TCL_WRAPPER
   Arg  wargs[1];
#endif

   saveptr->button = button;
   saveptr->buttoncall = getfunction;
   saveptr->dataptr = dataptr;

#ifndef TCL_WRAPPER
   if (button != NULL) {
      XtSetArg(wargs[0], XtNforeground, &saveptr->foreground);
      XtGetValues(button, wargs, 1);
      XtSetArg(wargs[0], XtNforeground, OFFBUTTONCOLOR);
      XtSetValues(button, wargs, 1);
      XtRemoveAllCallbacks(button, XtNselect);
   }
#endif
}


/*----------------------------------------------------------------*/
/* setgrid, getgridspace are for grid and snap spacing sizes;	  */
/* include routines to parse fractions				  */
/*----------------------------------------------------------------*/

void setgrid(xcWidget w, float *dataptr)
{
   float oldvalue = *dataptr;
   float oscale, iscale = (float)xobjs.pagelist[areastruct.page]->drawingscale.y /
        (float)xobjs.pagelist[areastruct.page]->drawingscale.x;
   float fval;

   /* For now, assume that the value is in the current display style. */
   /* Might be nice in the future to make it accept any input. . .    */

   switch (xobjs.pagelist[areastruct.page]->coordstyle) {
      case INTERNAL:
	 if (sscanf(_STR2, "%f", &fval) == 0) {
	    *dataptr = oldvalue;
	    Wprintf("Illegal value");
	 }
	 else *dataptr = fval / iscale;
	 break;
      case CM:
         oscale = xobjs.pagelist[areastruct.page]->outscale * CMSCALE;
	 if (sscanf(_STR2, "%f", &fval) == 0) {
	    *dataptr = oldvalue;
	    Wprintf("Illegal value");
	 }
	 else *dataptr = fval * IN_CM_CONVERT / (iscale * oscale);
	 break;
      case DEC_INCH: case FRAC_INCH: {
	 short parts;
	 char *sptr;
	 int f2, f3;

         oscale = xobjs.pagelist[areastruct.page]->outscale * INCHSCALE;
	 for (sptr = _STR2; *sptr != '\0'; sptr++)
	    if (*sptr == '/') *sptr = ' ';
	 parts = sscanf(_STR2, "%f %d %d", &fval, &f2, &f3);
	 if ((parts == 0) || (parts != 1 && (fval != (float)((int)fval)))) {
	    *dataptr = oldvalue;
	    Wprintf("Illegal value");
	    break;
	 }
	 if (parts == 2) fval /= (float)f2;
	 else if (parts == 3) fval += ((float)f2 / (float)f3);
	 *dataptr = fval * 72.0 / (iscale * oscale);
	 } break;
   }
   if (oldvalue != *dataptr) drawarea(NULL, NULL, NULL);
}

/*----------------------------------------------------------------*/
/* Write a measurement value into string "buffer" dependant on    */
/* the current default units of measure (centimeters or inches).  */
/*----------------------------------------------------------------*/

void measurestr(float value, char *buffer)
{  
   float oscale, iscale;
   iscale = (float)(xobjs.pagelist[areastruct.page]->drawingscale.y) /
        (float)(xobjs.pagelist[areastruct.page]->drawingscale.x);

   switch (xobjs.pagelist[areastruct.page]->coordstyle) {
      case INTERNAL:
	 sprintf(buffer, "%5.3f", value * iscale);
         break; 
      case CM:
         oscale = xobjs.pagelist[areastruct.page]->outscale * CMSCALE;
         sprintf(buffer, "%5.3f cm", value * iscale * oscale / IN_CM_CONVERT);
         break; 
      case DEC_INCH:
         oscale = xobjs.pagelist[areastruct.page]->outscale * INCHSCALE;
         sprintf(buffer, "%5.3f in", value * iscale * oscale / 72.0);
         break;
      case FRAC_INCH:
         oscale = xobjs.pagelist[areastruct.page]->outscale * INCHSCALE;
         fraccalc(((value * iscale * oscale) / 72.0), buffer);
         strcat(buffer, " in");
         break;
   }
}        

/*--------------------------------------------------------------*/
/* Generate popup dialog for snap space value input		*/
/*--------------------------------------------------------------*/

void getsnapspace(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   float *floatptr = &xobjs.pagelist[areastruct.page]->snapspace;

   getgeneric(savebutton, button, getsnapspace, (void *)floatptr);
   measurestr(*floatptr, buffer);
   popupprompt(button, "Enter value:", buffer, setgrid, savebutton, NULL);
}

/*--------------------------------------------------------------*/
/* Generate popup dialog for grid space value input		*/
/*--------------------------------------------------------------*/

void getgridspace(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   float *floatptr = &xobjs.pagelist[areastruct.page]->gridspace;

   getgeneric(savebutton, button, getgridspace, (void *)floatptr);
   measurestr(*floatptr, buffer);
   popupprompt(button, "Enter value:", buffer, setgrid, savebutton, NULL);
}

/*----------------------------------------------------------------*/
/* Generic routine for setting a floating-point value (through a  */
/* (float *) pointer passed as the second parameter)		  */
/*----------------------------------------------------------------*/

void setfloat(xcWidget w, float *dataptr)
{
   float oldvalue = *dataptr;
   int res = sscanf(_STR2, "%f", dataptr);

   if (res == 0 || *dataptr <= 0) {
      *dataptr = oldvalue;
      Wprintf("Illegal value");
   }
   if (oldvalue != *dataptr) drawarea(NULL, NULL, NULL);
}

/*----------------------------------------------------------------*/
/* set the global default line width.  The unit used internally   */
/* is twice the value passed through pointer "dataptr".		  */
/*----------------------------------------------------------------*/

void setwidth(xcWidget w, float *dataptr)
{
   float oldvalue = *dataptr;
   if (sscanf(_STR2, "%f", dataptr) == 0) {
      *dataptr = oldvalue;
      Wprintf("Illegal value");
      return;
   }
   (*dataptr) *= 2.0;
   if (oldvalue != *dataptr) drawarea(NULL, NULL, NULL);
}

/*----------------------------------------------------------------*/
/* Set text scale.						  */
/*----------------------------------------------------------------*/

void settsize(xcWidget w, labelptr settext)
{
   float tmpres;
   short *osel;
   labelptr nstext;
   stringpart *strptr, *nextptr;
   int res = sscanf(_STR2, "%f", &tmpres);

   if (res == 0 || tmpres <= 0) {  /* can't interpret value or bad value */
      Wprintf("Illegal value");
      return;
   }

   /* In edit mode, add font scale change. */

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      if (textpos > 0 || textpos < stringlength(settext->string, True, areastruct.topinstance)) {
	 undrawtext(settext);
	 strptr = findstringpart(textpos - 1, NULL, settext->string, areastruct.topinstance);
	 nextptr = findstringpart(textpos, NULL, settext->string, areastruct.topinstance);
	 if (strptr->type == FONT_SCALE)
	    strptr->data.scale = tmpres;
	 else if (nextptr && nextptr->type == FONT_SCALE)
	    nextptr->data.scale = tmpres;
	 else
	    labeltext(FONT_SCALE, (char *)&tmpres);
	 redrawtext(settext);
      }
      else if (stringlength(settext->string, True, areastruct.topinstance) > 0)
	 labeltext(FONT_SCALE, (char *)&tmpres);
      else (settext->scale = tmpres);
   }

   /* Change scale on all selected text objects */

   else if (areastruct.selects > 0) {
      for (osel = areastruct.selectlist; osel < areastruct.selectlist +
	     areastruct.selects; osel++) {
	 if (SELECTTYPE(osel) == LABEL) {
	    nstext = SELTOLABEL(osel);
	    undrawtext(nstext);
            nstext->scale = tmpres;
	    redrawtext(nstext);
	 }
      }
      unselect_all();
   }
}

#ifndef TCL_WRAPPER
/*--------------------------------------------------------------*/
/* Auto-set:  Enable automatic scaling				*/
/*--------------------------------------------------------------*/

void autoset(xcWidget w, xcWidgetList entertext, caddr_t nulldata)
{
   xobjs.pagelist[areastruct.page]->pmode |= 2;
   updatetext(w, entertext, nulldata);
}

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

void autostop(xcWidget w, caddr_t clientdata, caddr_t nulldata)
{
   xobjs.pagelist[areastruct.page]->pmode &= 1;
}
#endif

/*--------------------------------------------------------------*/
/* Auto-scale the drawing to fit the declared page size.	*/
/* (for now, with fixed 1" margins)				*/
/*								*/
/* If the page is declared encapsulated, then do nothing.	*/
/* If a frame box is on the page, then scale to fit the frame	*/
/* to the declared page size, not the whole object.		*/
/*--------------------------------------------------------------*/

void autoscale(int page)
{
   float newxscale, newyscale;
   float scalefudge = (xobjs.pagelist[page]->coordstyle
	== CM) ? CMSCALE : INCHSCALE;
   float margin = 144.0;
   int width, height;
   polyptr framebox;

   if (!(xobjs.pagelist[page]->pmode & 2)) return;
   else if (topobject->bbox.width == 0 || topobject->bbox.height == 0) {
      Wprintf("Cannot auto-fit empty page");
      return;
   }

   newxscale = (xobjs.pagelist[page]->pagesize.x - margin)
	/ scalefudge;
   newyscale = (xobjs.pagelist[page]->pagesize.y - margin)
	/ scalefudge;

   if ((framebox = checkforbbox(topobject)) != NULL) {
      int i, minx, miny, maxx, maxy;
      
      minx = maxx = framebox->points[0].x;
      miny = maxy = framebox->points[0].y;
      for (i = 1; i < framebox->number; i++) {
	 if (framebox->points[i].x < minx) minx = framebox->points[i].x;
	 else if (framebox->points[i].x > maxx) maxx = framebox->points[i].x;
	 if (framebox->points[i].y < miny) miny = framebox->points[i].y;
	 else if (framebox->points[i].y > maxy) maxy = framebox->points[i].y;
      }
      width = (maxx - minx);
      height = (maxy - miny);
   }
   else {

      width = toplevelwidth(areastruct.topinstance, NULL);
      height = toplevelheight(areastruct.topinstance, NULL);
   }

   if (xobjs.pagelist[page]->orient == 0) {	/* Portrait */
      newxscale /= width;
      newyscale /= height;
   }
   else {
      newxscale /= height;
      newyscale /= width;
   }
   xobjs.pagelist[page]->outscale = min(newxscale, newyscale);
}

/*--------------------------------------------------------------*/
/* Set the denomenator value of the drawing scale 		*/ 
/*--------------------------------------------------------------*/

void setscaley(xcWidget w, float *dataptr)
{
   float oldvalue = *dataptr;
   int res = sscanf(_STR2, "%f", dataptr);

   if (res == 0 || *dataptr <= 0 || topobject->bbox.height == 0) {
      *dataptr = oldvalue;
      Wprintf("Illegal value");
   }
   else {
      *dataptr = (*dataptr * 72) / topobject->bbox.height;
      *dataptr /= getpsscale(1.0, areastruct.page);
   }
}

/*----------------------------------------------------------------*/
/* Set the numerator value of the drawing scale			  */
/*----------------------------------------------------------------*/

void setscalex(xcWidget w, float *dataptr)
{
   float oldvalue = *dataptr;
   int res = sscanf(_STR2, "%f", dataptr);

   if (res == 0 || *dataptr <= 0 || topobject->bbox.width == 0) {
      *dataptr = oldvalue;
      Wprintf("Illegal value");
   }
   else {
      *dataptr = (*dataptr * 72) / topobject->bbox.width;
      *dataptr /= getpsscale(1.0, areastruct.page);
   }
}

/*----------------------------------------------------------------*/
/* Set the page orientation (either Landscape or Portrait)	  */
/*----------------------------------------------------------------*/

void setorient(xcWidget w, short *dataptr)
{
#ifndef TCL_WRAPPER
   Arg wargs[1];

   if (*dataptr == 0) {
      *dataptr = 90;
      XtSetArg(wargs[0], XtNlabel, "Landscape");
   }
   else {
      *dataptr = 0;
      XtSetArg(wargs[0], XtNlabel, "Portrait");
   }
   XtSetValues(w, wargs, 1);
#endif
}

/*----------------------------------------------------------------*/
/* Set the output mode to "Full Page" (unencapsulated PostScript) */
/* or "Embedded" (encapsulated PostScript)			  */
/*----------------------------------------------------------------*/

void setpmode(xcWidget w, short *dataptr)
{
#ifndef TCL_WRAPPER
   Arg wargs[1];
   xcWidget pwidg = XtParent(w);
   xcWidget autowidg = XtNameToWidget(pwidg, "Auto-fit");

   if (!(*dataptr & 1)) {
      *dataptr = 1;
      XtSetArg(wargs[0], XtNlabel, "Full Page");

      XtManageChild(XtNameToWidget(pwidg, "fpedit"));
      XtManageChild(XtNameToWidget(pwidg, "fpokay"));
      XtManageChild(autowidg);
   }
   else {
      *dataptr = 0;	/* This also turns off auto-fit */
      XtSetArg(wargs[0], XtNset, False);
      XtSetValues(autowidg, wargs, 1);
      XtSetArg(wargs[0], XtNlabel, "Embedded");

      XtUnmanageChild(XtNameToWidget(pwidg, "fpedit"));
      XtUnmanageChild(XtNameToWidget(pwidg, "fpokay"));
      XtUnmanageChild(autowidg);
   }
   XtSetValues(w, wargs, 1);
#endif
}

#ifdef TCL_WRAPPER

/*--------------------------------------------------------------*/
/* Parse a string for possible units of measure.  Convert to	*/ 
/* current units of measure, if necessary.  Return the value	*/
/* in current units, as a type float.				*/
/*--------------------------------------------------------------*/

float parseunits(char *strptr)
{
   short curtype;
   Boolean inchunits = True;
   float pv;
   char units[12];

   curtype = xobjs.pagelist[areastruct.page]->coordstyle;

   if (sscanf(strptr, "%f %11s", &pv, units) < 2)
      return pv;
   else {
      if (!strncmp(units, "cm", 2) || !strncmp(units, "centimeters", 11))
	 inchunits = False;
      switch(curtype) {
         case CM:
	    return ((inchunits) ? (pv * 2.54) : pv);
         default:
	    return ((inchunits) ? pv : (pv / 2.54));
      }
   }
}

#endif

/*--------------------------------------------------------------*/
/* Set the output page size, in the current unit of measure	*/
/*--------------------------------------------------------------*/

void setpagesize(xcWidget w, XPoint *dataptr)
{
   float px, py;
   char units[10], *expos, fpedit[75];
#ifndef TCL_WRAPPER
   Arg wargs[1];
#endif

   strcpy(units, "in");

   if (sscanf(_STR2, "%f %*c %f %9s", &px, &py, units) < 4) {
      if (sscanf(_STR2, "%f %*c %f", &px, &py) < 3) {
	 if ((expos = strchr(_STR2, 'x')) == NULL) {
            Wprintf("Illegal Form for page size.");
	    return;
	 }
	 else {
	    *expos = '\0';
	    if (sscanf(_STR2, "%f", &px) == 0 ||
		  sscanf(expos + 1, "%f %9s", &py, units) == 0) {
               Wprintf("Illegal Form for page size.");
	       return;
	    }
	 }
      }
   }

   /* Don't reduce page to less than the margins (1") or negative	*/
   /* scales result.							*/

   if ((px <= 2.0) || (py <= 2.0)) {
      Wprintf("Page size too small for margins.");
      return;
   }

   dataptr->x = (short)(px * 72.0);
   dataptr->y = (short)(py * 72.0);

   if (!strcmp(units, "cm")) {
      sprintf(fpedit, "%3.2f x %3.2f cm",
	  (float)xobjs.pagelist[areastruct.page]->pagesize.x
	  / 72.0, (float)xobjs.pagelist[areastruct.page]->pagesize.y / 72.0);
      dataptr->x /= 2.54;
      dataptr->y /= 2.54;
   }
   else {
      sprintf(fpedit, "%3.2f x %3.2f in",
	  (float)xobjs.pagelist[areastruct.page]->pagesize.x
	  / 72.0, (float)xobjs.pagelist[areastruct.page]->pagesize.y / 72.0);
   }

#ifndef TCL_WRAPPER
   XtSetArg(wargs[0], XtNstring, fpedit);
   XtSetValues(XtNameToWidget(XtParent(w), "fpedit"), wargs, 1);
#endif
}

/*--------------------------------------------------------------*/
/* Generate popup dialog to get a character kern value, which	*/
/* is two integer numbers in the range -128 to +127 (size char)	*/
/*--------------------------------------------------------------*/

void getkern(xcWidget button, caddr_t nulldata, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   int kx, ky;
   stringpart *strptr, *nextptr;

   strcpy(buffer, "0,0");

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      labelptr curlabel = TOLABEL(EDITPART);
      strptr = findstringpart(textpos - 1, NULL, curlabel->string, areastruct.topinstance);
      nextptr = findstringpart(textpos, NULL, curlabel->string, areastruct.topinstance);
      if (strptr->type == KERN) {
	 kx = strptr->data.kern[0];
	 ky = strptr->data.kern[1];
         sprintf(buffer, "%d,%d", kx, ky);
      }
      else if (nextptr && nextptr->type == KERN) {
	 strptr = nextptr;
	 kx = strptr->data.kern[0];
	 ky = strptr->data.kern[1];
         sprintf(buffer, "%d,%d", kx, ky);
      }
      else strptr = NULL;
   }

   getgeneric(savebutton, button, getkern, strptr);
   popupprompt(button, "Enter Kern X,Y:", buffer, setkern, savebutton, NULL);
}

/*--------------------------------------------------------------*/
/* Set a character kern value					*/ 
/*--------------------------------------------------------------*/

void setkern(xcWidget w, stringpart *kpart)
{
   char *sptr;
   short kd[2];

   kd[0] = kd[1] = 0;

   if ((sptr = strchr(_STR2, ',')) == NULL)
      Wprintf("Use notation X,Y");
   else {
      *sptr = '\0';
      sscanf(_STR2, "%hd", &kd[0]);
      sscanf(sptr + 1, "%hd", &kd[1]);
      if (kpart == NULL)
         labeltext(KERN, (char *)kd);
      else {
         labelptr curlabel = TOLABEL(EDITPART);
	 undrawtext(curlabel);
	 kpart->data.kern[0] = kd[0];
	 kpart->data.kern[1] = kd[1];
	 redrawtext(curlabel);
      }
   }
}

/*----------------------------------------------------------------*/
/* Generate popup dialog to get the drawing scale, specified as a */
/* whole-number ratio X:Y					  */
/*----------------------------------------------------------------*/

void getdscale(xcWidget button, caddr_t nulldata, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   XPoint *ptptr = &(xobjs.pagelist[areastruct.page]->drawingscale);

   getgeneric(savebutton, button, getdscale, ptptr);
   sprintf(buffer, "%d:%d", ptptr->x, ptptr->y);
   popupprompt(button, "Enter Scale:", buffer, setdscale, savebutton, NULL);
}

/*----------------------------------------------------------------*/
/* Set the drawing scale (specified as ratio X:Y)		  */
/*----------------------------------------------------------------*/

void setdscale(xcWidget w, XPoint *dataptr)
{
   char *sptr;

   if ((sptr = strchr(_STR2, ':')) == NULL)
      Wprintf("Use ratio X:Y");
   else {
      *sptr = '\0';
      sscanf(_STR2, "%hd", &(dataptr->x));
      sscanf(sptr + 1, "%hd", &(dataptr->y));
      sprintf(_STR2, "New scale is %hd:%hd", dataptr->x, dataptr->y);
      Wprintf(_STR2);
      W1printf(" ");
   }
}

/*--------------------------------------------------------------*/
/* Get the text size (global or selected, depending on mode	*/
/*--------------------------------------------------------------*/

labelptr gettextsize(float **floatptr)
{
   labelptr settext = NULL;
   short    *osel;
   stringpart *strptr, *nextptr;
   const float f_one = 1.00;

   if (floatptr) *floatptr = &areastruct.textscale;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      if (textpos > 0 || textpos < stringlength(settext->string, True,
		areastruct.topinstance)) {
         settext = *((labelptr *)EDITPART);
	 strptr = findstringpart(textpos - 1, NULL, settext->string,
			areastruct.topinstance);
	 nextptr = findstringpart(textpos, NULL, settext->string,
			areastruct.topinstance);
	 if (strptr->type == FONT_SCALE) {
	    if (floatptr) *floatptr = &strptr->data.scale;
	 }
	 else if (nextptr && nextptr->type == FONT_SCALE) {
	    if (floatptr) *floatptr = &nextptr->data.scale;
	 }
	 else if (floatptr) *floatptr = (float *)(&f_one);
      }
      else {
         settext = *((labelptr *)EDITPART);
         if (floatptr) *floatptr = &(settext->scale);
      }
   }
   else if (areastruct.selects > 0) {
      for (osel = areastruct.selectlist; osel < areastruct.selectlist +
		areastruct.selects; osel++) {
	 if (SELECTTYPE(osel) == LABEL) {
            settext = SELTOLABEL(osel);
            if (floatptr) *floatptr = &(settext->scale);
	    break;
	 }
      }
   }
   return settext;
}

/*--------------------------------------------------------------*/
/* Generate the popup dialog for getting text scale.		*/ 
/*--------------------------------------------------------------*/

void gettsize(xcWidget button, caddr_t nulldata, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   float *floatptr;
   Boolean local;
   labelptr settext;

   settext = gettextsize(&floatptr);
   sprintf(buffer, "%5.2f", *floatptr);

   if (settext) {
      getgeneric(savebutton, button, gettsize, settext);
      popupprompt(button, "Enter text scale:", buffer, settsize, savebutton, NULL);
   }
   else {
      getgeneric(savebutton, button, gettsize, floatptr);
      popupprompt(button,
	    "Enter default text scale:", buffer, setfloat, savebutton, NULL);
   }
}

/*----------------------------------------------------------------*/
/* Set the scale of an object or group of selected objects	  */
/*----------------------------------------------------------------*/

void setosize(xcWidget w, objinstptr dataptr)
{
   float tmpres;
   short *osel;
   objinstptr nsobj;
   int res = sscanf(_STR2, "%f", &tmpres);

   if (res == 0 || tmpres <= 0) {
      Wprintf("Illegal value");
      return;
   }
   for (osel = areastruct.selectlist; osel < areastruct.selectlist +
	     areastruct.selects; osel++) {
      if (SELECTTYPE(osel) == OBJINST) {
	 nsobj = SELTOOBJINST(osel);
         nsobj->scale = tmpres;
      }
   }
   unselect_all();
   pwriteback(areastruct.topinstance);
   drawarea(NULL, NULL, NULL);
}

/*----------------------------------------------------------------*/
/* Generate popup dialog for getting object scale		  */
/*----------------------------------------------------------------*/

void getosize(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   char buffer[50];
   float flval;
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   short *osel = areastruct.selectlist;
   short selects = 0;
   objinstptr setobj = NULL;

   for (; osel < areastruct.selectlist + areastruct.selects; osel++)
      if (SELECTTYPE(osel) == OBJINST) {
	 setobj = SELTOOBJINST(osel);
	 selects++;
	 break;
      }
   if (setobj == NULL) {
      Wprintf("No objects were selected for scaling.");
      return;
   }
   flval = setobj->scale;
   getgeneric(savebutton, button, getosize, setobj);
   sprintf(buffer, "%4.2f", flval);
   popupprompt(button, "Enter object scale:", buffer, setosize, savebutton, NULL);
}

/*----------------------------------------------------------------*/
/* Generate popup prompt for getting global linewidth		  */
/*----------------------------------------------------------------*/

void getwirewidth(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   float *widthptr;

   widthptr = &(xobjs.pagelist[areastruct.page]->wirewidth);
   getgeneric(savebutton, button, getwirewidth, widthptr);
   sprintf(buffer, "%4.2f", *widthptr / 2.0);
   popupprompt(button, "Enter new global linewidth:", buffer, setwidth,
	savebutton, NULL);
}

/*----------------------------------------------------------------*/
/* Set the linewidth of all selected arcs, polygons, splines, and */
/* paths.							  */
/*----------------------------------------------------------------*/

void setwwidth(xcWidget w, void *dataptr)
{
   float     tmpres;
   short     *osel;
   arcptr    nsarc;
   polyptr   nspoly;
   splineptr nsspline;
   pathptr   nspath;

   if (sscanf(_STR2, "%f", &tmpres) == 0) {
      Wprintf("Illegal value");
      return;
   }
   else if (areastruct.selects == 0) {
      areastruct.linewidth = tmpres;
   }
   else {
      for (osel = areastruct.selectlist; osel < areastruct.selectlist +
	     areastruct.selects; osel++) {
         if (SELECTTYPE(osel) == ARC) {
	    nsarc = SELTOARC(osel);
            nsarc->width = tmpres;
         }
         else if (SELECTTYPE(osel) == POLYGON) {
	    nspoly = SELTOPOLY(osel);
            nspoly->width = tmpres;
         }
         else if (SELECTTYPE(osel) == SPLINE) {
	    nsspline = SELTOSPLINE(osel);
            nsspline->width = tmpres;
         }
         else if (SELECTTYPE(osel) == PATH) {
	    nspath = SELTOPATH(osel);
            nspath->width = tmpres;
         }
      }
      unselect_all();
      pwriteback(areastruct.topinstance);
      drawarea(NULL, NULL, NULL);
   }
}

/*----------------------------------------------------------------*/
/* Generate popup dialong for getting linewidths of elements	  */ 
/*----------------------------------------------------------------*/

void getwwidth(xcWidget button, caddr_t clientdata, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   short *osel = areastruct.selectlist;
   genericptr setel;
   float flval;

   for (; osel < areastruct.selectlist + areastruct.selects; osel++) {
      setel = *(topobject->plist + (*osel));
      if (IS_ARC(setel)) {
	 flval = ((arcptr)setel)->width;
	 break;
      }
      else if (IS_POLYGON(setel)) {
	 flval = ((polyptr)setel)->width;
	 break;
      }
      else if (IS_SPLINE(setel)) {
	 flval = ((splineptr)setel)->width;
	 break;
      }
      else if (IS_PATH(setel)) {
	 flval = ((pathptr)setel)->width;
	 break;
      }
   }
   getgeneric(savebutton, button, getwwidth, setel);
   if (osel == areastruct.selectlist + areastruct.selects) {
      sprintf(buffer, "%4.2f", areastruct.linewidth);
      popupprompt(button, "Enter new default line width:", buffer, setwwidth,
		savebutton, NULL);
   }
   else {
      sprintf(buffer, "%4.2f", flval);
      popupprompt(button, "Enter new line width:", buffer, setwwidth,
		savebutton, NULL);
   }
}

/*----------------------------------------------------------------*/
/* Generic popup prompt for getting a floating-point value	  */
/*----------------------------------------------------------------*/

void getfloat(xcWidget button, float *floatptr, caddr_t calldata)
{
   char buffer[50];
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));

   getgeneric(savebutton, button, getfloat, floatptr);
   sprintf(buffer, "%4.2f", *floatptr);
   popupprompt(button, "Enter value:", buffer, setfloat, savebutton, NULL);
}

/*----------------------------------------------------------------*/
/* Set the filename for the current page			  */
/*----------------------------------------------------------------*/

void setfilename(xcWidget w, char **dataptr)
{
   short cpage, depend = 0;
   objectptr checkpage;
   char *oldstr = xobjs.pagelist[areastruct.page]->filename;

   if (!strcmp(*dataptr, _STR2)) return;   /* no change in string */

   /* Make the change to the current page */
   xobjs.pagelist[areastruct.page]->filename = strdup(_STR2);

   /* All existing filenames which match the old string should also be changed */
   for (cpage = 0; cpage < xobjs.pages; cpage++) {
      if ((xobjs.pagelist[cpage]->pageinst != NULL) && (cpage != areastruct.page)) {
	 if (!strcmp(xobjs.pagelist[cpage]->filename, oldstr)) {
	    free(xobjs.pagelist[cpage]->filename);
	    xobjs.pagelist[cpage]->filename = strdup(_STR2);
	 }
      }
   }
   free(oldstr);
}

/*----------------------------------------------------------------*/
/* Set the page label for the current page			  */
/*----------------------------------------------------------------*/

void setpagelabel(xcWidget w, char *dataptr)
{
   short i;

   /* Whitespace and non-printing characters not allowed */

   for (i = 0; i < strlen(_STR2); i++) {
      if ((!isprint(_STR2[i])) || (isspace(_STR2[i]))) {
         _STR2[i] = '_';
         Wprintf("Replaced illegal whitespace in name with underscore");
      }
   }

   if (!strcmp(dataptr, _STR2)) return; /* no change in string */
   if (strlen(_STR2) == 0)
      sprintf(topobject->name, "Page %d", areastruct.page + 1);
   else
      sprintf(topobject->name, "%.79s", _STR2);

   /* For schematics, all pages with associations to symbols must have	*/
   /* unique names.							*/
   if (topobject->symschem != NULL) checkpagename(topobject);

   printname(topobject);
   renamepage(areastruct.page);
}

/*--------------------------------------------------------------*/
/* Change the Button1 binding for a particular mode (non-Tcl)	*/
/*--------------------------------------------------------------*/

#ifndef TCL_WRAPPER

/*--------------------------------------------------------------*/
/* Change to indicated tool (key binding w/o value) */
/*--------------------------------------------------------------*/

void changetool(xcWidget w, pointertype value, caddr_t nulldata)
{
   mode_rebinding((int)value, (short)-1);
   highlightexcl(w, (int)value, (int)-1);
}

/*--------------------------------------------------------------*/
/* Execute function binding for the indicated tool if something	*/
/* is selected;  otherwise, change to the indicated tool mode.	*/
/*--------------------------------------------------------------*/

void exec_or_changetool(xcWidget w, pointertype value, caddr_t nulldata)
{
   if (areastruct.selects > 0)
      mode_tempbinding((int)value, -1);
   else {
      mode_rebinding((int)value, -1);
      highlightexcl(w, (int)value, -1);
   }
}

/*--------------------------------------------------------------*/
/* Special case of exec_or_changetool(), where an extra value	*/
/* is passed to the routine indication amount of rotation, or	*/
/* type of flip operation.					*/
/*--------------------------------------------------------------*/

void rotatetool(xcWidget w, pointertype value, caddr_t nulldata)
{
   if (areastruct.selects > 0)
      mode_tempbinding(XCF_Rotate, (int)value);
   else {
      mode_rebinding(XCF_Rotate, (int)value);
      highlightexcl(w, (int)XCF_Rotate, (int)value);
   }
}

/*--------------------------------------------------------------*/
/* Special case of changetool() for pan mode, where a value is	*/
/* required to pass to the key binding routine.			*/
/*--------------------------------------------------------------*/

void pantool(xcWidget w, pointertype value, caddr_t nulldata)
{
   mode_rebinding(XCF_Pan, (int)value);
   highlightexcl(w, (int)XCF_Pan, (int)value);
}

#endif  /* !TCL_WRAPPER */

/*--------------------------------------------------------------*/
/* Add a new font name to the list of known fonts		*/
/* Register the font number for the Alt-F cycling mechanism	*/
/* Tcl: depends on command tag mechanism for GUI menu update.	*/
/*--------------------------------------------------------------*/

void makenewfontbutton()
{

#ifndef TCL_WRAPPER
   Arg	wargs[1];
   int  n = 0;
   xcWidget newbutton, cascade;

   if (fontcount == 0) return;

   cascade = XtParent(FontAddNewFontButton);
   XtnSetArg(XtNfont, appdata.xcfont);
   newbutton = XtCreateWidget(fonts[fontcount - 1].family, XwmenubuttonWidgetClass,
	 cascade, wargs, n);

   XtAddCallback (newbutton, XtNselect, (XtCallbackProc)setfont,
		Number(fontcount - 1));
   XtManageChild(newbutton);
#endif

   nfontnumbers++;
   if (nfontnumbers == 1)
      fontnumbers = (u_short *)malloc(sizeof(u_short)); 
   else
      fontnumbers = (u_short *)realloc(fontnumbers, nfontnumbers
		* sizeof(u_short));
   fontnumbers[nfontnumbers - 1] = fontcount - 1;
}

#ifndef TCL_WRAPPER

/*--------------------------------------------------------------*/
/* Make new encoding menu button				*/
/*--------------------------------------------------------------*/

void makenewencodingbutton(char *ename, char value)
{
   Arg	wargs[1];
   int  n = 0;
   xcWidget newbutton, cascade;

   cascade = XtParent(EncodingStandardButton);

   /* return if button has already been made */
   newbutton = XtNameToWidget(cascade, ename);
   if (newbutton != NULL) return;

   XtnSetArg(XtNfont, appdata.xcfont);
   newbutton = XtCreateWidget(ename, XwmenubuttonWidgetClass,
	 cascade, wargs, n);

   XtAddCallback (newbutton, XtNselect, (XtCallbackProc)setfontencoding,
		Number(value));
   XtManageChild(newbutton);
}

#endif

/*--------------------------------------------------------------*/
/* Set the menu checkmarks on the font menu			*/
/*--------------------------------------------------------------*/

#ifdef TCL_WRAPPER

void togglefontmark(int fontval)
{
   Tcl_SetVar2(xcinterp, "XCOps", "fontfamily", fonts[fontval].family,
		TCL_NAMESPACE_ONLY);
}

#else

void togglefontmark(int fontval)
{
   Arg args[1];
   xcWidget widget, cascade, sibling;
   short i;

   cascade = XtParent(FontAddNewFontButton);
   widget = XtNameToWidget(cascade, fonts[fontval].family);

   /* Remove checkmark from all widgets in the list */

   XtSetArg(args[0], XtNsetMark, False);
   for (i = 0; i < fontcount; i++) {
      if (i != fontval) {
         sibling = XtNameToWidget(cascade, fonts[i].family);
         XtSetValues(sibling, args, 1);
      }
   }

   /* Add checkmark to designated font */

   XtSetArg(args[0], XtNsetMark, True);
   XtSetValues(widget, args, 1);
}

#endif

#ifndef TCL_WRAPPER

/*--------------------------------------------------------------------*/
/* Toggle one of a set of menu items, only one of which can be active */
/*--------------------------------------------------------------------*/

void toggleexcl(xcWidget widget, menuptr menu, int menulength)
{
   Arg          args[1];
   xcWidget     parent = xcParent(widget);
   xcWidget     sibling;
   menuptr	mitem, mtest;
   short	i;

   /* find the menu item which corresponds to the widget which was pushed */

   for (mtest = menu; mtest < menu + menulength; mtest++) {
      sibling = XtNameToWidget(parent, mtest->name);
      if (sibling == widget) break;
   }

   /* remove checkmark from other widgets in the list */

   XtSetArg(args[0], XtNsetMark, False);
   if (menu == Fonts) {     		/* special action for font list */
      for (i = 0; i < fontcount; i++) {
	 sibling = XtNameToWidget(parent, fonts[i].family);
	 if (sibling != widget)
	    XtSetValues(sibling, args, 1);
      }
      mtest = &Fonts[3];   /* so that mtest->func has correct value below */
   }
   else if (mtest == menu + menulength) return;  /* something went wrong? */

   for (mitem = menu; mitem < menu + menulength; mitem++) {
      sibling = XtNameToWidget(parent, mitem->name);
      if (mitem->func == mtest->func)
         XtSetValues(sibling, args, 1);
   }

   /* Now set the currently pushed widget */

   XtSetArg(args[0], XtNsetMark, True);
   XtSetValues(widget, args, 1);
}

/*----------------------------------------------------------------------*/
/* Cursor changes based on toolbar mode					*/
/*----------------------------------------------------------------------*/

void toolcursor(int mode)
{
   /* Some cursor types for different modes */
   switch(mode) {
      case XCF_Spline:
      case XCF_Move:
      case XCF_Join:
      case XCF_Unjoin:
	 XDefineCursor (dpy, areastruct.areawin, ARROW);
	 areastruct.defaultcursor = &ARROW;
	 break;
      case XCF_Pan:
	 XDefineCursor (dpy, areastruct.areawin, HAND);
	 areastruct.defaultcursor = &HAND;
	 break;
      case XCF_Delete:
	 XDefineCursor (dpy, areastruct.areawin, SCISSORS);
	 areastruct.defaultcursor = &SCISSORS;
	 break;
      case XCF_Copy:
	 XDefineCursor (dpy, areastruct.areawin, COPYCURSOR);
	 areastruct.defaultcursor = &COPYCURSOR;
	 break;
      case XCF_Push:
      case XCF_Select_Save:
	 XDefineCursor (dpy, areastruct.areawin, QUESTION);
	 areastruct.defaultcursor = &QUESTION;
	 break;
      case XCF_Rotate:
      case XCF_Flip_X:
      case XCF_Flip_Y:
	 XDefineCursor (dpy, areastruct.areawin, ROTATECURSOR);
	 areastruct.defaultcursor = &ROTATECURSOR;
	 break;
      case XCF_Edit:
	 XDefineCursor (dpy, areastruct.areawin, EDCURSOR);
	 areastruct.defaultcursor = &EDCURSOR;
	 break;
      case XCF_Arc:
	 XDefineCursor (dpy, areastruct.areawin, CIRCLE);
	 areastruct.defaultcursor = &CIRCLE;
	 break;
      case XCF_Text:
      case XCF_Pin_Label:
      case XCF_Pin_Global:
      case XCF_Info_Label:
	 XDefineCursor (dpy, areastruct.areawin, TEXTPTR);
	 areastruct.defaultcursor = &TEXTPTR;
	 break;
      default:
	 XDefineCursor (dpy, areastruct.areawin, CROSS);
	 areastruct.defaultcursor = &CROSS;
	 break;
   }
}

/*--------------------------------------------------------------------------*/
/* Highlight one of a set of toolbar items, only one of which can be active */
/*--------------------------------------------------------------------------*/

void highlightexcl(xcWidget widget, int func, int value)
{
   Arg          args[2];
   xcWidget     parent = xcParent(PanToolButton);
   xcWidget     sibling, self = (xcWidget)NULL;
   toolbarptr	titem, ttest;

   /* remove highlight from all widgets in the toolbar */

   XtSetArg(args[0], XtNbackground, BACKGROUND);
   XtSetArg(args[1], XtNborderColor, SNAPCOLOR);
   for (titem = ToolBar; titem < ToolBar + toolbuttons; titem++) {
      sibling = XtNameToWidget(parent, titem->name);
      if (sibling == widget)
	 self = sibling;
      else
         XtSetValues(sibling, args, 2);
   }

   if (self == (xcWidget)NULL) {
      /* We invoked a toolbar button from somewhere else.  Highlight	*/
      /* the toolbar associated with the function value.		*/
      switch (func) {
	 case XCF_Pan:
	    self = PanToolButton;
	    break;

	 case XCF_Rotate:
	    if (value > 0)
	       self = RotPToolButton;
	    else
	       self = RotNToolButton;
	    break;

	 default:
	    for (titem = ToolBar; titem < ToolBar + toolbuttons; titem++) {
	       if (func == (int)(titem->passeddata)) {
                  self = XtNameToWidget(parent, titem->name);
	          break;
	       }
	    }
	    break;
      }
   }

   /* Now highlight the currently pushed widget */

   if (self != (xcWidget)NULL) {
      XtSetArg(args[0], XtNbackground, RATSNESTCOLOR);
      XtSetArg(args[1], XtNborderColor, RATSNESTCOLOR);
      XtSetValues(self, args, 2);
   }
}

#endif

/*--------------------*/
/* Toggle a menu item */
/*--------------------*/

#ifndef TCL_WRAPPER

void toggle(xcWidget w, Boolean *boolvalue, caddr_t calldata)
{
   Arg	wargs[1];

   *boolvalue = !(*boolvalue);
   XtSetArg(wargs[0], XtNsetMark, *boolvalue);
   XtSetValues(w, wargs, 1);
   drawarea(w, NULL, NULL);
}

#endif

/*---------------------------------------------------------------*/
/* Some Xcircuit routines using toggle and toggleexcl, 		 */
/* put here because they reference the menu structures directly. */
/*---------------------------------------------------------------*/

void setcolorscheme(Boolean boolvalue)
{
   if (boolvalue) {
      PARAMCOLOR = appdata.parampix;
      AUXCOLOR = appdata.auxpix;
      BARCOLOR = appdata.barpix;
      OFFBUTTONCOLOR = appdata.buttonpix;
      SELECTCOLOR = appdata.selectpix;
      QUERYCOLOR = appdata.querypix;
      FILTERCOLOR = appdata.filterpix;
      GRIDCOLOR = appdata.gridpix;
      SNAPCOLOR = appdata.snappix;
      AXESCOLOR = appdata.axespix;
      BACKGROUND = appdata.bg;
      FOREGROUND = appdata.fg;
   }
   else {
      PARAMCOLOR = appdata.parampix2;
      AUXCOLOR = appdata.auxpix2;
      BARCOLOR = appdata.barpix2;
      OFFBUTTONCOLOR = appdata.buttonpix2;
      SELECTCOLOR = appdata.selectpix2;
      QUERYCOLOR = appdata.querypix2;
      FILTERCOLOR = appdata.filterpix2;
      GRIDCOLOR = appdata.gridpix2;
      SNAPCOLOR = appdata.snappix2;
      AXESCOLOR = appdata.axespix2;
      BACKGROUND = appdata.bg2;
      FOREGROUND = appdata.fg2;
   }

   makecursors();
}

/*----------------------------------------------------------------*/
/* Invert the color scheme used for the background/foreground	  */
/*----------------------------------------------------------------*/

#ifndef TCL_WRAPPER

void inversecolor(xcWidget w, Boolean *boolvalue, caddr_t calldata)
{
   /* change color scheme */

   setcolorscheme(*boolvalue);

   /* toggle checkmark in menu on "Alt colors" */

   if (w == NULL) w = OptionsAltColorsButton;
   if (w != NULL) toggle(w, boolvalue, calldata);
   if (eventmode == NORMAL_MODE)
      XDefineCursor (dpy, areastruct.areawin, DEFAULTCURSOR);
}

#endif

/*----------------------------------------------------------------*/
/* Change menu selection for reported measurement units		  */
/*----------------------------------------------------------------*/

#ifdef TCL_WRAPPER

void togglegrid(u_short type)
{
   static char *stylenames[] = {
      "decimal inches",
      "fractional inches",
      "centimeters",
      "internal units",
   };

   /* Note that Tcl_SetVar() doesn't require double-quotes around the	*/
   /* string; doing so prevents the menu from recognizing the variable	*/
   /* value.								*/

   Tcl_SetVar2(xcinterp, "XCOps", "gridstyle", stylenames[type], TCL_NAMESPACE_ONLY);
}

#else

void togglegrid(u_short type)
{
   xcWidget button, bparent = XtParent(GridtypedisplayDecimalInchesButton);

   if (type == CM) button = XtNameToWidget(bparent, "Centimeters");
   else if (type == FRAC_INCH) button = XtNameToWidget(bparent, "Fractional Inches");
   else if (type == DEC_INCH) button = XtNameToWidget(bparent, "Decimal Inches");
   else if (type == INTERNAL) button = XtNameToWidget(bparent, "Internal Units");
   else button = XtNameToWidget(bparent, "Coordinates");
   toggleexcl(button, GridStyles, XtNumber(GridStyles));
   W1printf(" ");
}

#endif

/*----------------------------------------------------------------*/
/* Set the default reported grid units to inches or centimeters   */
/*----------------------------------------------------------------*/

#ifdef TCL_WRAPPER

void setgridtype(char *string)
{
}

#else

void setgridtype(char *string)
{
   xcWidget button, bparent = XtParent(GridtypedisplayDecimalInchesButton);

   if (!strcmp(string, "inchscale")) {
      button = XtNameToWidget(bparent, "Fractional Inches");
      getgridtype(button, FRAC_INCH, NULL);
   }
   else if (!strcmp(string, "cmscale")) {
      button = XtNameToWidget(bparent, "Centimeters");
      getgridtype(button, CM, NULL);
   }
}

#endif

/*----------------------------------------------------------------*/
/* Called by setgridtype() to complete setting the reported 	  */
/* measurement units						  */
/*----------------------------------------------------------------*/

void getgridtype(xcWidget button, pointertype value, caddr_t calldata)
{
   short oldtype = xobjs.pagelist[areastruct.page]->coordstyle;
   float scalefac = getpsscale(1.0, areastruct.page) / INCHSCALE;

#ifndef TCL_WRAPPER
   toggleexcl(button, GridStyles, XtNumber(GridStyles));
#endif
   xobjs.pagelist[areastruct.page]->coordstyle = (short)value;

   switch(value) {
      case FRAC_INCH: case DEC_INCH: case INTERNAL:
	 if (oldtype == CM) {
            xobjs.pagelist[areastruct.page]->outscale *= scalefac;
#ifndef TCL_WRAPPER
	    /* Note:  Tcl defines a method for selecting standard  */
	    /* page sizes.  We really DON'T want to reset the size */
	    /* just because we switched measurement formats!	   */

	    xobjs.pagelist[areastruct.page]->pagesize.x = 612;
	    xobjs.pagelist[areastruct.page]->pagesize.y = 792; /* letter */
#endif
	 }
	 break;
      case CM:
	 if (oldtype != CM) {
            xobjs.pagelist[areastruct.page]->outscale *= scalefac;
#ifndef TCL_WRAPPER
	    xobjs.pagelist[areastruct.page]->pagesize.x = 595;
	    xobjs.pagelist[areastruct.page]->pagesize.y = 842; /* A4 */
#endif
	 }
	 break;
   }
   if (oldtype != xobjs.pagelist[areastruct.page]->coordstyle) {
      drawarea(NULL, NULL, NULL);
      W1printf(" ");
   }
}

/*------------------------------------------------------*/
/* Make new library, add new button to the "Libraries"	*/
/* cascaded menu, compose the library page, update the  */
/* library directory, and go to that library page.	*/
/*------------------------------------------------------*/

void newlibrary(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   int libnum = createlibrary(FALSE);
   startcatalog(w, libnum, NULL);
}

/*----------------------------------------------*/
/* Find an empty library, and return its page	*/
/* number if it exists.  Otherwise, return -1.	*/
/* This search does not include the so-called	*/
/* "User Library" (last library in list).	*/
/*----------------------------------------------*/

int findemptylib()
{
  int i;

  for (i = 0; i < xobjs.numlibs - 1; i++) {
     if (xobjs.userlibs[i].number == 0)
	return i;
  }
  return -1;
}

/*----------------------------------------------*/
/* Make new library and add a new button to the */
/* "Libraries" cascaded menu.			*/
/*----------------------------------------------*/

int createlibrary(Boolean force)
{
   xcWidget libmenu, newbutton, oldbutton;
#ifndef TCL_WRAPPER
   Arg wargs[2];
#endif
   char libstring[20];
   int libnum;
   objectptr newlibobj;

   /* If there's an empty library, return its number */
   if ((!force) && (libnum = findemptylib()) >= 0) return (libnum + LIBRARY);
   libnum = (xobjs.numlibs++) + LIBRARY;
   xobjs.libtop = (objinstptr *)realloc(xobjs.libtop,
		(libnum + 1) * sizeof(objinstptr));
   xobjs.libtop[libnum] = xobjs.libtop[libnum - 1];
   libnum--;

   newlibobj = (objectptr) malloc(sizeof(object));
   initmem(newlibobj);
   xobjs.libtop[libnum] = newpageinst(newlibobj);

   sprintf(newlibobj->name, "Library %d", libnum - LIBRARY + 1);

   /* Create the library */

   xobjs.userlibs = (Library *) realloc(xobjs.userlibs, xobjs.numlibs
	* sizeof(Library));
   xobjs.userlibs[libnum + 1 - LIBRARY] = xobjs.userlibs[libnum - LIBRARY];
   xobjs.userlibs[libnum - LIBRARY].library = (objectptr *) malloc(sizeof(objectptr));
   xobjs.userlibs[libnum - LIBRARY].number = 0;
   xobjs.userlibs[libnum - LIBRARY].instlist = NULL;
   xobjs.userlibs[libnum - LIBRARY].filename = NULL;

#ifdef TCL_WRAPPER

   sprintf(_STR2, "xcircuit::newlibrarybutton \"%s\"", newlibobj->name);
   Tcl_Eval(xcinterp, _STR2);

#else

   /* Previously last button becomes new library pointer */

   oldbutton = GotoLibraryLibrary2Button;
   XtRemoveAllCallbacks (oldbutton, XtNselect);
   XtAddCallback (oldbutton, XtNselect, (XtCallbackProc)startcatalog,
	Number(libnum));
   XtSetArg(wargs[0], XtNlabel, xobjs.libtop[libnum]->thisobject->name);
   XtSetValues(oldbutton, wargs, 1);

   /* Make new entry in the menu to replace the User Library button */
   /* xcWidget name is unique so button can be found later.  Label is */
   /* always set to "User Library"				    */

   sprintf(libstring, "Library %d", libnum - LIBRARY + 2);
   libmenu = XtParent(GotoLibraryAddNewLibraryButton);
   XtSetArg(wargs[0], XtNfont, appdata.xcfont);
   XtSetArg(wargs[1], XtNlabel, "User Library");
   newbutton = XtCreateWidget(libstring, XwmenubuttonWidgetClass,
         libmenu, wargs, 2);
   XtAddCallback (newbutton, XtNselect, (XtCallbackProc)startcatalog,
	Number(libnum + 1));
   XtManageChild(newbutton);
   GotoLibraryLibrary2Button = newbutton;
#endif

   /* Update the library directory to include the new page */

   composelib(LIBLIB);

   return libnum;
}

/*----------------------------------------------*/
/* Make new page; goto that page		*/
/* (wrapper for routine events.c:newpage())	*/
/*----------------------------------------------*/

void newpagemenu(xcWidget w, pointertype value, caddr_t nulldata)
{
   newpage((short)value);
}

/*--------------------------------------------------------------*/
/* Routine called by newpage() if new button needs to be made 	*/
/* to add to the "Pages" cascaded menu.				*/
/*--------------------------------------------------------------*/

void makepagebutton()
{
   xcWidget pagemenu, newbutton;
#ifndef TCL_WRAPPER
   Arg wargs[1];
#endif
   char pagestring[10];

   /* make new entry in the menu */

#ifdef TCL_WRAPPER
   sprintf(_STR2, "newpagebutton \"Page %d\"", xobjs.pages);
   Tcl_Eval(xcinterp, _STR2);
#else
   pagemenu = XtParent(GotoPageAddNewPageButton);
   XtSetArg(wargs[0], XtNfont, appdata.xcfont);
   sprintf(pagestring, "Page %d", xobjs.pages);
   newbutton = XtCreateWidget(pagestring, XwmenubuttonWidgetClass,
         pagemenu, wargs, 1);
   XtAddCallback (newbutton, XtNselect, (XtCallbackProc)newpagemenu,
	Number(xobjs.pages - 1));
   XtManageChild(newbutton);
#endif

   /* Update the page directory */

   composelib(PAGELIB);
}

/*----------------------------------------------------------------*/
/* Find the Page menu button associated with the page number	  */
/* (passed parameter) and set the label of that button to the	  */
/* object name (= page label)					  */
/*----------------------------------------------------------------*/

void renamepage(short pagenumber)
{
   int page;
   objinstptr thisinst = xobjs.pagelist[pagenumber]->pageinst;

#ifdef TCL_WRAPPER
   char *pname, *plabel;

   if ((pagenumber >= 0) && (pagenumber < xobjs.pages - 1) &&
	    (thisinst != NULL)) {
      plabel = thisinst->thisobject->name;
      pname = (char *)malloc(28 + strlen(plabel));
      sprintf(pname, "xcircuit::renamepage %d {%s}", pagenumber + 1, plabel);
      Tcl_Eval(xcinterp, pname);
      free(pname);
   }
#else
   Arg wargs[1];
   xcWidget parent = XtParent(GotoPageAddNewPageButton);
   xcWidget button;
   char bname[10];

   sprintf(bname, "Page %d", pagenumber + 1);
   button = XtNameToWidget(parent, bname);

   if ((button != NULL) && (thisinst != NULL)) {
      if (thisinst->thisobject->name != NULL)
         XtSetArg(wargs[0], XtNlabel,
		thisinst->thisobject->name);
      else
         XtSetArg(wargs[0], XtNlabel, bname);
      XtSetValues(button, wargs, 1);
   }
   else if (button == NULL)
      Fprintf(stderr, "Error:  No Button Widget named \"%9s\"\n", bname);
#endif

   /* The GUI has been taken care of, but the page itself might not exist */
   if (!thisinst) return;

   /* If the page had a master but now has a different	*/
   /* name, disconnect the schematics.			*/

   if (thisinst->thisobject->schemtype == SECONDARY) {
      if (strcmp(thisinst->thisobject->name, thisinst->thisobject->symschem->name)) {
	 thisinst->thisobject->symschem = NULL;
	 thisinst->thisobject->schemtype = PRIMARY;
      }
      Wprintf("Page disconnected from master schematic.");
   }

   /* Conversely, if the page has the same name as another page, the	*/
   /* schematics are considered connected.				*/

   for (page = 0; page < xobjs.pages; page++) {
      objinstptr pageinst;
      if (page == pagenumber) continue;
      pageinst = xobjs.pagelist[page]->pageinst;
      if (pageinst && !strcmp(pageinst->thisobject->name,
		thisinst->thisobject->name)) {
	 thisinst->thisobject->symschem = pageinst->thisobject;
	 thisinst->thisobject->schemtype = SECONDARY;
         Wprintf("Page connected to master schematic.");
	 break;
      }
   }
}

/*--------------------------------------------------------------*/
/* Same routine as above, for Library page menu buttons		*/
/*--------------------------------------------------------------*/

void renamelib(short libnumber)
{
#ifdef TCL_WRAPPER
   sprintf(_STR2, "xcircuit::renamelib %d \"%s\"", libnumber - LIBRARY + 1,
	xobjs.libtop[libnumber]->thisobject->name);
   Tcl_Eval(xcinterp, _STR2);
#else
   Arg wargs[1];
   xcWidget parent = XtParent(GotoLibraryAddNewLibraryButton);
   xcWidget button;
   char bname[13];

   sprintf(bname, "Library %d", libnumber - LIBRARY + 1);
   button = XtNameToWidget(parent, bname);

   if (button != NULL) {
      if (xobjs.libtop[libnumber]->thisobject->name != NULL)
         XtSetArg(wargs[0], XtNlabel, xobjs.libtop[libnumber]->thisobject->name);
      else
         XtSetArg(wargs[0], XtNlabel, bname);
      XtSetValues(button, wargs, 1);
   }
   else
      Fprintf(stderr, "Error:  No Button Widget named \"%12s\"\n", bname);
#endif
}

/*----------------------------------------------*/
/* Set Poly and Arc line styles and fill styles */
/*----------------------------------------------*/

#define BORDERS  (NOBORDER | DOTTED | DASHED)
#define ALLFILLS (FILLSOLID | FILLED)

/*--------------------------------------------------------------*/
/* Set the menu checkmarks on the color menu			*/
/*--------------------------------------------------------------*/

void setcolormark(int colorval)
{
#ifndef TCL_WRAPPER
   Arg args[1];
   xcWidget w = NULL;
   short i;

   if (colorval == DEFAULTCOLOR)
      w = ColorInheritColorButton;
   else {
      for (i = 0; i < number_colors; i++)
         if (colorlist[i].color.pixel == colorval) {
            w = colorlist[i].cbutton;
	    break;
         }
   }

   /* Remove mark from all menu items */

   XtSetArg(args[0], XtNsetMark, False);
   for (i = 0; i < number_colors; i++)
      XtSetValues(colorlist[i].cbutton, args, 1);
   XtSetValues(ColorInheritColorButton, args, 1);

   /* Add mark to the menu button for the chosen color */

   if (w != (xcWidget)NULL) {
      overdrawpixmap(w);
      XtSetArg(args[0], XtNsetMark, True);
      XtSetValues(w, args, 1);
   }
#else
   /* Set GUI variables and execute any command tags associated */
   /* with the "color" command */

   Tcl_Obj *objv[3];
   int i;

   objv[0] = Tcl_NewStringObj("color", 5);
   objv[1] = Tcl_NewStringObj("set", 3);
   if (colorval == DEFAULTCOLOR)
      objv[2] = Tcl_NewStringObj("inherit", 7);
   else {
      for (i = 0; i < number_colors; i++)
         if (colorlist[i].color.pixel == colorval)
	    break;
      objv[2] = Tcl_NewIntObj((int)i);
   }
   Tcl_SetVar2Ex(xcinterp, "XCOps", "colorval", objv[2], TCL_NAMESPACE_ONLY);

   XcTagCallback(xcinterp, 3, objv);
#endif
}

/*----------------------------------------------------------------*/
/* Set the checkmarks on the element styles menu		  */
/*----------------------------------------------------------------*/

void setallstylemarks(u_short styleval)
{
#ifndef TCL_WRAPPER
   xcWidget w;
   Arg	wargs[1];

   XtSetArg(wargs[0], XtNsetMark, (styleval & UNCLOSED) ? 0 : 1);
   XtSetValues(BorderClosedButton, wargs, 1);

   XtSetArg(wargs[0], XtNsetMark, (styleval & BBOX) ? 1 : 0);
   XtSetValues(BorderBoundingBoxButton, wargs, 1);

   if (styleval & NOBORDER)
      w = BorderUnborderedButton;
   else if (styleval & DASHED)
      w = BorderDashedButton;
   else if (styleval & DOTTED)
      w = BorderDottedButton;
   else
      w = BorderSolidButton;
   toggleexcl(w, BorderStyles, XtNumber(BorderStyles));

   if (styleval & OPAQUE)
      w = FillOpaqueButton;
   else
      w = FillTransparentButton;
   toggleexcl(w, Stipples, XtNumber(Stipples));

   if (!(styleval & FILLED))
      w = FillWhiteButton;
   else {
      styleval &= FILLSOLID;
      styleval /= STIP0;
      switch(styleval) {
	 case 0: w = FillGray87Button; break;
	 case 1: w = FillGray75Button; break;
	 case 2: w = FillGray62Button; break;
	 case 3: w = FillGray50Button; break;
	 case 4: w = FillGray37Button; break;
	 case 5: w = FillGray25Button; break;
	 case 6: w = FillGray12Button; break;
	 case 7: w = FillBlackButton;  break;
      }
   }
   toggleexcl(w, Stipples, XtNumber(Stipples));
#else

   /* Set GUI variables and execute any command tags associated	*/
   /* with the "fill" and "border" commands.			*/

   Tcl_Obj *objv[3];
   int fillfactor;
   const char *bptr;

   const char *borders[] = {"solid", "unbordered", "dashed", "dotted"};
   enum BorderIdx { SolidIdx, UnborderedIdx, DashedIdx, DottedIdx };

   objv[0] = Tcl_NewStringObj("fill", 4);

   if (styleval & FILLED) {
      fillfactor = (int)(12.5 * (float)(1 + ((styleval & FILLSOLID) >> 5)));
      if (fillfactor == 100)
         objv[1] = Tcl_NewStringObj("solid", 5);
      else
         objv[1] = Tcl_NewIntObj(fillfactor);
   }
   else
      objv[1] = Tcl_NewStringObj("unfilled", 8);

   if (styleval & OPAQUE)
      objv[2] = Tcl_NewStringObj("opaque", 6);
   else
      objv[2] = Tcl_NewStringObj("transparent", 11);

   Tcl_SetVar2Ex(xcinterp, "XCOps", "fillamount", objv[1], TCL_NAMESPACE_ONLY);
   Tcl_SetVar2(xcinterp, "XCOps", "opaque", (styleval & OPAQUE) ? "true" : "false",
		TCL_NAMESPACE_ONLY);
   Tcl_SetVar2(xcinterp, "XCOps", "bboxtype", (styleval & BBOX) ? "true" : "false",
		TCL_NAMESPACE_ONLY);
   Tcl_SetVar2(xcinterp, "XCOps", "polyclosed", (styleval & UNCLOSED) ? "false" :
		"true", TCL_NAMESPACE_ONLY);

   switch (styleval & BORDERS) {
      case DASHED:
	 bptr = borders[DashedIdx];
	 break;
      case DOTTED:
	 bptr = borders[DottedIdx];
	 break;
      case NOBORDER:
	 bptr = borders[UnborderedIdx];
	 break;
      default:
	 bptr = borders[SolidIdx];
	 break;
   }
   Tcl_SetVar2(xcinterp, "XCOps", "linestyle", (char *)bptr, TCL_NAMESPACE_ONLY);

   XcTagCallback(xcinterp, 3, objv);

#endif
}

/*--------------------------------------------------------------*/
/* Check for a bounding box polygon				*/
/*--------------------------------------------------------------*/

polyptr checkforbbox(objectptr localdata)
{
   polyptr *cbbox;

   for (cbbox = (polyptr *)localdata->plist; cbbox <
	(polyptr *)localdata->plist + localdata->parts; cbbox++)
      if (IS_POLYGON(*cbbox))
         if ((*cbbox)->style & BBOX) return *cbbox;

   return NULL;
}

/*--------------------------------------------------------------*/
/* Set a value for element style.  "Mask" determines the bits	*/
/* to be affected, so that "value" may turn bits either on or	*/
/* off.								*/
/*--------------------------------------------------------------*/

int setelementstyle(xcWidget w, u_short value, u_short mask)
{
   Boolean selected = False;
   short *sstyle;
   u_short newstyle;

   if (areastruct.selects > 0) {
      if (value & BBOX) {
	 polyptr ckp;
	 if (areastruct.selects != 1) {
	    Wprintf("Choose only one polygon to be the bounding box");
	    return -1;
	 }
	 else if (SELECTTYPE(areastruct.selectlist) != POLYGON) {
	    Wprintf("Bounding box can only be a polygon");
	    return -1;
	 }
	 else if (((ckp = checkforbbox(topobject)) != NULL) &&
		(ckp != SELTOPOLY(areastruct.selectlist))) {
	    Wprintf("Only one bounding box allowed per page");
	    return -1;
	 }
      }

      for (sstyle = areastruct.selectlist; sstyle < areastruct.selectlist
	   + areastruct.selects; sstyle++) {
	 short stype = SELECTTYPE(sstyle);
	 if (stype & (ARC | POLYGON | SPLINE | PATH)) {
	    short *estyle;
	    switch (stype) {
	       case ARC:
	          estyle = &((SELTOARC(sstyle))->style);
		  break;
	       case SPLINE:
	          estyle = &((SELTOSPLINE(sstyle))->style);
		  break;
	       case POLYGON:
	          estyle = &((SELTOPOLY(sstyle))->style);
		  break;
	       case PATH:
	          estyle = &((SELTOPATH(sstyle))->style);
		  break;
	    }
	    newstyle = *estyle;
	    newstyle &= ~(mask);
	    newstyle |= value;

	    if ((newstyle & NOBORDER) && !(newstyle & FILLED)) {
	       Wprintf("Must have either a border or filler");
	       continue;
	    }

	    XSetFunction(dpy, areastruct.gc, GXcopy);
	    XSetForeground(dpy, areastruct.gc, BACKGROUND);
	    easydraw(*sstyle, DOFORALL);

	    *estyle = newstyle;
	    if (mask & BBOX)
	       (SELTOPOLY(sstyle))->color = (value & BBOX) ? BBOXCOLOR : DEFAULTCOLOR;

	    XSetFunction(dpy, areastruct.gc, GXxor);
	    XSetForeground(dpy, areastruct.gc, SELECTCOLOR ^ BACKGROUND);
	    easydraw(*sstyle, DOFORALL);
	    selected = True;
	 }
      }
   }
   if (selected)
      pwriteback(areastruct.topinstance);
   else {
      newstyle = areastruct.style;
      if (value & BBOX) {
	 Wprintf("Cannot set default style to Bounding Box");
	 newstyle &= ~(BBOX);
	 return -1;
      }
      else {
	 newstyle &= ~mask;
	 newstyle |= value;
      }

      if ((newstyle & NOBORDER) && !(newstyle & FILLED)) {
	 Wprintf("Must have either a border or filler");
	 return -1;
      }
      areastruct.style = newstyle;
      overdrawpixmap(w);
   }
#ifndef TCL_WRAPPER
   setallstylemarks(newstyle);
   unselect_all();
#endif
   return (int)newstyle;
}

#ifndef TCL_WRAPPER
/*--------------------------------------------------------------*/
/* The following five routines are all wrappers for		*/
/* setelementstyle(),	  					*/
/* used in menudefs to differentiate between sections, each of  */
/* which has settings independent of the others.		*/
/*--------------------------------------------------------------*/

void setfill(xcWidget w, pointertype value, caddr_t calldata)
{
   setelementstyle(w, (u_short)value, OPAQUE | FILLED | FILLSOLID);
}

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

void makebbox(xcWidget w, pointertype value, caddr_t calldata)
{
   setelementstyle(w, (u_short)value, BBOX);
}

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

void setclosure(xcWidget w, pointertype value, caddr_t calldata)
{
   setelementstyle(w, (u_short)value, UNCLOSED);
}

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

void setopaque(xcWidget w, pointertype value, caddr_t calldata)
{
   setelementstyle(w, (u_short)value, OPAQUE);
}
   
#endif /* TCL_WRAPPER */

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

int setline(xcWidget w, pointertype value, caddr_t calldata)
{
   return setelementstyle(w, (u_short)value, BORDERS);
}

/*-----------------------------------------------*/
/* Set the color value for all selected elements */
/*-----------------------------------------------*/

#ifdef TCL_WRAPPER

void setcolor(xcWidget w, int cindex)
{
   short *scolor;
   int *ecolor, cval;
   Boolean selected = False;
   stringpart *strptr, *nextptr;

   if (cindex == -1) cval = -1;
   else cval = colorlist[cindex].color.pixel;

#else

void setcolor(xcWidget w, pointertype value, caddr_t calldata)
{
   short *scolor;
   int *ecolor, cindex, cval;
   Arg wargs[1];
   Boolean selected = False;
   stringpart *strptr, *nextptr;

   /* Get the color index value from the menu button widget itself */

   if (value == 1)
      cindex = cval = -1;
   else {
      XtSetArg(wargs[0], XtNrectColor, &cval);
      XtGetValues(w, wargs, 1);

      for (cindex = 0; cindex < number_colors; cindex++)
         if (colorlist[cindex].color.pixel == cval)
	    break;
      if (cindex >= number_colors) {
	 Wprintf("Error: No such color!");
	 return;
      }
   }

#endif
   
   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      labelptr curlabel = TOLABEL(EDITPART);
      strptr = findstringpart(textpos - 1, NULL, curlabel->string, areastruct.topinstance);
      nextptr = findstringpart(textpos, NULL, curlabel->string, areastruct.topinstance);
      if (strptr->type == FONT_COLOR) {
	 undrawtext(curlabel);
	 strptr->data.color = cindex;
	 redrawtext(curlabel);
      }
      else if (nextptr && nextptr->type == FONT_COLOR) {
	    undrawtext(curlabel);
	    nextptr->data.color = cindex;
	    redrawtext(curlabel);
      }
      else {
	 sprintf(_STR2, "%d", cindex);
	 labeltext(FONT_COLOR, (char *)&cindex);
      }
   }

   else if (areastruct.selects > 0) {
      for (scolor = areastruct.selectlist; scolor < areastruct.selectlist
	   + areastruct.selects; scolor++) {
	 ecolor = &(SELTOCOLOR(scolor));

	 *ecolor = cval;
	 selected = True;
      }
   }

   setcolormark(cval);
   if (!selected) {
      if (eventmode != TEXT_MODE && eventmode != ETEXT_MODE)
         areastruct.color = cval;      
      overdrawpixmap(w);
   }

#ifndef TCL_WRAPPER
   unselect_all();
#endif
}

/*----------------------------------------------------------------*/
/* Parse a new color entry and add it to the color list.	  */
/*----------------------------------------------------------------*/

void setnewcolor(xcWidget w, caddr_t nullptr)
{
   int ccolor, red, green, blue;
   char *ppos, *cpos;

   ppos = strchr(_STR2, '#');
   cpos = strchr(_STR2, ',');

   if (cpos != NULL || ppos != NULL) {
      if (cpos != NULL || strlen(ppos + 1) == 6) {
	 if (cpos != NULL)
            sscanf(_STR2, "%d, %d, %d", &red, &green, &blue);
	 else
            sscanf(ppos + 1, "%2x%2x%2x", &red, &green, &blue);
	 red *= 256;
	 green *= 256;
	 blue *= 256;
      }
      else if (sscanf(ppos + 1, "%4x%4x%4x", &red, &green, &blue) != 3) {
	 Wprintf("Bad color entry.  Use #rrggbb");
	 return;
      }
      ccolor = rgb_alloccolor(red, green, blue);
   }
   else
      ccolor = xc_alloccolor(_STR2);

   addnewcolorentry(ccolor);
}

/*----------------------------------------------------------------*/
/* Generate popup dialog for adding a new color name or RGB value */
/*----------------------------------------------------------------*/

void addnewcolor(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));

   getgeneric(savebutton, w, addnewcolor, NULL);
   popupprompt(w, "Enter color name or #rgb or r,g,b:", "\0", setnewcolor,
	savebutton, NULL);
}

/*------------------------------------------------------*/
/* Set checkmarks on label style menu 			*/
/* fvalue is for font, jvalue is for justification	*/
/*------------------------------------------------------*/

#ifdef TCL_WRAPPER

void togglestylemark(int styleval)
{
   switch(styleval) {
      case 0: Tcl_SetVar2(xcinterp, "XCOps", "fontstyle", "normal", TCL_NAMESPACE_ONLY);
	 break;
      case 1: Tcl_SetVar2(xcinterp, "XCOps", "fontstyle", "bold", TCL_NAMESPACE_ONLY);
	 break;
      case 2: Tcl_SetVar2(xcinterp, "XCOps", "fontstyle", "italic", TCL_NAMESPACE_ONLY);
	 break;
      case 3: Tcl_SetVar2(xcinterp, "XCOps", "fontstyle", "bolditalic",
		TCL_NAMESPACE_ONLY);
	 break;
   }
}

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

void toggleencodingmark(int encodingval)
{
   switch(encodingval) {
      case 0: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "Standard",
		TCL_NAMESPACE_ONLY);
	 break;
      case 1: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "special",
		TCL_NAMESPACE_ONLY);
	 break;
      case 2: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "ISOLatin1",
		TCL_NAMESPACE_ONLY);
	 break;
      case 3: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "ISOLatin2",
		TCL_NAMESPACE_ONLY);
	 break;
      case 4: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "ISOLatin3",
		TCL_NAMESPACE_ONLY);
	 break;
      case 5: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "ISOLatin4",
		TCL_NAMESPACE_ONLY);
	 break;
      case 6: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "ISOLatin5",
		TCL_NAMESPACE_ONLY);
	 break;
      case 7: Tcl_SetVar2(xcinterp, "XCOps", "fontencoding", "ISOLatin6",
		TCL_NAMESPACE_ONLY);
	 break;
   }
}

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

void togglejustmarks(int justvalue)
{
   switch(justvalue & RLJUSTFIELD) {
      case NORMAL: Tcl_SetVar2(xcinterp, "XCOps", "jhoriz", "left",
		TCL_NAMESPACE_ONLY);
	 break;
      case NOTLEFT: Tcl_SetVar2(xcinterp, "XCOps", "jhoriz", "center",
		TCL_NAMESPACE_ONLY);
	 break;
      case RIGHT|NOTLEFT: Tcl_SetVar2(xcinterp, "XCOps", "jhoriz", "right",
		TCL_NAMESPACE_ONLY);
	 break;
   }
   switch(justvalue & TBJUSTFIELD) {
      case NORMAL: Tcl_SetVar2(xcinterp, "XCOps", "jvert", "bottom",
		TCL_NAMESPACE_ONLY);
	 break;
      case NOTBOTTOM: Tcl_SetVar2(xcinterp, "XCOps", "jvert", "middle",
		TCL_NAMESPACE_ONLY);
	 break;
      case TOP|NOTBOTTOM: Tcl_SetVar2(xcinterp, "XCOps", "jvert", "top",
		TCL_NAMESPACE_ONLY);
	 break;
   }

   /* Flip Invariance property */
   Tcl_SetVar2(xcinterp, "XCOps", "flipinvariant", (justvalue & FLIPINV) ?
		"true" : "false", TCL_NAMESPACE_ONLY);

   /* LaTeX mode property */
   Tcl_SetVar2(xcinterp, "XCOps", "latexmode", (justvalue & LATEXLABEL) ?
		"true" : "false", TCL_NAMESPACE_ONLY);
     
   /* Pin visibility property */
   Tcl_SetVar2(xcinterp, "XCOps", "pinvisible", (justvalue & PINVISIBLE) ?
		"true" : "false", TCL_NAMESPACE_ONLY);
}

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

void setfontmarks(short fvalue, short jvalue)
{
   if ((fvalue >= 0) && (fontcount > 0)) {
      togglestylemark(fonts[fvalue].flags & 0x03);
      toggleencodingmark((fonts[fvalue].flags & 0xf80) >> 7);
      togglefontmark(fvalue);
   }
   if (jvalue >= 0) {
      togglejustmarks(jvalue);
   }
}

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

#else /* !TCL_WRAPPER */

void setfontmarks(short fvalue, short jvalue)
{
   xcWidget w;
   Arg wargs[1];

   if ((fvalue >= 0) && (fontcount > 0)) {
      switch(fonts[fvalue].flags & 0x03) {
         case 0: w = StyleNormalButton; break;
         case 1: w = StyleBoldButton; break;
         case 2: w = StyleItalicButton; break;
         case 3: w = StyleBoldItalicButton; break;
      }
      toggleexcl(w, FontStyles, XtNumber(FontStyles));

      switch((fonts[fvalue].flags & 0xf80) >> 7) {
         case 0: w = EncodingStandardButton; break;
         case 2: w = EncodingISOLatin1Button; break;
	 default: w = NULL;
      }
      if (w != NULL) toggleexcl(w, FontEncodings, XtNumber(FontEncodings));

      togglefontmark(fvalue);
   }
   if (jvalue >= 0) {
      switch(jvalue & (RLJUSTFIELD)) {
         case NORMAL: w = JustificationLeftJustifiedButton; break;
         case NOTLEFT: w = JustificationCenterJustifiedButton; break;
         case RIGHT|NOTLEFT: w = JustificationRightJustifiedButton; break;
      }
      toggleexcl(w, Justifs, XtNumber(Justifs));

      switch(jvalue & (TBJUSTFIELD)) {
         case NORMAL: w = JustificationBottomJustifiedButton; break;
         case NOTBOTTOM: w = JustificationMiddleJustifiedButton; break;
         case TOP|NOTBOTTOM: w = JustificationTopJustifiedButton; break;
      }

      toggleexcl(w, Justifs, XtNumber(Justifs));

      /* Flip Invariance property */
      w = JustificationFlipInvariantButton;
      if (jvalue & FLIPINV)
         XtSetArg(wargs[0], XtNsetMark, True);
      else
         XtSetArg(wargs[0], XtNsetMark, False);
      XtSetValues(w, wargs, 1);

      /* Pin visibility property */
      w = NetlistPinVisibilityButton;
      if (jvalue & PINVISIBLE)
         XtSetArg(wargs[0], XtNsetMark, True);
      else
         XtSetArg(wargs[0], XtNsetMark, False);
      XtSetValues(w, wargs, 1);
   }
}

/*----------------------------------------------*/
/* GUI wrapper for startparam()			*/
/*----------------------------------------------*/

void promptparam(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   buttonsave *popdata = (buttonsave *)malloc(sizeof(buttonsave));

   if (areastruct.selects == 0) return;  /* nothing was selected */

   /* Get a name for the new object */

   eventmode = NORMAL_MODE;
   popdata->dataptr = NULL;
   popdata->button = NULL; /* indicates that no button is assc'd w/ the popup */
   popupprompt(w, "Enter name for new parameter:", "\0", stringparam, popdata, NULL);
}

#endif /* !TCL_WRAPPER */

/*--------------------------------------------------------------*/
/* Parameterize a label string (wrapper for parameterize()).	*/
/* Assumes that the name has been generated by the popup prompt	*/
/* and is therefore held in variable _STR2			*/
/*--------------------------------------------------------------*/

void stringparam(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   genericptr *settext;

      if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
         settext = (genericptr *)EDITPART;
         makeparam(TOLABEL(settext), _STR2);
         unselect_all();
	 setparammarks(NULL);
      }
      else if (checkselect(LABEL)) parameterize(P_SUBSTRING, _STR2);
}

/*--------------------------------------------------------------*/
/* Numerical parameterization (wrapper for parameterize()).	*/
/*--------------------------------------------------------------*/

void startparam(xcWidget w, pointertype value, caddr_t calldata)
{
   genericptr *settext;

   if (value == (pointertype)P_SUBSTRING) {
      strcpy(_STR2, (calldata != NULL) ? (char *)calldata : "substring");
      stringparam(w, NULL, NULL);
   }
   else if ((eventmode != NORMAL_MODE) || (areastruct.selects > 0))
	 parameterize((int)value, (char *)calldata);
}

/*---------------------------------------------------------------*/
/* Unparameterize a label string (wrapper for unparameterize()). */
/*---------------------------------------------------------------*/

void startunparam(xcWidget w, pointertype value, caddr_t calldata)
{
   if (areastruct.selects > 0)
      unparameterize((int)value);
   unselect_all();
   setparammarks(NULL);
}

/*----------------------------------------------------------------*/
/* Set checkmarks on font menu according to the global defaults	  */
/*----------------------------------------------------------------*/

void setdefaultfontmarks()
{
   setfontmarks(areastruct.psfont, areastruct.justify);
}

/*--------------------------------------------------------------*/
/* Toggle a bit in the justification field of a label		*/
/*--------------------------------------------------------------*/

void dojustifybit(xcWidget w, labelptr settext, short bitfield)
{
   if (settext != NULL) {
      undrawtext(settext);
      settext->justify ^= bitfield;
      redrawtext(settext);
      pwriteback(areastruct.topinstance);
   }
   else
      areastruct.justify ^= bitfield;

#ifndef TCL_WRAPPER
   if (w != NULL) {
      Boolean boolval;
      if (settext)
	 boolval = (settext->justify & bitfield) ? 0 : 1;
      else
	 boolval = (areastruct.justify & bitfield) ? 0 : 1;
      toggle(w, &boolval, NULL);
   }
#endif
}

/*--------------------------------------------------------------*/
/* Toggle a pin-related bit in the justification field of a	*/
/* label.  This differs from dojustifybit() in that the label	*/
/* must be a pin, and this function cannot change the default	*/
/* behavior set by areastruct.justify.				*/
/*--------------------------------------------------------------*/

void dopinjustbit(xcWidget w, labelptr settext, short bitfield)
{

   if ((settext != NULL) && settext->pin) {
      undrawtext(settext);
      settext->justify ^= bitfield;
      redrawtext(settext);
      pwriteback(areastruct.topinstance);

#ifndef TCL_WRAPPER
      if (w != NULL) {
	 Boolean boolval = (settext->justify & bitfield) ? 0 : 1;
	 toggle(w, &boolval, NULL);
      }
#endif
   }
}

/*----------------------------------------------------------------*/
/* Set the justification for the label passed as 3rd parameter	  */
/*----------------------------------------------------------------*/

void setjust(xcWidget w, pointertype value, labelptr settext, short mode)
{
   short newjust;

   if (settext != NULL) {

      if (mode == 1)
         newjust = (settext->justify & (NONJUSTFIELD | TBJUSTFIELD))
			| value;
      else
         newjust = (settext->justify & (NONJUSTFIELD | RLJUSTFIELD))
			| value;

      undrawtext(settext);
      settext->justify = newjust;
      redrawtext(settext);
      pwriteback(areastruct.topinstance);
   }
   else {
      if (mode == 1)
         newjust = (areastruct.justify & (NONJUSTFIELD | TBJUSTFIELD))
			| value;
      else
         newjust = (areastruct.justify & (NONJUSTFIELD | RLJUSTFIELD))
			| value;
      areastruct.justify = newjust;
   }
#ifndef TCL_WRAPPER
   if (w != NULL) toggleexcl(w, Justifs, XtNumber(Justifs));
#endif
}

/*----------------------------------------------------------------*/
/* Set vertical justification (top, middle, bottom) on all	  */
/* selected labels						  */
/*----------------------------------------------------------------*/

void setvjust(xcWidget w, pointertype value, caddr_t nulldata)
{
   short *fselect;
   labelptr settext;
   short labelcount = 0;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      settext = *((labelptr *)EDITPART);
      setjust(w, value, settext, 2);
   }
   else {
      for (fselect = areastruct.selectlist; fselect < areastruct.selectlist +
            areastruct.selects; fselect++) {
         if (SELECTTYPE(fselect) == LABEL) {
            labelcount++;
            settext = SELTOLABEL(fselect);
            setjust(NULL, value, settext, 2);
         }
      }
      if (labelcount == 0) setjust(w, value, NULL, 2);
      else unselect_all();
   }
}

/*----------------------------------------------------------------*/
/* Set horizontal justification (left, center, right) on all	  */
/* selected labels						  */
/*----------------------------------------------------------------*/

void sethjust(xcWidget w, pointertype value, caddr_t nulldata)
{
   short *fselect;
   labelptr settext;
   short labelcount = 0;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      settext = *((labelptr *)EDITPART);
      setjust(w, value, settext, 1);
   }
   else {
      for (fselect = areastruct.selectlist; fselect < areastruct.selectlist +
            areastruct.selects; fselect++) {
         if (SELECTTYPE(fselect) == LABEL) {
            labelcount++;
            settext = SELTOLABEL(fselect);
            setjust(NULL, value, settext, 1);
         }
      }
      if (labelcount == 0) setjust(w, value, NULL, 1);
      else unselect_all();
   }
}

/*--------------------------------------------------------------*/
/* Set a justify field bit on all selected labels		*/ 
/* (flip invariance, latex mode, etc.)				*/
/*--------------------------------------------------------------*/

void setjustbit(xcWidget w, pointertype value, caddr_t nulldata)
{
   short *fselect;
   labelptr settext;
   short labelcount = 0;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      settext = *((labelptr *)EDITPART);
      dojustifybit(w, settext, (short)value);
   }
   else {
      for (fselect = areastruct.selectlist; fselect < areastruct.selectlist +
            areastruct.selects; fselect++) {
         if (SELECTTYPE(fselect) == LABEL) {
            labelcount++;
            settext = SELTOLABEL(fselect);
            dojustifybit(NULL, settext, (short)value);
         }
      }
      if (labelcount == 0) dojustifybit(w, NULL, (short)value);
      else unselect_all();
   }
}

/*--------------------------------------------------------------*/
/* Set pin-related bit field of "justify" on all selected pins	*/ 
/* (e.g., pin visibility)					*/
/*--------------------------------------------------------------*/

void setpinjustbit(xcWidget w, pointertype value, caddr_t nulldata)
{
   short *fselect;
   labelptr settext;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      settext = *((labelptr *)EDITPART);
      if (settext->pin)
	 dopinjustbit(w, settext, (short)value);
   }
   else {
      for (fselect = areastruct.selectlist; fselect < areastruct.selectlist +
            areastruct.selects; fselect++) {
         if (SELECTTYPE(fselect) == LABEL) {
            settext = SELTOLABEL(fselect);
	    if (settext->pin)
               dopinjustbit(NULL, settext, (short)value);
         }
      }
      unselect_all();
   }
}

/*---------------------------*/
/* Set polygon editing style */
/*---------------------------*/

#ifdef TCL_WRAPPER

void boxedit(xcWidget w, pointertype value, caddr_t nulldata)
{
   switch (value) {
      case MANHATTAN:
	 Tcl_SetVar2(xcinterp, "XCOps", "polyedittype", "manhattan", TCL_NAMESPACE_ONLY);
	 break;
      case RHOMBOIDX:
	 Tcl_SetVar2(xcinterp, "XCOps", "polyedittype", "rhomboidx", TCL_NAMESPACE_ONLY);
	 break;
      case RHOMBOIDY:
	 Tcl_SetVar2(xcinterp, "XCOps", "polyedittype", "rhomboidy", TCL_NAMESPACE_ONLY);
	 break;
      case RHOMBOIDA:
	 Tcl_SetVar2(xcinterp, "XCOps", "polyedittype", "rhomboida", TCL_NAMESPACE_ONLY);
	 break;
      case NORMAL:
	 Tcl_SetVar2(xcinterp, "XCOps", "polyedittype", "normal", TCL_NAMESPACE_ONLY);
	 break;
   }

   if (areastruct.boxedit == value) return;
   areastruct.boxedit = value;
}

#else

void boxedit(xcWidget w, pointertype value, caddr_t nulldata)
{
   if (w == NULL) {
      switch (value) {
         case MANHATTAN: w = PolygonEditManhattanBoxEditButton; break;
         case RHOMBOIDX: w = PolygonEditRhomboidXButton; break;
	 case RHOMBOIDY: w = PolygonEditRhomboidYButton; break;
	 case RHOMBOIDA: w = PolygonEditRhomboidAButton; break;
	 case NORMAL: w = PolygonEditNormalButton; break;
      }
   }

   if (areastruct.boxedit == value) return;

   toggleexcl(w, BoxEditStyles, XtNumber(BoxEditStyles));
   areastruct.boxedit = value;
}

#endif

/*----------------------------------------------------------------*/
/* Pick up font name from _STR2 and pass it to loadfontfile()	  */
/*----------------------------------------------------------------*/

void locloadfont(xcWidget w, char *value)
{
   loadfontfile(_STR2);
   free(value);
}

/*----------------------------------------------------*/
/* Generate popup dialog for entering a new font name */
/*----------------------------------------------------*/

void addnewfont(xcWidget w, caddr_t clientdata, caddr_t calldata)
{
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   char *tempstr = malloc(2 * sizeof(char));
   tempstr[0] = '\0';
   getgeneric(savebutton, w, addnewfont, tempstr);
   popupprompt(w, "Enter font name:", tempstr, locloadfont, savebutton, NULL);
}

/*----------------------------------------------------------------*/
/* Find the best matching font given new font, style, or encoding */
/*----------------------------------------------------------------*/
/* newfont, style, or encoding = -1 when not applicable		  */
/* Return the number of the best matching font.			  */  
/*----------------------------------------------------------------*/

short findbestfont(short curfont, short newfont, short style, short encoding) {

   char *newfamily;
   short i, newstyle, newenc;

   if (fontcount == 0) return -1;

   if (newfont < 0)
      newfamily = fonts[curfont].family;
   else if (newfont >= fontcount) {	/* move to next font family */
      short newidx;
      newfont = 0;
      while (strcmp(fonts[fontnumbers[newfont]].family, fonts[curfont].family))
	 newfont++;
      newidx = (newfont + 1) % nfontnumbers;
      while (!strcmp(fonts[curfont].family, fonts[fontnumbers[newidx]].family) &&
		newfont != newidx)
	 newidx = (newidx + 1) % nfontnumbers;
      newfamily = fonts[fontnumbers[newidx]].family;
      newfont = fontnumbers[newidx];
   }
   else
      newfamily = fonts[newfont].family;

   if (style < 0) 
      newstyle = fonts[curfont].flags & 0x03;
   else
      newstyle = style & 0x03;
 
   if (encoding < 0)
      newenc = fonts[curfont].flags & 0xf80;
   else
      newenc = encoding << 7;

   /* Best position is a match on all conditions */

   for (i = 0; i < fontcount; i++)
      if ((!strcmp(fonts[i].family, newfamily)) &&
		((fonts[i].flags & 0x03) == newstyle) &&
	 	((fonts[i].flags & 0xf80) == newenc))
	 return i;

   /* Fallback position 1:  Match requested property and one other. 	*/
   /* order of preference:  					   	*/
   /*   Requested property	Priority 1	Priority 2		*/
   /*		Font		  Style		  Encoding		*/
   /*		Style		  Font		  (none)		*/
   /*		Encoding	  Font		  (none)		*/

   for (i = 0; i < fontcount; i++) {
      if (newfont >= 0) {
	 if ((!strcmp(fonts[i].family, newfamily)) &&
		(fonts[i].flags & 0x03) == newstyle) return i;
      }
      else if (style >= 0) {
	 if (((fonts[i].flags & 0x03) == newstyle) &&
	 	(!strcmp(fonts[i].family, newfamily))) return i;
      }
      else if (encoding >= 0) {
	 if (((fonts[i].flags & 0xf80) == newenc) &&
	 	(!strcmp(fonts[i].family, newfamily))) return i;
      }
   }

   for (i = 0; i < fontcount; i++) {
      if (newfont >= 0) {
	 if ((!strcmp(fonts[i].family, newfamily)) &&
	    ((fonts[i].flags & 0xf80) >> 7) == newenc) return i;
      }
   }

   /* Fallback position 2:  Match only the requested property.  */
   /* For font selection only:  Don't want to select a new font */
   /* just because a certain style or encoding wasn't available.*/

   for (i = 0; i < fontcount; i++) {
      if (newfont >= 0) {
	 if (!strcmp(fonts[i].family, newfamily)) return i;
      }
   }      

   /* Failure to find matching font property */

   if (style >= 0) {
      sprintf(_STR, "Font %s not available in this style", newfamily);
   }
   else {
      sprintf(_STR, "Font %s not available in this encoding", newfamily);
   }
   Wprintf(_STR);
   return (-1);
}

/*----------------------------------------------------------------*/
/* Set the font.  This depends on the system state as to whether  */
/* font is set at current position in label, for an entire label, */
/* or as the default font to begin new labels.			  */
/*----------------------------------------------------------------*/

void setfontval(xcWidget w, pointertype value, labelptr settext)
{
   int newfont;
   short i, tc;
   stringpart *strptr;

   if (settext != NULL) {

      /* if last byte was a font designator, use it */

      if (textpos > 0 || textpos < stringlength(settext->string, True, areastruct.topinstance)) {
	 strptr = findstringpart(textpos - 1, NULL, settext->string, areastruct.topinstance);
	 if (strptr->type == FONT_NAME) {
	    tc = strptr->data.font;
	    i = findbestfont(tc, (short)value, -1, -1);
	    if (i >= 0) {
	       undrawtext(settext);
	       strptr->data.font = i;
	       redrawtext(settext);
	       if (w != NULL) {
	          charreport(settext);
      		  togglefontmark(i);
	       }
	    }
	    return;
         }
      }

      /* otherwise, look for the last style used in the string */
      tc = findcurfont(textpos, settext->string, areastruct.topinstance);
   }
   else tc = areastruct.psfont;

   /* Font change command will always find a value since at least one	*/
   /* font has to exist for the font menu button to exist.		*/

   if ((newfont = (int)findbestfont(tc, (short)value, -1, -1)) < 0) return;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      sprintf(_STR, "Font is now %s", fonts[newfont].psname);
      sprintf(_STR2, "%d", newfont);
      labeltext(FONT_NAME, (char *)&newfont);
   }
   else {
      sprintf(_STR, "Default font is now %s", fonts[newfont].psname);
      areastruct.psfont = newfont;
   }
   Wprintf(_STR);

   if (w != NULL) togglefontmark(newfont);
}

/*----------------------------------------------------------------*/
/* Wrapper for routine setfontval()				  */
/*----------------------------------------------------------------*/

void setfont(xcWidget w, pointertype value, caddr_t calldata)
{
   short *fselect;
   labelptr settext;
   short labelcount = 0;

   if (eventmode == CATALOG_MODE || eventmode == FONTCAT_MODE ||
		eventmode == EFONTCAT_MODE) return;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      settext = *((labelptr *)EDITPART);
      setfontval(w, value, settext);
      charreport(settext);
   }
   else {
      textpos = 1;
      for (fselect = areastruct.selectlist; fselect < areastruct.selectlist +
	    areastruct.selects; fselect++) {
         if (SELECTTYPE(fselect) == LABEL) {
	    labelcount++;
	    settext = SELTOLABEL(fselect);
            setfontval(NULL, value, settext);
         }
      }
      if (labelcount == 0) setfontval(w, value, NULL);
      else unselect_all();
   }
}

/*----------------------------------------------------------------*/
/* Set the style (bold, italic, normal) for the font, similarly   */
/* to the above routine setfontval().				  */
/*----------------------------------------------------------------*/

void setfontstyle(xcWidget w, pointertype value, labelptr settext)
{
   int newfont;
   short i, tc;
   stringpart *strptr;

   if (settext != NULL) {

      /* if last byte was a font designator, use it */

      if (textpos > 0 || textpos < stringlength(settext->string, True, areastruct.topinstance)) {
	 strptr = findstringpart(textpos - 1, NULL, settext->string, areastruct.topinstance);
	 if (strptr->type == FONT_NAME) {
	    tc = strptr->data.font;

	    /* find font which matches family and style, if available */

	    i = findbestfont(tc, -1, (short)value, -1);
	    if (i >= 0) {
	       undrawtext(settext);
	       strptr->data.font = i;
	       redrawtext(settext);
	       if (w != NULL) {
	          charreport(settext);
#ifndef TCL_WRAPPER
	          toggleexcl(w, FontStyles, XtNumber(FontStyles));
#endif
	       }
	    }
            return;
         }
      }

      /* otherwise, look for the last font used in the string */
      tc = findcurfont(textpos - 2, settext->string, areastruct.topinstance);
   }
   else tc = areastruct.psfont;

   if ((newfont = (int)findbestfont(tc, -1, (short)value, -1)) < 0) return;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      sprintf(_STR, "Font is now %s", fonts[newfont].psname);
      sprintf(_STR2, "%d", newfont);
      labeltext(FONT_NAME, (char *)&newfont);
   }
   else {
      sprintf(_STR, "Default font is now %s", fonts[newfont].psname);
      areastruct.psfont = newfont;
   }
#ifdef TCL_WRAPPER
   toggleencodingmark(value);
#else
   if (w != NULL) toggleexcl(w, FontStyles, XtNumber(FontStyles));
#endif

   Wprintf(_STR);
}

/*----------------------------------------------------------------*/
/* Wrapper for routine setfontstyle()				  */
/*----------------------------------------------------------------*/

void fontstyle(xcWidget w, pointertype value, caddr_t nulldata)
{
   short *fselect;
   labelptr settext;
   short labelcount = 0;

   if (eventmode == CATALOG_MODE || eventmode == FONTCAT_MODE ||
		eventmode == EFONTCAT_MODE) return;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      settext = *((labelptr *)EDITPART);
      setfontstyle(w, value, settext);
      charreport(settext);
   }
   else {
      textpos = 1;
      for (fselect = areastruct.selectlist; fselect < areastruct.selectlist +
	    areastruct.selects; fselect++) {
         if (SELECTTYPE(fselect) == LABEL) {
	    labelcount++;
	    settext = SELTOLABEL(fselect);
            setfontstyle(NULL, value, settext);
         }
      }
      if (labelcount == 0) setfontstyle(w, value, NULL);
      else unselect_all();
   }
}

/*----------------------------------------------------------------*/
/* Set the encoding (standard, ISO-Latin1, special) for the font, */
/* similarly to the above routine setfontval().			  */
/*----------------------------------------------------------------*/

void setfontencoding(xcWidget w, pointertype value, labelptr settext)
{
   int newfont;
   short i, tc;
   stringpart *strptr;

   if (settext != NULL) {

      /* if last byte was a font designator, use it */

      if (textpos > 0 || textpos < stringlength(settext->string, True, areastruct.topinstance)) {
	 strptr = findstringpart(textpos - 1, NULL, settext->string, areastruct.topinstance);
	 if (strptr->type == FONT_NAME) {
	    tc = strptr->data.font;

	    i = findbestfont(tc, -1, -1, (short)value);
	    if (i >= 0) {
	       undrawtext(settext);
	       strptr->data.font = i;
	       redrawtext(settext);
	       if (w != NULL) {
	          charreport(settext);
#ifdef TCL_WRAPPER
		  toggleencodingmark(value);
#else
   	          toggleexcl(w, FontEncodings, XtNumber(FontEncodings));
#endif
	       }
	    }
	    return;
	 }
      }

      /* otherwise, look for the last style used in the string */
      tc = findcurfont(textpos - 2, settext->string, areastruct.topinstance);
   }
   else tc = areastruct.psfont;

   if ((newfont = (int)findbestfont(tc, -1, -1, (short)value)) < 0) return;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      sprintf(_STR, "Font is now %s", fonts[newfont].psname);
      sprintf(_STR2, "%d", newfont);
      labeltext(FONT_NAME, (char *)&newfont);
   }
   else {
      sprintf(_STR, "Default font is now %s", fonts[newfont].psname);
      areastruct.psfont = newfont;
   }

#ifndef TCL_WRAPPER
   if (w != NULL) toggleexcl(w, FontEncodings, XtNumber(FontEncodings));
#endif

   Wprintf(_STR);
}

/*----------------------------------------------------------------*/
/* Wrapper for routine setfontencoding()			  */
/*----------------------------------------------------------------*/

void fontencoding(xcWidget w, pointertype value, caddr_t nulldata)
{
   short *fselect;
   labelptr settext;
   short labelcount = 0;

   if (eventmode == CATALOG_MODE || eventmode == FONTCAT_MODE ||
		eventmode == EFONTCAT_MODE) return;

   if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
      settext = *((labelptr *)EDITPART);
      setfontencoding(w, value, settext);
      charreport(settext);
   }
   else {
      textpos = 1;
      for (fselect = areastruct.selectlist; fselect < areastruct.selectlist +
	    areastruct.selects; fselect++) {
         if (SELECTTYPE(fselect) == LABEL) {
	    labelcount++;
	    settext = SELTOLABEL(fselect);
            setfontencoding(NULL, value, settext);
         }
      }
      if (labelcount == 0) setfontencoding(w, value, NULL);
      else unselect_all();
   }
}

/*----------------------------------------------*/
/* Generate the table of special characters	*/
/*						*/
/* Return FALSE if the edited label is LaTeX-	*/
/* compatible, since LaTeX text does not	*/
/* usually contain special characters, but it	*/
/* does contain a lot of backslashes.		*/
/*----------------------------------------------*/

Boolean dospecial()
{
   labelptr curlabel;
   int cfont;

   curlabel = TOLABEL(EDITPART);
   if (curlabel->justify & LATEXLABEL) return False;

   cfont = findcurfont(textpos, curlabel->string, areastruct.topinstance);
   composefontlib(cfont);
   startcatalog(NULL, FONTLIB, NULL);
   return True;
}

/*-------------------------------------------------*/
/* Wrapper for labeltext when called from the menu */
/*-------------------------------------------------*/

void addtotext(xcWidget w, pointertype value, caddr_t nulldata)
{
   if (eventmode != TEXT_MODE && eventmode != ETEXT_MODE) return;
   if (value == (pointertype)SPECIAL)
      dospecial();
   else
      labeltext((int)value, (char *)1);
}

/*----------------------------------------------------------*/
/* Position a popup menu directly beside the toolbar button */
/*----------------------------------------------------------*/

void position_popup(xcWidget toolbutton, xcWidget menubutton)
{
   int n = 0;
#ifndef TCL_WRAPPER
   Arg wargs[2];
   Position pz, pw, ph;
#endif
   int dx, dy;

#ifndef TCL_WRAPPER
   xcWidget cascade = XtParent(menubutton);
   xcWidget pshell = XtParent(XtParent(cascade));

   XtnSetArg(XtNheight, &pz);
   XtGetValues(toolbutton, wargs, n); n = 0;

   XtnSetArg(XtNwidth, &pw);
   XtnSetArg(XtNheight, &ph);
   XtGetValues(cascade, wargs, n); n = 0;

   dx = -pw - 6;
   dy = (pz - ph) >> 1;

   XwPostPopup(pshell, cascade, toolbutton, dx, dy);
#endif
}

/*------------------------------------------------------*/
/* Functions which pop up a menu cascade in sticky mode */
/*------------------------------------------------------*/

void border_popup(xcWidget w, caddr_t clientdata, caddr_t calldata)
{ 
#ifndef TCL_WRAPPER
   position_popup(w, BorderLinewidthButton);
#endif
}

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

void color_popup(xcWidget w, caddr_t clientdata, caddr_t calldata)
{ 
#ifndef TCL_WRAPPER
   position_popup(w, ColorAddNewColorButton);
#endif
}

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

void fill_popup(xcWidget w, caddr_t clientdata, caddr_t calldata)
{ 
#ifndef TCL_WRAPPER
   position_popup(w, FillOpaqueButton);
#endif
}

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

void param_popup(xcWidget w, caddr_t clientdata, caddr_t calldata)
{ 
#ifndef TCL_WRAPPER
   position_popup(w, ParametersSubstringButton);
#endif
}

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