Logo Search packages:      
Sourcecode: xcircuit version File versions

text.c

/*-----------------------------------------------------------------------*/
/* text.c --- text processing routines for xcircuit                */
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University             */
/*-----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>   /* for isprint() */

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

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

#ifdef TCL_WRAPPER 
#include <tk.h>
#endif

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

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

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

extern Display *dpy;
extern Clientdata areastruct;
extern Globaldata xobjs;
extern short textpos, textend;
extern short fontcount;
extern fontinfo *fonts;
extern short eventmode;
extern int *appcolors;
extern colorindex *colorlist;
extern char _STR[150];

/* Global value of distance between characters in the font catalog */
short del;

/*----------------------------------------------------------------------*/
/* Determine if a label contains a parameter.                     */
/*----------------------------------------------------------------------*/

Boolean hasparameter(labelptr curlabel)
{
   stringpart *chrptr;

   for (chrptr = curlabel->string; chrptr != NULL; chrptr = chrptr->nextpart)
      if (chrptr->type == PARAM_START)
         return True;

   return False;
}

/*----------------------------------------------------------------------*/
/* Join selected labels together.                           */
/*----------------------------------------------------------------------*/

void joinlabels()
{
   genericptr *genobj;
   short *jl;
   stringpart *endpart;
   int tpos;
   labelptr dest, source;

   if (areastruct.selects < 2) {
      Wprintf("Not enough labels selected for joining");
      return;
   }

   XcSetFunction(GXcopy);
   XSetForeground(dpy, areastruct.gc, BACKGROUND);

   for (jl = areastruct.selectlist; jl < areastruct.selectlist +
            areastruct.selects; jl++) {
      if (SELECTTYPE(jl) == LABEL) {
       dest = SELTOLABEL(jl);
       UDrawString(dest, DOFORALL, areastruct.topinstance);
       for (endpart = dest->string; endpart->nextpart != NULL; endpart =
                  endpart->nextpart);
       break;
      }
   }
      
   for (++jl; jl < areastruct.selectlist + areastruct.selects; jl++) {
      if (SELECTTYPE(jl) == LABEL) {
       source = SELTOLABEL(jl);
       UDrawString(source, DOFORALL, areastruct.topinstance);
       endpart->nextpart = source->string;
       for (; endpart->nextpart != NULL; endpart = endpart->nextpart);
       free(source);
       removep(jl, 0);
         reviseselect(jl);
      }
   }

   XSetForeground(dpy, areastruct.gc, dest->color);
   UDrawString(dest, dest->color, areastruct.topinstance);

   incr_changes(topobject);
   clearselects();
}

/*----------------------------------------------------------------------*/
/* Insert a new segment into a string                             */
/*----------------------------------------------------------------------*/

stringpart *makesegment(stringpart **strhead, stringpart *before)
{
   stringpart *newptr, *lastptr, *nextptr;

   newptr = (stringpart *)malloc(sizeof(stringpart));
   newptr->data.string = NULL;

   if (before == *strhead) {  /* insert at beginning */
      newptr->nextpart = *strhead;
      *strhead = newptr;
   }
   else {                     /* otherwise */
      for(lastptr = *strhead; lastptr != NULL;) {
       nextptr = nextstringpart(lastptr, areastruct.topinstance);
       if (nextptr == before) {
          if (lastptr->type == PARAM_START) {
             oparamptr obs = NULL;
             int paramno = lastptr->data.paramno;
             if (areastruct.topinstance == NULL) {
              if (topobject->params != NULL)
                 obs = topobject->params[paramno];
              else {
                 Wprintf("Error:  Bad parameter!");
                 break;
              }
             }
             else {
                if (areastruct.topinstance->params != NULL)
                   obs = areastruct.topinstance->params[paramno];
                if (obs == NULL)
                 obs = areastruct.topinstance->thisobject->params[paramno];
             }
             if (obs == NULL) {
              Wprintf("Error:  Bad parameter");
              return NULL;
             }
             obs->parameter.string = newptr;
          }
          else {
             lastptr->nextpart = newptr;
          }
          newptr->nextpart = nextptr;
          break;
         }
       else if (lastptr->nextpart == before && lastptr->type == PARAM_START) {
          lastptr->nextpart = newptr;
          newptr->nextpart = before;
          break;
       }
       lastptr = nextptr;
      }
   }
   return newptr;
}

/*----------------------------------------------------------------------*/
/* Split a string across text segments                            */
/*----------------------------------------------------------------------*/

stringpart *splitstring(int tpos, stringpart **strtop, objinstptr localinst)
{
   int locpos, slen;
   stringpart *newpart, *ipart;

   ipart = findstringpart(tpos, &locpos, *strtop, localinst);
   if (locpos > 0) {        /* split the string */
      newpart = makesegment(strtop, ipart);
      newpart->type = TEXT_STRING;
      newpart->data.string = ipart->data.string;
      slen = strlen(newpart->data.string) - locpos;
      ipart->data.string = (u_char *)malloc(slen + 1);
      strncpy(ipart->data.string, newpart->data.string + locpos, slen  + 1);
      *(newpart->data.string + locpos) = '\0';
   }
   else newpart = ipart;

   return newpart;
}

/*----------------------------------------------------------------------*/
/* Get the next string part, linking to a paramter if necessary         */
/*----------------------------------------------------------------------*/

stringpart *nextstringpart(stringpart *strptr, objinstptr thisinst)
{
   stringpart *nextptr = strptr->nextpart;

   if (strptr->type == PARAM_START)
      nextptr = linkstring(thisinst, strptr);
   else if (strptr->type == PARAM_END)
      strptr->nextpart = NULL;

   return nextptr;
}


/*----------------------------------------------------------------------*/
/* Remove a string part from the string                           */
/*----------------------------------------------------------------------*/

stringpart *deletestring(stringpart *dstr, stringpart **strtop, objinstptr thisinst)
{
   stringpart *strptr, *nextptr;
   int paramno;

   if (dstr == *strtop)
      *strtop = dstr->nextpart;
   else {
      strptr = *strtop;
      while (strptr != NULL) {
       nextptr = nextstringpart(strptr, thisinst);
       if (nextptr == dstr) break;
       strptr = nextptr;
      }
      if (strptr == NULL)
       return NULL;

      /* If this is the begining of a parameter, then we have to figure */
      /* out if it's an instance or a default, and change the pointer   */
      /* to the parameter in the parameter list, accordingly.           */

      else if (strptr->type == PARAM_START) {
       paramno = strptr->data.paramno;
       if (thisinst == NULL)
       {
          if (topobject->params != NULL)
             topobject->params[paramno]->parameter.string = dstr->nextpart;
          else
             Fprintf(stderr, "Error in deletestring:  Bad parameter number found\n"); 
       }
       else if (thisinst->params == NULL || thisinst->params[paramno] == NULL)
          thisinst->thisobject->params[paramno]->parameter.string =
                  dstr->nextpart;
       else
          thisinst->params[paramno]->parameter.string = dstr->nextpart;
      }
      /* If this is the end of a parameter, we have to link the         */
      /* PARAM_START, not the PARAM_END, which has already been nulled. */ 
      else if (strptr->type == PARAM_END) {
       for (strptr = *strtop; strptr != NULL; strptr = strptr->nextpart) {
          if (strptr->nextpart == dstr) {
             strptr->nextpart = dstr->nextpart;
             break;
          }
         }
      }
      else
       strptr->nextpart = dstr->nextpart;
   }
   if (dstr->type == TEXT_STRING) free(dstr->data.string);
   free(dstr);

   /* attempt to merge, if legal */
   mergestring(strptr);

   return strptr;
}

/*----------------------------------------------------------------------*/
/* Merge string parts at boundary, if parts can be legally merged */
/* If the indicated string part is text and the part following the      */
/* indicated string part is also text, merge the two.  The indicated    */
/* string part is returned, and the following part is freed.            */
/*----------------------------------------------------------------------*/

stringpart *mergestring(stringpart *firststr)
{
   stringpart *nextstr = firststr->nextpart;
   if (nextstr != NULL) {
      if (firststr->type == TEXT_STRING && nextstr->type == TEXT_STRING) {
         firststr->nextpart = nextstr->nextpart;
         firststr->data.string = (char *)realloc(firststr->data.string,
            1 + strlen(firststr->data.string) + strlen(nextstr->data.string));
         strcat(firststr->data.string, nextstr->data.string);
         free(nextstr->data.string);
         free(nextstr);
         return firststr;
      }
   }
   return NULL;
}

/*----------------------------------------------------------------------*/
/* Link a parameter to a string                                   */
/*----------------------------------------------------------------------*/

stringpart *linkstring(objinstptr localinst, stringpart *strstart)
{
   int paramno;
   stringpart *nextptr = NULL, *tmpptr;

   if (strstart->type != PARAM_START) return NULL;

   paramno = strstart->data.paramno;

   /* In case of no calling instance, always get the default from the   */
   /* current page object.                                  */

   if (localinst == NULL) {
      if (topobject->num_params > paramno) {
         if (topobject->params[paramno]->type != XC_STRING)
          goto linkerror;
       else
            nextptr = topobject->params[paramno]->parameter.string;
      }
      else
       return NULL;  /* There is no parameter here. */
   }
   else {
      if (localinst->params != NULL) {
       if (localinst->params[paramno] != NULL) {
          if (localinst->params[paramno]->type != XC_STRING)
             goto linkerror;
            nextptr = localinst->params[paramno]->parameter.string;
       }
      }

      /* If there's no instance, substitute the default parameter */

      if (nextptr == NULL) {
       if (localinst->thisobject->params != NULL) {
          if (localinst->thisobject->num_params > paramno) {
             if (localinst->thisobject->params[paramno]->type != XC_STRING)
                goto linkerror;
               nextptr = localinst->thisobject->params[paramno]->parameter.string;
          }
          else
             return NULL;     /* Error:  There is no parameter here! */
       }
       else
          /* We get here in cases where the object definition is being read,  */
          /* and there is no instance of the object to link to.  In that      */
          /* case, we ignore parameters and move on to the next part.         */
          return strstart->nextpart;
      }
   }

   /* If the parameter exists, link the end of the parameter back to    */
   /* the calling string.                                   */

   if (nextptr != NULL) {
      tmpptr = nextptr;
      while (tmpptr->type != PARAM_END)
       if ((tmpptr = tmpptr->nextpart) == NULL)
          return NULL;
      tmpptr->nextpart = strstart->nextpart;
      return nextptr;
   }
   return NULL;

linkerror:
   Fprintf(stderr, "linkstring() error: parameter %d is not a substring!\n",
            paramno);
   return strstart->nextpart;
}

/*----------------------------------------------------------------------*/
/* Find the last font used prior to the indicated text position         */
/*----------------------------------------------------------------------*/

int findcurfont(int tpos, stringpart *strtop, objinstptr thisinst)
{
   stringpart *curpos;
   int cfont = -1;
   stringpart *strptr;

   curpos = findstringpart(tpos, NULL, strtop, thisinst);
   for (strptr = strtop; strptr != curpos; strptr = nextstringpart(strptr, thisinst))
      if (strptr->type == FONT_NAME)
         cfont = strptr->data.font;

   return cfont;
}

/*----------------------------------------------------------------------*/
/* Return a local position and stringpart for "tpos" positions into     */
/* the indicated string.  Position and stringpart are for the character */
/* or command immediately preceding "tpos"                        */
/*----------------------------------------------------------------------*/

stringpart *findstringpart(int tpos, int *locpos, stringpart *strtop,
      objinstptr localinst)
{
   stringpart *strptr = strtop;
   int testpos = 0, tmplen;

   for (strptr = strtop; strptr != NULL; strptr = nextstringpart(strptr, localinst)) {
      if ((strptr->type == TEXT_STRING) && strptr->data.string) {
       tmplen = strlen(strptr->data.string);
       if (testpos + tmplen > tpos) {
          if (locpos != NULL) *locpos = (tpos - testpos);
          return strptr;
       }
       else testpos += tmplen - 1;
      }
      if (locpos != NULL) *locpos = -1;
      if (testpos >= tpos) return strptr;

      testpos++;
   }
   return NULL;
}

/*----------------------------------------------------------------------*/
/* The following must be in an order matching the "Text string part     */
/* types" defined in xcircuit.h.                            */
/*----------------------------------------------------------------------*/

static char *nonprint[] = {
      "Text", "Subscript", "Superscript", "Normalscript",
      "Underline", "Overline", "Noline",
      "Tab_Stop", "Tab_Forward", "Tab_Backward",
      "Halfspace", "Quarterspace", "<Return>",
      "Font", "Scale", "Color", "Kern", 
        "Parameter", ">", "Error"};

/*----------------------------------------------------------------------*/
/* charprint():                                       */
/* Write a printable version of the character or command at the   */
/* indicated string part and position.                            */
/*----------------------------------------------------------------------*/

void charprint(char *sout, stringpart *strptr, int locpos)
{
   char sc;
 
   switch (strptr->type) {
      case TEXT_STRING:
       if (strptr->data.string) {
            if (locpos > strlen(strptr->data.string)) {
             strcpy(sout, "<ERROR>");
          }
            else sc = *(strptr->data.string + locpos);
            if (isprint(sc))
             sprintf(sout, "%c", sc);
            else
             sprintf(sout, "/%03o", (u_char)sc);
       }
       else
          *sout = '\0';
       break;
      case FONT_NAME:
       sprintf(sout, "Font=%s", fonts[strptr->data.font].psname);
       break;
      case FONT_SCALE:
       sprintf(sout, "Scale=%3.2f", strptr->data.scale);
       break;
      case KERN:
       sprintf(sout, "Kern=(%d,%d)", strptr->data.kern[0], strptr->data.kern[1]);
       break;
      case PARAM_START:
       sprintf(sout, "Parameter(%d)<", strptr->data.paramno + 1);
       break;
      default:
         strcpy(sout, nonprint[strptr->type]);
       break;
   }
}

/*----------------------------------------------------------------------*/
/* Print a string (allocates memory for the string; must be freed by    */
/* the calling routine).                                    */
/*----------------------------------------------------------------------*/

char *xcstringtostring(stringpart *strtop, objinstptr localinst, Boolean textonly)
{
   stringpart *strptr;
   int pos = 0, locpos;
   char *sout;

   sout = (char *)malloc(1);
   sout[0] = '\0';

   while ((strptr = findstringpart(pos++, &locpos, strtop, localinst)) != NULL) {
      if (!textonly || strptr->type == TEXT_STRING) {
         charprint(_STR, strptr, locpos);
         sout = (char *)realloc(sout, strlen(sout) + strlen(_STR) + 1);
         strcat(sout, _STR);
      }
      /* Overbar on schematic names is translated to logical-NOT ("!") */
      else if (textonly && strptr->type == OVERLINE) {
         sout = (char *)realloc(sout, strlen(sout) + 2);
         strcat(sout, "!");
      }
   }
   return sout;
}

/*----------------------------------------------------------------------*/
/* Wrappers for xcstringtostring():                         */
/*  stringprint() includes information on text controls appropriate     */
/*  for printing in the message window (charreport())             */
/*----------------------------------------------------------------------*/

char *stringprint(stringpart *strtop, objinstptr localinst)
{
   return xcstringtostring(strtop, localinst, False);
}

/*----------------------------------------------------------------------*/
/*  textprint() excludes text controls, resulting in a string           */
/*  appropriate for netlist information.                    */
/*----------------------------------------------------------------------*/

char *textprint(stringpart *strtop, objinstptr localinst)
{
   return xcstringtostring(strtop, localinst, True);
}

/*----------------------------------------------------------------------*/
/* Test equivalence of the text string parts of a label with the  */
/* indicated char * string.                                 */
/*    If "exact" is True, requires an exact match, otherwise requires   */
/*    that the label only match text to the length of text.       */
/*----------------------------------------------------------------------*/

int textcompx(stringpart *string, char *text, Boolean exact, objinstptr localinst)
{
   stringpart *strptr;
   char *tptr = text;
   char *sptr;
   int rval, llen = strlen(text), slen;

   for (strptr = string; strptr != NULL; strptr = nextstringpart(strptr, localinst)) {
      if (strptr->type == TEXT_STRING) {
       sptr = strptr->data.string;
       slen = min(strlen(sptr), llen);
       llen -= slen;
       if (!exact && (rval = strncmp(sptr, tptr, slen)))
          return rval;
       else if (exact && (rval = strcmp(sptr, tptr)))
          return rval;
       else if (!exact && (llen == 0))
          return 0;
       else
          tptr += slen;
      }
   }
   return 0;
}

/*----------------------------------------------------------------------*/
/* Wrappers for textcompx(), equivalent to strcmp() and strncmp().      */
/*----------------------------------------------------------------------*/

int textcomp(stringpart *string, char *text, objinstptr localinst)
{
   return textcompx(string, text, True, localinst);
}

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

int textncomp(stringpart *string, char *text, objinstptr localinst)
{
   return textcompx(string, text, False, localinst);
}

/*----------------------------------------------------------------------*/
/* Test equivalence of two label strings                    */
/*----------------------------------------------------------------------*/

int stringcomp(stringpart *string1, stringpart *string2)
{
   stringpart *strptr1, *strptr2;

   for (strptr1 = string1, strptr2 = string2; strptr1 != NULL && strptr2 != NULL;
            strptr1 = strptr1->nextpart, strptr2 = strptr2->nextpart) {
      if (strptr1->type != strptr2->type)
       return 1;
      else {
         switch (strptr1->type) {
          case TEXT_STRING:
             if (strptr1->data.string && strptr2->data.string) {
                if (strcmp(strptr1->data.string, strptr2->data.string))
                 return 1;
             }
             else if (strptr1->data.string || strptr2->data.string)
              return 1;
             break;
          case FONT_SCALE:
             if (strptr1->data.scale != strptr2->data.scale) return 1;
             break;
          case FONT_COLOR:
             if (strptr1->data.color != strptr2->data.color) return 1;
             break;
          case FONT_NAME:
             if (strptr1->data.font != strptr2->data.font) return 1;
             break;
          case KERN:
             if (strptr1->data.kern[0] != strptr2->data.kern[0] ||
               strptr1->data.kern[1] != strptr2->data.kern[1]) return 1;
             break;
       }
      }
   }

   /* One string continues after the other ends. . . */ 
   if (strptr1 != NULL || strptr2 != NULL) return 1;
   return 0;
}

/*----------------------------------------------------------------------*/
/* Test if the specified font is in the "Symbol" font family.           */
/*----------------------------------------------------------------------*/

Boolean issymbolfont(int fontnumber)
{
   if (!strcmp(fonts[fontnumber].family, "Symbol")) return True;
   return False;
}

/*----------------------------------------------------------------------*/
/* Test if the specified font is ISO-Latin1 encoding              */
/*----------------------------------------------------------------------*/

Boolean isisolatin1(int fontnumber)
{
   if ((fonts[fontnumber].flags & 0xf80) == 0x100) return True;
   return False;
}

/*----------------------------------------------------------------------*/
/* Test equivalence of two label strings (relaxed constraints)          */
/*    Like stringcomp(), but ignores "superficial" differences such as  */
/*    color and font (unless one of the fonts is Symbol), scale,  */
/*    underlining, tabbing, and kerning.                    */
/*----------------------------------------------------------------------*/

int stringcomprelaxed(stringpart *string1, stringpart *string2,
                  objinstptr thisinst)
{
   stringpart *strptr1 = string1, *strptr2 = string2;
   Boolean font1 = False, font2 = False;

   if (strptr1->type == FONT_NAME)
      font1 = issymbolfont(strptr1->data.font);
   if (strptr2->type == FONT_NAME)
      font2 = issymbolfont(strptr2->data.font);

   while ((strptr1 != NULL) || (strptr2 != NULL)) {
      while (strptr1 != NULL && strptr1->type != TEXT_STRING &&
            strptr1->type != OVERLINE) {
       if (strptr1->type == FONT_NAME)
          font1 = issymbolfont(strptr1->data.font);
       strptr1 = nextstringpart(strptr1, thisinst);
      }
      while (strptr2 != NULL && strptr2->type != TEXT_STRING &&
            strptr2->type != OVERLINE) {
       if (strptr2->type == FONT_NAME)
          font2 = issymbolfont(strptr2->data.font);
       strptr2 = nextstringpart(strptr2, thisinst);
      }
      if (strptr1 == NULL || strptr2 == NULL) break;
      if (font1 != font2) return 1;
      if (strptr1->type != strptr2->type) return 1;
      else {
         switch (strptr1->type) {
          case TEXT_STRING:
             if (strcmp(strptr1->data.string, strptr2->data.string)) return 1;
             break;
          case OVERLINE:
             if (strptr1->type != strptr2->type) return 1;
             break;
         }
       strptr1 = nextstringpart(strptr1, thisinst);
       strptr2 = nextstringpart(strptr2, thisinst);
      }
   }

   /* One string continues after the other ends. . . */ 
   if (strptr1 != NULL || strptr2 != NULL) return 1;
   return 0;
}
/*----------------------------------------------------------------------*/
/* Find the number of parts in a string   (excluding parameter contents)      */
/*----------------------------------------------------------------------*/

int stringparts(stringpart *string)
{
   stringpart *strptr;
   int ptotal = 0;

   for (strptr = string; strptr != NULL; strptr = strptr->nextpart)
      ptotal++;

   return ptotal;
}

/*----------------------------------------------------------------------*/
/* Compute the total character length of a string                 */
/*    If "doparam" is True, include parameter contents.                 */
/*----------------------------------------------------------------------*/

int stringlength(stringpart *string, Boolean doparam, objinstptr thisinst)
{
   stringpart *strptr;
   int ctotal = 0;

   for (strptr = string; strptr != NULL; strptr = (doparam) ?
            nextstringpart(strptr, thisinst) : strptr->nextpart) {
      if (strptr->type == TEXT_STRING) {
       if (strptr->data.string)
          ctotal += strlen(strptr->data.string);
      }
      else
       ctotal++;
   }

   return ctotal;
}

/*----------------------------------------------------------------------*/
/* Copy the contents of a string (excluding parameter contents)         */
/*----------------------------------------------------------------------*/

stringpart *stringcopy(stringpart *string)
{
   stringpart *strptr, *newpart, *newtop = NULL, *topptr;

   for (strptr = string; strptr != NULL; strptr = strptr->nextpart) {

      /* Don't use makesegment(), which looks at parameter contents */
      newpart = (stringpart *)malloc(sizeof(stringpart));
      newpart->nextpart = NULL;
      if (newtop == NULL)
       newtop = newpart;
      else 
       topptr->nextpart = newpart;
      topptr = newpart;

      newpart->type = strptr->type;
      if (strptr->type == TEXT_STRING) {
       newpart->data.string = (char *)malloc(1 + strlen(strptr->data.string));
       strcpy(newpart->data.string, strptr->data.string);
      }
      else
         newpart->data = strptr->data;
   }
   return newtop;
}

/*----------------------------------------------------------------------*/
/* Copy the contents of a string, embedding parameter contents          */
/*----------------------------------------------------------------------*/

stringpart *stringcopyall(stringpart *string, objinstptr thisinst)
{
   stringpart *strptr, *newpart, *newtop, *topend;

   for (strptr = string; strptr != NULL;
            strptr = nextstringpart(strptr, thisinst)) {
      newpart = (stringpart *)malloc(sizeof(stringpart));
      newpart->type = strptr->type;
      newpart->nextpart = NULL;
      if (strptr == string) newtop = newpart;
      else topend->nextpart = newpart;
      topend = newpart;
      if ((strptr->type == TEXT_STRING) && strptr->data.string) {
       newpart->data.string = (char *)malloc(1 + strlen(strptr->data.string));
       strcpy(newpart->data.string, strptr->data.string);
      }
      else
         newpart->data = strptr->data;
   }
   return newtop;
}

/*----------------------------------------------------------------------*/
/* Copy the contents of a saved string with embedded parameter contents */
/* back to the string and the instance parameters.                */
/*----------------------------------------------------------------------*/

stringpart *stringcopyback(stringpart *string, objinstptr thisinst)
{
   stringpart *strptr, *newpart, *curend = NULL, *rettop, *curtop, *savend;
   int paramno = -1;
   oparamptr pparam;

   for (strptr = string; strptr != NULL; strptr = strptr->nextpart) {

      newpart = (stringpart *)malloc(sizeof(stringpart));
      newpart->type = strptr->type;
      newpart->nextpart = NULL;

      if (strptr == string) rettop = newpart;   /* initial segment */
      else curend->nextpart = newpart;    /* append segment to label */

      if (curend) {
         if (curend->type == PARAM_START) {
          paramno = curend->data.paramno;
          curtop = newpart;
          savend = curend;
         }
         else if (curend->type == PARAM_END) {
          curend->nextpart = NULL;
          savend->nextpart = newpart;
         }
      }
      curend = newpart;

      if (strptr->type == TEXT_STRING) {
       if (strptr->data.string) {
          newpart->data.string = (char *)malloc(1 + strlen(strptr->data.string));
          strcpy(newpart->data.string, strptr->data.string);
       }
       else
          newpart->data.string = NULL;
      }
      else if (strptr->type == PARAM_END) {
       /* If thisinst is NULL, then use topobject (top level object) */

       if (paramno >= 0) {
          if (thisinst == NULL) {
             if (topobject->params != NULL)
                pparam = topobject->params[paramno];
             else
              Fprintf(stderr, "Error:  Bad parameter number encountered!\n");
          }
          else if ((thisinst->params) != NULL && (thisinst->params[paramno] != NULL))
             pparam = thisinst->params[paramno];
          else 
             pparam = thisinst->thisobject->params[paramno];

          freelabel(pparam->parameter.string);
          pparam->parameter.string = curtop;
          paramno = -1;
       }
         else {
          Fprintf(stderr, "Error:  Bad parameter in stringcopyback()\n");
       }
      }
      else
         newpart->data = strptr->data;
   }

   /* tie up loose ends, if necessary */
   if ((curend != NULL) && (curend->type == PARAM_END))
      savend->nextpart = NULL;

   return rettop;
}

/*---------------------------------------------------------------------*/
/* draw a single character at 0, 0 using current transformation matrix */
/*---------------------------------------------------------------------*/

short UDrawChar(u_char code, short styles, short ffont, int groupheight, int passcolor)
{
   objectptr drawchar;
   XPoint alphapts[2];
   short  localwidth;
   objinst charinst;

   alphapts[0].x = 0;
   alphapts[0].y = 0;
   charinst.type = OBJECT;
   charinst.color = DEFAULTCOLOR;
   charinst.rotation = 0;
   charinst.scale = fonts[ffont].scale;
   charinst.position = alphapts[0];
   charinst.params = NULL;
   
   /* get proper font and character */

   drawchar = fonts[ffont].encoding[(u_char)code];
   charinst.thisobject = drawchar;

   localwidth = (drawchar->bbox.lowerleft.x + drawchar->bbox.width) * fonts[ffont].scale;

   if ((fonts[ffont].flags & 0x22) == 0x22) { /* font is derived and italic */
      USlantCTM(DCTM, 0.25);              /* premultiply by slanting function */
   }

   if (!(styles & 64)) {
      
      UDrawObject(&charinst, SINGLE, passcolor, NULL);

      /* under- and overlines */
      if (styles & 8)
         alphapts[0].y = alphapts[1].y = -6;
      else if (styles & 16)
         alphapts[0].y = alphapts[1].y = groupheight + 4;
      if (styles & 24) {
         alphapts[0].x = 0; alphapts[1].x = localwidth;
         UDrawSimpleLine(&alphapts[0], &alphapts[1]);
      }
   }
   return localwidth;
}

/*----------------------------------------------------------------------*/
/* Draw an entire string, including parameter substitutions       */
/*----------------------------------------------------------------------*/

void UDrawString(labelptr drawlabel, int passcolor, objinstptr localinst)
{
   stringpart *strptr;
   char *textptr;
   short  fstyle, ffont, tmpjust, baseline;
   int    pos, group = 0;
   int    defaultcolor, curcolor, scolor;
   short  oldx, oldfont, oldstyle;
   float  tmpscale = 1.0, natscale = 1.0;
   float  tmpthick = xobjs.pagelist[areastruct.page]->wirewidth;
   XPoint newpoint, bboxin[2], bboxout[2];
   u_char xm, ym;
   TextExtents tmpext;
   short *tabstops = NULL;
   short tabno, numtabs = 0;

   if (fontcount == 0) return;

#ifdef SCHEMA
   /* Don't draw temporary labels from schematic capture system */
   if (drawlabel->string->type != FONT_NAME) return;
#endif

   if (passcolor == DOSUBSTRING)
      defaultcolor = curcolor = drawlabel->color;
   else
      defaultcolor = curcolor = passcolor;

   if (defaultcolor != DOFORALL) {
      if (drawlabel->color != DEFAULTCOLOR)
       curcolor = drawlabel->color;
      else
       curcolor = defaultcolor;
      XTopSetForeground(curcolor);
   }

   /* calculate the transformation matrix for this object */
   /* in natural units of the alphabet vectors          */
   /* (conversion to window units)                */

   UPushCTM();
   UPreMultCTM(DCTM, drawlabel->position, drawlabel->scale, drawlabel->rotation);

   /* check for flip invariance; recompute CTM and justification if necessary */

   tmpjust = flipadjust(drawlabel->justify);

   /* "natural" (unscaled) length */
   tmpext = ULength(drawlabel->string, localinst, 0.0, 0, NULL);

   newpoint.x = (tmpjust & NOTLEFT ?
       (tmpjust & RIGHT ? -tmpext.width : -tmpext.width >> 1) : 0);
   newpoint.y = (tmpjust & NOTBOTTOM ?
       (tmpjust & TOP ? -tmpext.ascent : -(tmpext.ascent + tmpext.base) >> 1)
            : -tmpext.base);

#ifdef SCHEMA
   /* Pinlabels have an additional offset spacing to pad */
   /* them from the circuit point to which they attach.  */

   if (drawlabel->pin) {
      if (!areastruct.schemon)
       goto enddraw;    /* Don't draw pins when schematic capture is off */
      else
       pinadjust(tmpjust, &(newpoint.x), &(newpoint.y), 1);
   }
#endif

   oldx = newpoint.x;
   baseline = newpoint.y;

   /* do quick calculation on bounding box; don't draw if off-screen */

   bboxin[0].x = newpoint.x;
   bboxin[0].y = newpoint.y + tmpext.descent;
   bboxin[1].x = newpoint.x + tmpext.width;
   bboxin[1].y = newpoint.y + tmpext.ascent;
   UTransformbyCTM(DCTM, bboxin, bboxout, 2);
   xm = (bboxout[0].x < bboxout[1].x) ? 0 : 1;
   ym = (bboxout[0].y < bboxout[1].y) ? 0 : 1;

   if (bboxout[xm].x < areastruct.width && bboxout[ym].y < areastruct.height &&
       bboxout[1 - xm].x > 0 && bboxout[1 - ym].y > 0) {

       pos = 0;
       for (strptr = drawlabel->string; strptr != NULL;
            strptr = nextstringpart(strptr, localinst)) {

        /* All segments other than text cancel any    */
        /* existing overline/underline in effect.     */

        if (strptr->type != TEXT_STRING)
           fstyle &= 0xfc7;

          /* deal with each text segment type */

        switch(strptr->type) {
           case FONT_NAME:
            ffont = strptr->data.font;
            fstyle = 0;          /* style reset by font change */
              if (baseline == newpoint.y) {  /* set top-level font and style */
                 oldfont = ffont;
                 oldstyle = fstyle;
              }
            
            /* simple boldface technique for derived fonts */

            xobjs.pagelist[areastruct.page]->wirewidth =
               ((fonts[ffont].flags & 0x21) == 0x21) ?  4.0 : 2.0;
            break;

           case FONT_SCALE:
            tmpscale = natscale * strptr->data.scale;
              if (baseline == newpoint.y) /* reset top-level scale */
               natscale = tmpscale;
            break;

           case KERN:
              newpoint.x += strptr->data.kern[0];
              newpoint.y += strptr->data.kern[1];
            break;
            
           case FONT_COLOR:
            if (defaultcolor != DOFORALL) {
               if (strptr->data.color != DEFAULTCOLOR)
                  curcolor = colorlist[strptr->data.color].color.pixel;
               else {
                  if (curcolor != DEFAULTCOLOR) {
                   XTopSetForeground(defaultcolor);
                  }
                  curcolor = DEFAULTCOLOR;
               }
            }
            break;

           case TABBACKWARD:  /* find first tab value with x < xtotal */
              for (tabno = numtabs - 1; tabno >= 0; tabno--) {
                 if (tabstops[tabno] < newpoint.x) {
                  newpoint.x = tabstops[tabno];
                  break;
                 }
              }
              break;

           case TABFORWARD:   /* find first tab value with x > xtotal */
              for (tabno = 0; tabno < numtabs; tabno++) {
                 if (tabstops[tabno] > newpoint.x) {
                  newpoint.x = tabstops[tabno];
                  break;
                 }
              }
              break;

           case TABSTOP:
              numtabs++;
              if (tabstops == NULL) tabstops = (short *)malloc(sizeof(short));
              else tabstops = (short *)realloc(tabstops, numtabs * sizeof(short));
              tabstops[numtabs - 1] = newpoint.x;
            break;

           case RETURN:
            tmpscale = natscale = 1.0;
            baseline -= BASELINE;
              newpoint.y = baseline;
            newpoint.x = oldx;
            break;
      
           case SUBSCRIPT:
              natscale *= SUBSCALE; 
            tmpscale = natscale;
              newpoint.y -= (short)((TEXTHEIGHT >> 1) * natscale);
            break;

           case SUPERSCRIPT:
              natscale *= SUBSCALE;
            tmpscale = natscale;
              newpoint.y += (short)(TEXTHEIGHT * natscale);
            break;

           case NORMALSCRIPT:
              tmpscale = natscale = 1.0;
              ffont = oldfont;      /* revert to top-level font and style */
              fstyle = oldstyle;
              newpoint.y = baseline;
            break;

           case UNDERLINE:
              fstyle |= 8;
            break;

           case OVERLINE:
            if (strptr->nextpart != NULL && strptr->nextpart->type == TEXT_STRING) {
               objectptr charptr;
               int tmpheight;

               group = 0;
               for (textptr = strptr->nextpart->data.string;
                        textptr && *textptr != '\0'; textptr++) {
                  charptr = fonts[ffont].encoding[*(unsigned char *)textptr];
                  tmpheight = (int)((float)charptr->bbox.height
                        * fonts[ffont].scale);
                  if (group < tmpheight) group = tmpheight;
               }
                 fstyle |= 16;
            }
            break;

           case NOLINE:
            break;

           case HALFSPACE: case QTRSPACE: {
            short addx;
            UPushCTM();
            UPreMultCTM(DCTM, newpoint, tmpscale, 0);
            addx = UDrawChar((u_char)32, fstyle, ffont, group,
                  curcolor);
            newpoint.x += addx >> ((strptr->type == HALFSPACE) ? 1 : 2);
            UPopCTM();
            } break;

           case TEXT_STRING:
            for (textptr = strptr->data.string; textptr && *textptr != '\0'; textptr++) {
               pos++;
               UPushCTM();
               UPreMultCTM(DCTM, newpoint, tmpscale, 0);

                 /* Special case of selection:  only substring is */
               /* drawn in the selection color.             */

               scolor = curcolor;
                 if (passcolor == DOSUBSTRING) {
                    if (pos <= textpos && pos > textend)
                     scolor = SELECTCOLOR;
                  else if (pos > textpos) {
                           XTopSetForeground(curcolor);
                  }
                 }
                 newpoint.x += UDrawChar(*textptr, fstyle, ffont,
                  group, scolor) * tmpscale;

               UPopCTM();
            }
            pos--;
            break;
        }
        pos++;
       }
   }

enddraw:

   if (tabstops != NULL) free(tabstops);

   /* Pop the string transformation matrix */

   UPopCTM();

#ifdef SCHEMA
   if (drawlabel->pin && areastruct.schemon) UDrawXDown(drawlabel);
#endif

   if ((defaultcolor != DOFORALL) && (passcolor != curcolor)) {
      XTopSetForeground(passcolor);
   }

   xobjs.pagelist[areastruct.page]->wirewidth = tmpthick;
}

/*----------------------------------------------------------------------*/
/* Compute the actual length of a string or portion thereof.            */
/*----------------------------------------------------------------------*/

TextExtents ULength(stringpart *string, objinstptr localinst, float newscale,
      short dostop, XPoint *tbreak)
{
   float oldscale, strscale, natscale, locscale = 1.0, xtotal = 0.5;
   float lasttotal = xtotal;
   stringpart *strptr;
   u_char *textptr;
   objectptr *somebet = NULL, chptr;
   short locpos = 0, lastpos = 0;
   float ykern = 0.0;
   TextExtents retext;
   short *tabstops = NULL;
   short tabno, numtabs = 0;

   retext.width = retext.ascent = retext.descent = retext.base = 0;

   if (fontcount == 0) return retext;
#ifdef SCHEMA
   /* Don't draw temporary labels from schematic capture system */
   else if (string->type != FONT_NAME) return retext;
#endif

   if (newscale > 0.0) natscale = UTopScale() * newscale;
   else natscale = 1.0;
     
   oldscale = strscale = natscale;

   for (strptr = string; strptr != NULL;
            strptr = nextstringpart(strptr, localinst)) {
      switch (strptr->type) {
       case SUPERSCRIPT:
          natscale *= SUBSCALE;
          strscale = natscale;
          ykern += TEXTHEIGHT * natscale;
          break;
       case SUBSCRIPT:
          natscale *= SUBSCALE;
          strscale = natscale;
          ykern -= TEXTHEIGHT * natscale / 2.0;
          break;
       case NORMALSCRIPT:
          natscale = strscale = oldscale;
          ykern = 0.0;
          break;
       case RETURN:
          natscale = strscale = oldscale;
          ykern = 0.0;
          retext.base -= BASELINE;
          if (!dostop)
             retext.width = max(retext.width, xtotal);
          xtotal = 0.5;
          break;
       case HALFSPACE: 
          chptr = (*(somebet + 32));
          xtotal += (float)(chptr->bbox.width + chptr->bbox.lowerleft.x)
                  * locscale * natscale / 2;
          break;
       case QTRSPACE:
          chptr = (*(somebet + 32));
          xtotal += (float)(chptr->bbox.width + chptr->bbox.lowerleft.x) 
                  * locscale * natscale / 4;
          break;
       case TABBACKWARD:      /* find first tab value with x < xtotal */
          for (tabno = numtabs - 1; tabno >= 0; tabno--) {
             if (tabstops[tabno] < xtotal) {
              xtotal = tabstops[tabno];
              break;
             }
          }
          break;
       case TABFORWARD: /* find first tab value with x > xtotal */
          for (tabno = 0; tabno < numtabs; tabno++) {
             if (tabstops[tabno] > xtotal) {
              xtotal = tabstops[tabno];
              break;
             }
          }
          break;
       case TABSTOP:
          numtabs++;
          if (tabstops == NULL) tabstops = (short *)malloc(sizeof(short));
          else tabstops = (short *)realloc(tabstops, numtabs * sizeof(short));
          tabstops[numtabs - 1] = xtotal;
          break;
       case FONT_SCALE:
          strscale = natscale * strptr->data.scale;
          if (ykern == 0.0)
             natscale = strscale;
          if (newscale > 0.0)
             strscale *= UTopScale();
          break;
       case KERN:
          xtotal += strptr->data.kern[0];
          ykern += strptr->data.kern[1];
          break;
       case FONT_NAME:
          somebet = fonts[strptr->data.font].encoding;
          locscale = fonts[strptr->data.font].scale;
          if (ykern == 0.0)
             natscale = locscale;
          break;
       case TEXT_STRING:
          for (textptr = strptr->data.string; textptr && *textptr != '\0'; textptr++) {
               if (dostop && (locpos >= dostop)) break;
             locpos++;

             chptr = (*(somebet + *textptr));
             xtotal += (float)(chptr->bbox.width + chptr->bbox.lowerleft.x)
                  * locscale * strscale;
             retext.ascent = max(retext.ascent, (short)(retext.base + ykern +
                  (float)((chptr->bbox.height + chptr->bbox.lowerleft.y)
                  * locscale * strscale)));
             retext.descent = min(retext.descent, (short)(retext.base + ykern +
                  (float)(chptr->bbox.lowerleft.y * locscale * strscale)));

               if (tbreak != NULL) 
                if ((xtotal > tbreak->x) && (retext.base <= tbreak->y)) break;
               lasttotal = xtotal;
             lastpos = locpos;
          }
          break;
      }
      if (strptr->type != TEXT_STRING) locpos++;
      if (dostop && (locpos >= dostop)) break;
   }
   if (tabstops != NULL) free(tabstops);

   /* special case: return character position in retext.width */
   if (tbreak != NULL) {
      int slen = stringlength(string, True, localinst);
      if ((tbreak->x - lasttotal) < (xtotal - tbreak->x))
       locpos = lastpos + 1;
      if (locpos < 1) locpos = 1;
      else if (locpos > slen) locpos = slen;
      retext.width = locpos;
      return retext;
   }
   retext.width = max(retext.width, xtotal);
   return retext;
}

/*----------------------------------------------------------------------*/
/* low-level routines for drawing and erasing labels              */
/*----------------------------------------------------------------------*/

void undrawtextsimple(labelptr settext)
{
   XSetFunction(dpy, areastruct.gc, GXcopy);
   XTopSetForeground(BACKGROUND);
   UDrawString(settext, DOFORALL, areastruct.topinstance);
}

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

void redrawtextsimple(labelptr settext)
{
   UDrawString(settext, settext->color, areastruct.topinstance);
}

/*----------------------------------------------------------------------*/
/* Redraw all labels in the current object which contain the same */
/* parameter(s) as the indicated label.                           */
/* (It's easier not to bother to check for the same parameter, as there */
/* are typically so few parameters in an object that the extra compute  */
/* and draw time is negligible.)                            */
/*                                                    */
/* Function pointer (undrawtextsimple or redrawtextsimple) indicates    */
/* which function to call on this text label.                     */
/*----------------------------------------------------------------------*/

void drawtextandupdate(labelptr curlabel, void (*function)(labelptr))
{
   genericptr *pgen;
   labelptr slab;

   for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts;
            pgen++) {
      if ((*pgen)->type == LABEL) {
       slab = TOLABEL(pgen);
       if (slab == curlabel) continue;
       if (hasparameter(slab))
          function(slab);
      }
   }
}

/*----------------------------------------------------------------------*/
/* Wrapper functions for drawtextandupdate()                      */
/*----------------------------------------------------------------------*/

void undrawtext(labelptr curlabel)
{
   undrawtextsimple(curlabel);

   if (hasparameter(curlabel))
      drawtextandupdate(curlabel, undrawtextsimple);
}

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

void redrawtext(labelptr curlabel)
{
   redrawtextsimple(curlabel);

   if (hasparameter(curlabel))
      drawtextandupdate(curlabel, redrawtextsimple);
}

/*----------------------------------------------------------------------*/
/* Draw the catalog of font characters                            */
/*----------------------------------------------------------------------*/

void composefontlib(short cfont)
{
   genericptr *pgen;
   objinstptr *drawinst;
   objectptr *curlib, libobj, nullobj;
   objectptr directory = xobjs.libtop[FONTLIB]->thisobject;
   short visobjects, i, qdel;
   polyptr *drawbox;
   pointlist pointptr;

   reset(directory, NORMAL);

   /* Create a pointer to the font library */

   curlib = xobjs.fontlib.library;

   /* Find the number of non-null characters.  Do this by assuming */
   /* that all fonts encode nullchar at position zero.               */

   visobjects = 0;
   nullobj = fonts[cfont].encoding[0];
   for(i = 1; i < 256; i++)
      if (fonts[cfont].encoding[i] != nullobj) visobjects++;

   /* add the box and gridlines */

   visobjects += 34;

   /* generate the list of object instances */

   directory->plist = (genericptr *) realloc(directory->plist, visobjects
            * sizeof(genericptr));
   directory->parts = 0;

   /* 0.5 is the default vscale;  16 is no. characters per line */
   del = min(areastruct.width, areastruct.height) / (0.5 * 16);
   qdel = del >> 2;

   for (i = 0; i < 256; i++) {

      if ((libobj = fonts[cfont].encoding[i]) == nullobj) continue;
      
      NEW_OBJINST(drawinst, directory);
      objectdefaults(*drawinst, libobj,
            (i % 16) * del + qdel,        /* X position */
            -(i / 16) * del + qdel);      /* Y position */
      drawinst = (objinstptr *)directory->plist + directory->parts;
      (*drawinst)->color = DEFAULTCOLOR;
      directory->parts++;
   }

   /* separate characters with gridlines (17 vert., 17 horiz.) */

   for (i = 0; i < 34; i++) {
      NEW_POLY(drawbox, directory);
      polydefaults(*drawbox, 2, 0, 0);

      (*drawbox)->color = SNAPCOLOR;   /* default red */
      (*drawbox)->style = UNCLOSED;
      (*drawbox)->width = 1.0;

      if (i < 17) {
         pointptr = (*drawbox)->points;
         pointptr->x = i * del;
         pointptr->y = 0;
         pointptr = (*drawbox)->points + 1;
         pointptr->x = i * del;
         pointptr->y = -16 * del;
      }
      else {
         pointptr = (*drawbox)->points;
         pointptr->x = 0;
         pointptr->y = (17 - i) * del;
         pointptr = (*drawbox)->points + 1;
         pointptr->x = 16 * del;
         pointptr->y = (17 - i) * del;
      }
      directory->parts++;
   }

   /* Set the bounding box for this display.                      */
   /* This is just the bounds of the grid built above, so there's no    */
   /* need to call any of the bounding box calculation routines.        */

   directory->bbox.lowerleft.x = 0;
   directory->bbox.lowerleft.y = pointptr->y;
   directory->bbox.width = pointptr->x;
   directory->bbox.height = pointptr->x;

   xobjs.libtop[FONTLIB]->bbox.lowerleft.x = 0;
   xobjs.libtop[FONTLIB]->bbox.lowerleft.y = pointptr->y;
   xobjs.libtop[FONTLIB]->bbox.width = pointptr->x;
   xobjs.libtop[FONTLIB]->bbox.height = pointptr->x;

   centerview(xobjs.libtop[FONTLIB]);
}

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

void fontcatbutton(XButtonEvent *event)
{
   short chx, chy;
   u_long rch = 0;

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

      window_to_user(event->x, event->y, &areastruct.save);
 
      chy = -areastruct.save.y / del + 1;
      chx = areastruct.save.x / del;

      chx = min(15, chx);
      chy = min(15, chy);
    
      rch = (u_long)(chy * 16 + chx);
   }

   catreturn();

   if (rch != 0)
      labeltext(rch, NULL);
}

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

Generated by  Doxygen 1.6.0   Back to index