Logo Search packages:      
Sourcecode: xcircuit version File versions

python.c

/*--------------------------------------------------------------*/
/* python.c --- Embedded python interpreter for xcircuit    */
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University    */
/*--------------------------------------------------------------*/
/* NOTE: These routines work only if python-2.0 is installed.   */
/*    Requires library file libpython2.0.a.  Hopefully      */
/*    one day there will be a standard libpython${VERSION}.so     */
/*    so that the whole thing doesn't have to be linked     */
/*    into the xcircuit executable. . .               */
/*                                              */
/*    Modeled after demo.c in Python2.0 source Demo/embed   */
/*--------------------------------------------------------------*/

#if defined(HAVE_PYTHON) && !defined(TCL_WRAPPER)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>     /* for usleep() */
#include <string.h>
   
#include <Python.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include "Xw/Xw.h"
#include "Xw/MenuBtn.h"
      
#ifdef HAVE_XPM
#include <X11/xpm.h>
#include "lib/pixmaps/q.xpm"
#endif

PyObject *xcmod;

/*----------------------------------------------------------------------*/
/* Local includes                                                       */
/*----------------------------------------------------------------------*/
      
#include "xcircuit.h"
#include "menudep.h"
#include "colordefs.h"

/*----------------------------------------------------------------------*/
/* Function prototype declarations                                      */
/*----------------------------------------------------------------------*/

#include "prototypes.h"

static PyObject *xc_new(PyObject *, PyObject *);
static PyObject *xc_set(PyObject *, PyObject *);
static PyObject *xc_override(PyObject *, PyObject *);
static PyObject *xc_library(PyObject *, PyObject *);
static PyObject *xc_font(PyObject *, PyObject *);
static PyObject *xc_color(PyObject *, PyObject *);
static PyObject *xc_pause(PyObject *, PyObject *);
static PyObject *xc_refresh(PyObject *, PyObject *);
static PyObject *xc_bind(PyObject *, PyObject *);
static PyObject *xc_unbind(PyObject *, PyObject *);
static PyObject *xc_simplepopup(PyObject *, PyObject *);
static PyObject *xc_popupprompt(PyObject *, PyObject *);
static PyObject *xc_filepopup(PyObject *, PyObject *);
static PyObject *xc_newbutton(PyObject *, PyObject *);
static PyObject *xc_page(PyObject *, PyObject *);
static PyObject *xc_load(PyObject *, PyObject *);
static PyObject *xc_pan(PyObject *, PyObject *);
static PyObject *xc_zoom(PyObject *, PyObject *);
static PyObject *xc_getcursor();
static PyObject *xc_getwindow();
static PyObject *xc_reset();
static PyObject *xc_netlist();
#ifdef HAVE_XPM
static PyObject *xc_newtool(PyObject *, PyObject *);
#endif

int string_to_type(const char *);

/*----------------------------------------------------------------------*/
/* External variable declarations                                       */
/*----------------------------------------------------------------------*/
      
extern char _STR2[250], _STR[150];
extern fontinfo *fonts;
extern short fontcount;
extern Clientdata areastruct;
extern Globaldata xobjs;
extern short beeper; 
extern Widget menuwidgets[];
extern Display *dpy;
extern keybinding *keylist;
extern int *appcolors;
extern int number_colors;
extern colorindex *colorlist;
extern ApplicationData appdata;
#ifdef HAVE_XPM
extern Widget toolbar;
#endif

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

short flags = -1;

#define LIBOVERRIDE     1
#define LIBLOADED       2
#define COLOROVERRIDE   4
#define FONTOVERRIDE    8
#define KEYOVERRIDE     16

/*----------------------------------------------------------------*/
/* Similar to "PyRun_SimpleString()" but returns a PyObject value */
/* rather than a status word.                           */
/*----------------------------------------------------------------*/

PyObject *PyRun_ObjString(char *string)
{
   PyObject *m, *d, *v;
   m = PyImport_AddModule("__main__");
   if (m == NULL) return NULL;
   d = PyModule_GetDict(m);
   v = PyRun_String(string, Py_file_input, d, d);
   return v;
}

/*--------------------------------------------------------------*/
/* Define all of the xcircuit functions available to Python */
/* Currently this does the bare minimum of having Python do the   */
/* argument parsing; ideally, we would like to be doing more      */
/* fundamental processing here.                             */
/*--------------------------------------------------------------*/

/*--------------------------------------------------------------*/
/* Extract text from a popup and call the indicated python  */
/* function.   If there is no text, function is called without    */
/* arguments.                                         */
/*--------------------------------------------------------------*/

void pypromptvalue(Widget w, char *functionptr)
{
   if (strlen(_STR2) == 0)
      sprintf(_STR, "%s()\n", functionptr);
   else
      sprintf(_STR, "%s('%s')\n", functionptr, _STR2);

   PyRun_SimpleString(_STR);
   refresh(NULL, NULL, NULL);
}

/*--------------------------------------------------------------*/
/* Pop up a prompt, and return the value to the indicated   */
/* python function.                                   */
/*--------------------------------------------------------------*/

static PyObject *pypopupprompt(PyObject *self, PyObject *args,
      const char *dofile, char *displaystr)
{
   const char *pfunction = NULL;
   const char *prompt = NULL;
   buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave));

   if (!PyArg_ParseTuple(args, "ss:popupprompt", &prompt, &pfunction))
      return PyInt_FromLong(-1L);


   getgeneric(savebutton, NULL, NULL, (void *)pfunction);
   popupprompt(areastruct.area, (char *)prompt, displaystr,
            pypromptvalue, savebutton, dofile);

   return PyInt_FromLong(0L);
}

/*--------------------------------------------------------------*/
/* Wrappers for pypopuprompt()                              */
/*--------------------------------------------------------------*/

static PyObject *xc_popupprompt(PyObject *self, PyObject *args)
{
   return pypopupprompt(self, args, NULL, "");
}

static PyObject *xc_filepopup(PyObject *self, PyObject *args)
{
   return pypopupprompt(self, args, "", "");
}

static PyObject *xc_simplepopup(PyObject *self, PyObject *args)
{
   _STR2[0] = '\0';
   return pypopupprompt(self, args, False, NULL);
}

/*--------------------------------------------------------------*/
/* Create objects and return a handle to that object in the */
/* form of a Python integer Object.                   */
/*--------------------------------------------------------------*/

static PyObject *xc_new(PyObject *self, PyObject *args)
{
   const char *etype;
   int type;

   genericptr *newgen;
   arcptr *newarc;
   splineptr *newspline;
   polyptr *newpoly;
   labelptr *newlabel;
   pathptr *newpath;
   objinstptr *newobjinst;

   if (!PyArg_ParseTuple(args, "s:newelement", &etype))
      return NULL;
   
   switch(type = string_to_type(etype)) {
      case ARC:
         NEW_ARC(newarc, topobject);
       arcdefaults(*newarc, 0, 0);
       newgen = (genericptr *)newarc;
       break;
      case SPLINE:
         NEW_SPLINE(newspline, topobject);
       splinedefaults(*newspline, 0, 0);
       newgen = (genericptr *)newspline;
       break;
      case POLYGON:
         NEW_POLY(newpoly, topobject);
       polydefaults(*newpoly, 4, 0, 0);
       newgen = (genericptr *)newpoly;
       break;
      case LABEL:
         NEW_LABEL(newlabel, topobject);
       labeldefaults(*newlabel, 0, 0, 0);
       newgen = (genericptr *)newlabel;
       break;
      case PATH:
         NEW_PATH(newpath, topobject);
       pathdefaults(*newpath, 0, 0);
       newgen = (genericptr *)newpath;
       break;
      case OBJECT:
         NEW_OBJINST(newobjinst, topobject);
       objectdefaults(*newobjinst, NULL, 0, 0);
       newgen = (genericptr *)newobjinst;
       break;
      default:
         PyErr_SetString(PyExc_TypeError,
            "newelement() 2nd argument must be a valid element type");
         return NULL;
   } 
   topobject->parts++;
   incr_changes(topobject);
#ifdef SCHEMA
   topobject->valid = False;
#endif

   return PyInt_FromLong((long)(*newgen));
}

/*--------------------------------------------------------------*/
/* Convert object type to a string                    */
/*--------------------------------------------------------------*/

char *type_to_string(int type)
{
   char *retstr = NULL;

   switch(type) {
      case LABEL:
       retstr = malloc(6);
       strcpy(retstr, "Label");
       break;
      case POLYGON:
       retstr = malloc(8);
       strcpy(retstr, "Polygon");
       break;
      case SPLINE:
       retstr = malloc(12);
       strcpy(retstr, "Bezier Curve");
       break;
      case OBJECT:
       retstr = malloc(16);
       strcpy(retstr, "Object Instance");
       break;
      case PATH:
       retstr = malloc(5);
       strcpy(retstr, "Path");
       break;
      case ARC:
       retstr = malloc(4);
       strcpy(retstr, "Arc");
       break;
   }
   return retstr;  
}

/*--------------------------------------------------------------*/
/* Convert a string to an element type                      */
/*--------------------------------------------------------------*/

int string_to_type(const char *etype)
{
   if (!strcmp(etype, "Arc"))
      return ARC;
   else if (!strcmp(etype, "Bezier Curve"))
      return SPLINE;
   else if (!strcmp(etype, "Polygon"))
      return POLYGON;
   else if (!strcmp(etype, "Label"))
      return LABEL;
   else if (!strcmp(etype, "Path"))
      return PATH;
   else if (!strcmp(etype, "Object Instance"))
      return OBJECT;
   else
      return -1;
}

/*--------------------------------------------------------------*/
/* Convert color index to 3-tuple and vice versa            */
/* We assume that this color exists in the color table.           */
/*--------------------------------------------------------------*/

PyObject *PyIndexToRGB(int cidx)
{
   int i;
   PyObject *RGBTuple;

   if (cidx < 0) {      /* Handle "default color" */
      return PyString_FromString("Default");
   }

   for (i = 0; i < number_colors; i++) {
      if (cidx == colorlist[i].color.pixel) {
       RGBTuple = PyTuple_New(3);
       PyTuple_SetItem(RGBTuple, 0,
            PyInt_FromLong((long)(colorlist[i].color.red / 256)));
       PyTuple_SetItem(RGBTuple, 1,
            PyInt_FromLong((long)(colorlist[i].color.green / 256)));
       PyTuple_SetItem(RGBTuple, 2,
            PyInt_FromLong((long)(colorlist[i].color.blue / 256)));
       return RGBTuple;
      }
   }
   PyErr_SetString(PyExc_TypeError, "invalid or unknown color index");
   return NULL;
}

/*--------------------------------------------------------------*/
/* Convert color 3-tuple (RGB) or name to a color index           */
/*--------------------------------------------------------------*/

int PyRGBToIndex(PyObject *cobj)
{
   PyObject *tobj;
   int ccolor, r, g, b;

   if (PyTuple_Check(cobj) && PyTuple_Size(cobj) == 3) {  /* RGB 3-tuple */
      tobj = PyTuple_GetItem(cobj, 0);
      if (PyFloat_Check(tobj)) {
         r = (int)(65535 * (float)PyFloat_AsDouble(tobj));
         g = (int)(65535 * (float)PyFloat_AsDouble(PyTuple_GetItem(cobj, 1)));
         b = (int)(65535 * (float)PyFloat_AsDouble(PyTuple_GetItem(cobj, 2)));
      }
      else if (PyInt_Check(tobj)) {
         r = (int)(256 * PyInt_AsLong(tobj));
         g = (int)(256 * PyInt_AsLong(PyTuple_GetItem(cobj, 1)));
         b = (int)(256 * PyInt_AsLong(PyTuple_GetItem(cobj, 2)));
      }
      else {
         PyErr_SetString(PyExc_TypeError, "tuple components must be integer or float");
         return -1;
      }
      ccolor = rgb_alloccolor(r, g, b);
   }
   else if (PyString_Check(cobj)) {  /* color name */
      ccolor = xc_alloccolor(PyString_AsString(cobj));
   }
   else if (PyInt_Check(cobj)) {  /* index (for backward compatibility) */
      ccolor = (int)PyInt_AsLong(cobj);
   }
   else {
      PyErr_SetString(PyExc_TypeError, "argument must be a string or 3-tuple");
      return -1;
   }

   addnewcolorentry(ccolor);
   return ccolor;
}

/*--------------------------------------------------------------*/
/* Convert a Python list to a stringpart*                   */
/*--------------------------------------------------------------*/

stringpart *PySetStringParts(PyObject *dval)
{
   PyObject *lstr, *litem, *ditem, *sitem, *titem;
   int i, j, llen;
   stringpart *strptr, *newpart;
   char *string;
   
   /* If we pass a string, create a list of size 1 to hold the string.  */
   /* Otherwise, the argument must be a list.                     */

   if (PyList_Check(dval)) {
      lstr = dval;
   }
   else {
      if (PyString_Check(dval)) {
         lstr = PyList_New(1);
       PyList_SetItem(lstr, 0, dval);
      }
      else {
         PyErr_SetString(PyExc_TypeError, "argument must be a string or a list");
       return NULL;
      }
   }

   strptr = NULL;
   llen = PyList_Size(lstr);
   for (i = 0; i < llen; i++) {
      newpart = makesegment(&strptr, NULL);
      newpart->nextpart = NULL;

      litem = PyList_GetItem(lstr, i);
      if (PyDict_Check(litem)) {
       if ((ditem = PyDict_GetItemString(litem, "Parameter")) != NULL) {
          newpart->type = PARAM_START;
          newpart->data.paramno = (int)PyInt_AsLong(ditem);
       }
       else if ((ditem = PyDict_GetItemString(litem, "Font")) != NULL) {
          newpart->type = FONT_NAME;
          string = PyString_AsString(ditem);
          for (j = 0; j < fontcount; j++)
             if (!strcmp(fonts[j].psname, string)) break;
          if (j == fontcount) loadfontfile(string);
          newpart->data.font = j;
       }
       else if ((ditem = PyDict_GetItemString(litem, "Kern")) != NULL) {
          newpart->type = KERN;
          newpart->data.kern[0] = (int)PyInt_AsLong(PyTuple_GetItem(ditem, 0));
          newpart->data.kern[1] = (int)PyInt_AsLong(PyTuple_GetItem(ditem, 1));
       }
       else if ((ditem = PyDict_GetItemString(litem, "Color")) != NULL) {
          newpart->type = FONT_COLOR;
          newpart->data.color = PyRGBToIndex(ditem);
       }
       else if ((ditem = PyDict_GetItemString(litem, "Font Scale")) != NULL) {
          newpart->type = FONT_SCALE;
          newpart->data.scale = (float)PyFloat_AsDouble(ditem);
       }
       else if ((ditem = PyDict_GetItemString(litem, "Text")) != NULL) {
          newpart->type = TEXT_STRING;
          newpart->data.string = strdup(PyString_AsString(ditem));
       }
      }
      else if (PyString_Check(litem)) {
       string = PyString_AsString(litem);
       if (!strcmp(string, "End Parameter")) {
          newpart->type = PARAM_END;
       }
       else if (!strcmp(string, "Tab Stop")) {
          newpart->type = TABSTOP;
       }
       else if (!strcmp(string, "Tab Forward")) {
          newpart->type = TABFORWARD;
       }
       else if (!strcmp(string, "Tab Backward")) {
          newpart->type = TABBACKWARD;
       }
       else if (!strcmp(string, "Return")) {
          newpart->type = RETURN;
       }
       else if (!strcmp(string, "Subscript")) {
          newpart->type = SUBSCRIPT;
       }
       else if (!strcmp(string, "Superscript")) {
          newpart->type = SUPERSCRIPT;
       }
       else if (!strcmp(string, "Normalscript")) {
          newpart->type = NORMALSCRIPT;
       }
       else if (!strcmp(string, "Underline")) {
          newpart->type = UNDERLINE;
       }
       else if (!strcmp(string, "Overline")) {
          newpart->type = OVERLINE;
       }
       else if (!strcmp(string, "No Line")) {
          newpart->type = NOLINE;
       }
       else if (!strcmp(string, "Half Space")) {
          newpart->type = HALFSPACE;
       }
       else if (!strcmp(string, "Quarter Space")) {
          newpart->type = QTRSPACE;
       }
       else {
          newpart->type = TEXT_STRING;
          newpart->data.string = strdup(string);
       }
      }
   }
   return strptr;
}

/*--------------------------------------------------------------*/
/* Convert a stringpart* to a Python list                   */
/*--------------------------------------------------------------*/

PyObject *PyGetStringParts(stringpart *thisstring)
{
   PyObject *lstr, *sdict, *stup;
   int i, llen;
   stringpart *strptr;
   
   llen = stringparts(thisstring);
   lstr = PyList_New(llen);
   for (strptr = thisstring, i = 0; strptr != NULL;
      strptr = strptr->nextpart, i++) {
      switch(strptr->type) {
       case TEXT_STRING:
          sdict = PyDict_New();
          PyDict_SetItem(sdict, PyString_FromString("Text"),
              PyString_FromString(strptr->data.string));
          PyList_SetItem(lstr, i, sdict);
          break;
       case PARAM_START:
          sdict = PyDict_New();
          PyDict_SetItem(sdict, PyString_FromString("Parameter"),
              PyInt_FromLong((long)strptr->data.paramno));
          PyList_SetItem(lstr, i, sdict);
          break;
       case PARAM_END:
          PyList_SetItem(lstr, i, PyString_FromString("End Parameter"));
          break;
       case FONT_NAME:
          sdict = PyDict_New();
          PyDict_SetItem(sdict, PyString_FromString("Font"),
              PyString_FromString(fonts[strptr->data.font].psname));
          PyList_SetItem(lstr, i, sdict);
          break;
       case FONT_SCALE:
          sdict = PyDict_New();
          PyDict_SetItem(sdict, PyString_FromString("Font Scale"),
              PyFloat_FromDouble((double)strptr->data.scale));
          PyList_SetItem(lstr, i, sdict);
          break;
       case KERN:
          sdict = PyDict_New();
          stup = PyTuple_New(2);
          PyTuple_SetItem(stup, 0, PyInt_FromLong((long)strptr->data.kern[0]));
          PyTuple_SetItem(stup, 1, PyInt_FromLong((long)strptr->data.kern[1]));
          PyDict_SetItem(sdict, PyString_FromString("Kern"), stup);
          PyList_SetItem(lstr, i, sdict);
          break;
       case FONT_COLOR:
          stup = PyIndexToRGB(strptr->data.color);
          if (stup != NULL) {
             sdict = PyDict_New();
             PyDict_SetItem(sdict, PyString_FromString("Color"), stup); 
             PyList_SetItem(lstr, i, sdict);
          }
          break;
       case TABSTOP:
          PyList_SetItem(lstr, i, PyString_FromString("Tab Stop"));
          break;
       case TABFORWARD:
          PyList_SetItem(lstr, i, PyString_FromString("Tab Forward"));
          break;
       case TABBACKWARD:
          PyList_SetItem(lstr, i, PyString_FromString("Tab Backward"));
          break;
       case RETURN:
          PyList_SetItem(lstr, i, PyString_FromString("Return"));
          break;
       case SUBSCRIPT:
          PyList_SetItem(lstr, i, PyString_FromString("Subscript"));
          break;
       case SUPERSCRIPT:
          PyList_SetItem(lstr, i, PyString_FromString("Superscript"));
          break;
       case NORMALSCRIPT:
          PyList_SetItem(lstr, i, PyString_FromString("Normalscript"));
          break;
       case UNDERLINE:
          PyList_SetItem(lstr, i, PyString_FromString("Underline"));
          break;
       case OVERLINE:
          PyList_SetItem(lstr, i, PyString_FromString("Overline"));
          break;
       case NOLINE:
          PyList_SetItem(lstr, i, PyString_FromString("No Line"));
          break;
       case HALFSPACE:
          PyList_SetItem(lstr, i, PyString_FromString("Half Space"));
          break;
       case QTRSPACE:
          PyList_SetItem(lstr, i, PyString_FromString("Quarter Space"));
          break;
      }
   }
   return lstr;
}

/*--------------------------------------------------------------*/
/* Check if the handle (integer) is an existing element           */
/*--------------------------------------------------------------*/

genericptr *CheckHandle(PyObject *ehandle)
{
   genericptr *gelem;
   int i, j, eaddr;
   objectptr thisobj;
   Library *thislib;

   eaddr = (int)PyInt_AsLong(ehandle);
   for (gelem = topobject->plist; gelem < topobject->plist +
      topobject->parts; gelem++)
      if ((int)(*gelem) == eaddr) goto exists;

   /* Okay, it isn't in topobject.  Try all other pages. */

   for (i = 0; i < xobjs.pages; i++) {
      if (xobjs.pagelist[i]->pageinst == NULL) continue;
      thisobj = xobjs.pagelist[i]->pageinst->thisobject;
      for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts; gelem++)
         if ((int)(*gelem) == eaddr) goto exists;
   }

   /* Still not found?  Maybe in a library */

   for (i = 0; i < xobjs.numlibs; i++) {
      thislib = xobjs.userlibs + i;
      for (j = 0; j < thislib->number; j++) {
         thisobj = thislib->library[j];
         for (gelem = thisobj->plist; gelem < thisobj->plist + thisobj->parts; gelem++)
            if ((int)(*gelem) == eaddr) goto exists;
      }
   }

   /* Either in the delete list (where we don't want to go) or    */
   /* is an invalid number.                           */
   return NULL;

exists:
   return gelem;
}

/*--------------------------------------------------------------*/
/* Check if the handle (integer) is an existing page        */
/*--------------------------------------------------------------*/

objectptr CheckPageHandle(PyObject *ehandle)
{
   int eaddr, pageno;

   eaddr = (int)PyInt_AsLong(ehandle);
   pageno = is_page((objectptr)eaddr);
   if (pageno < 0) return NULL;
   return (objectptr)eaddr;
}

/*--------------------------------------------------------------*/
/* Form a 2-tuple from a pair of integers.                  */
/*--------------------------------------------------------------*/

static PyObject *make_pair(XPoint *thispoint)
{
   PyObject *dtup;

   dtup = PyTuple_New(2);

   PyTuple_SetItem(dtup, 0,
            PyInt_FromLong((long)(thispoint->x)));
   PyTuple_SetItem(dtup, 1,
            PyInt_FromLong((long)(thispoint->y)));
   return dtup;
}

/*--------------------------------------------------------------*/
/* Get the properties of a page (returned as a dictionary)  */
/*--------------------------------------------------------------*/

static PyObject *xc_getpage(PyObject *self, PyObject *args)
{
   Pagedata *thispage;
   PyObject *rdict, *dlist;
   int pageno = areastruct.page + 1, i;

   if (!PyArg_ParseTuple(args, "|d:getpage", &pageno))
      return NULL;

   if (pageno <= 0 || pageno > xobjs.pages) return NULL;
   else pageno--;

   thispage = xobjs.pagelist[pageno];

   rdict = PyDict_New();

   PyDict_SetItem(rdict, PyString_FromString("filename"),
      PyString_FromString(thispage->filename));

   PyDict_SetItem(rdict, PyString_FromString("page label"),
      PyString_FromString(thispage->pageinst->thisobject->name));

   PyDict_SetItem(rdict, PyString_FromString("output scale"),
      PyFloat_FromDouble((double)thispage->outscale));

   /* To be done:  change these from internal units to "natural" units */

   PyDict_SetItem(rdict, PyString_FromString("grid space"),
      PyFloat_FromDouble((double)thispage->gridspace));

   PyDict_SetItem(rdict, PyString_FromString("snap space"),
      PyFloat_FromDouble((double)thispage->snapspace));

   PyDict_SetItem(rdict, PyString_FromString("orientation"),
      PyInt_FromLong((long)thispage->orient));

   PyDict_SetItem(rdict, PyString_FromString("output mode"),
      PyInt_FromLong((long)thispage->pmode));

   PyDict_SetItem(rdict, PyString_FromString("coordinate style"),
      PyInt_FromLong((long)thispage->coordstyle));

   PyDict_SetItem(rdict, PyString_FromString("page size"),
      make_pair(&(thispage->pagesize)));

   PyDict_SetItem(rdict, PyString_FromString("drawing scale"),
      make_pair(&(thispage->drawingscale)));

   return rdict;
}

/*--------------------------------------------------------------*/
/* Get the properties of a library (returned as a dictionary)     */
/*--------------------------------------------------------------*/

static PyObject *xc_getlibrary(PyObject *self, PyObject *args)
{
   PyObject *rdict, *dlist;
   const char *lname = NULL;
   objectptr *curlib = NULL, thisobj;
   char *errptr;
   int i, lpage;
   int curobjs;

   if (!PyArg_ParseTuple(args, "s:getlibrary", &lname))
      return NULL;

   lpage = strtol(lname, &errptr, 10);
   if (*lname != '\0' && *errptr == '\0' && lpage <= xobjs.numlibs &&
            lpage > 0) {      /* numerical */
      curlib = xobjs.userlibs[lpage - 1].library;
   }
   else {
      for (lpage = 0; lpage < xobjs.numlibs; lpage++) {
       if (!strcmp(lname, xobjs.libtop[lpage + LIBRARY]->thisobject->name)) {
          curlib = xobjs.userlibs[lpage - 1].library;
          break;
       }
       else if (!strncmp(xobjs.libtop[lpage + LIBRARY]->thisobject->name,
                  "Library: ", 9)) {
          if (!strcmp(lname, xobjs.libtop[lpage + LIBRARY]->thisobject->name + 9)) {
             curlib = xobjs.userlibs[lpage - 1].library;
             break;
          }
       }
      }
   }
 
   if (curlib == NULL) {
      PyErr_SetString(PyExc_TypeError,
            "getlibrary() 2nd argument must be a library page or name");
      return NULL;
   }
   curobjs = xobjs.userlibs[lpage - 1].number;

   /* Argument to getlibrary() can be a library name or library page # */

   rdict = PyDict_New();

   PyDict_SetItem(rdict, PyString_FromString("name"),
      PyString_FromString(xobjs.libtop[lpage + LIBRARY]->thisobject->name));

   if (curobjs > 0) {
      dlist = PyList_New(curobjs);
      for (i = 0; i < curobjs; i++) {
       thisobj = *(curlib + i);
         PyList_SetItem(dlist, i, PyString_FromString(thisobj->name));
      }
      PyDict_SetItem(rdict, PyString_FromString("objects"), dlist);
   }
   return rdict;
}

/*--------------------------------------------------------------*/
/* Get the properties of an object (returned as a dictionary)     */
/*--------------------------------------------------------------*/

static PyObject *xc_getobject(PyObject *self, PyObject *args)
{
   objectptr thisobj = NULL, *libobj;
   PyObject *rdict, *dlist, *tpos;
   const char *oname;
   int i, j, k;

   if (!PyArg_ParseTuple(args, "s:getobject", &oname))
      return NULL;

   for (k = 0; k < xobjs.numlibs; k++) { 
      for (j = 0; j < xobjs.userlibs[k].number; j++) {
       libobj = xobjs.userlibs[k].library + j;
       if (!strcmp(oname, (*libobj)->name)) {
          thisobj = *libobj;
          break;
       }
      }
      if (thisobj != NULL) break;
   }

   if (thisobj == NULL) {     /* try the page objects */
      for (k = 0; k < xobjs.pages; k++) {
       if (xobjs.pagelist[k]->pageinst != NULL)
          if (!strcmp(oname, xobjs.pagelist[k]->pageinst->thisobject->name))
             break;
      }
      if (k == xobjs.pages) return NULL;  /* not found */
   }

   /* return all the object's properties as a dictionary */

   rdict = PyDict_New();

   PyDict_SetItem(rdict, PyString_FromString("name"),
      PyString_FromString(thisobj->name));
   PyDict_SetItem(rdict, PyString_FromString("width"), 
      PyInt_FromLong((long)(thisobj->bbox.width)));
   PyDict_SetItem(rdict, PyString_FromString("height"), 
      PyInt_FromLong((long)(thisobj->bbox.height)));
   PyDict_SetItem(rdict, PyString_FromString("viewscale"), 
      PyFloat_FromDouble((double)(thisobj->viewscale)));

   tpos = PyTuple_New(2);
   PyTuple_SetItem(tpos, 0, PyInt_FromLong((long)thisobj->bbox.lowerleft.x));
   PyTuple_SetItem(tpos, 1, PyInt_FromLong((long)thisobj->bbox.lowerleft.y));
   PyDict_SetItem(rdict, PyString_FromString("boundingbox"), tpos);

   tpos = PyTuple_New(2);
   PyTuple_SetItem(tpos, 0, PyInt_FromLong((long)thisobj->pcorner.x));
   PyTuple_SetItem(tpos, 1, PyInt_FromLong((long)thisobj->pcorner.y));
   PyDict_SetItem(rdict, PyString_FromString("viewcorner"), tpos);

   dlist = PyList_New(thisobj->parts);
   for (i = 0; i < thisobj->parts; i++)
      PyList_SetItem(dlist, i, PyInt_FromLong((long)(*(thisobj->plist + i))));
   PyDict_SetItem(rdict, PyString_FromString("parts"), dlist);

   if (thisobj->num_params > 0) {
      dlist = PyList_New(thisobj->num_params);
      for (i = 0; i < thisobj->num_params; i++) {
       if (thisobj->params[i]->type == XC_INT)
            PyList_SetItem(dlist, i,
            PyInt_FromLong((long)(thisobj->params[i]->parameter.ivalue)));
       else if (thisobj->params[i]->type == XC_FLOAT)
            PyList_SetItem(dlist, i,
            PyFloat_FromDouble((double)(thisobj->params[i]->parameter.fvalue)));
       else if (thisobj->params[i]->type == XC_STRING)
            PyList_SetItem(dlist, i,
            PyGetStringParts(thisobj->params[i]->parameter.string));
      }
      PyDict_SetItem(rdict, PyString_FromString("parameters"), dlist);
   }
   return rdict;
}

/*--------------------------------------------------------------*/
/* Reset the current page                             */
/*--------------------------------------------------------------*/

static PyObject *xc_reset()
{
   resetbutton(NULL, (pointertype)0, NULL);
   return PyInt_FromLong(0L);
}

#ifdef SCHEMA
/*--------------------------------------------------------------*/
/* Get a netlist                                */
/*--------------------------------------------------------------*/

extern u_int subindex;  /* count for labeling spice subcircuits */
extern PyObject *pyglobals(objectptr);
extern PyObject *pytoplevel(objectptr);

static PyObject *xc_netlist()
{
   PyObject *rdict = NULL;

   if (checkvalid(topobject) == -1) {
      if (cleartraversed(topobject, 0) == -1)
         PyErr_SetString(PyExc_TypeError, "Error:  Check circuit for infinite recursion.");
      else {
         destroynets();
         createnets();
      }
   }

   subindex = 1;
   rdict = PyDict_New();
   PyDict_SetItem(rdict, PyString_FromString("globals"), pyglobals(topobject));
   PyDict_SetItem(rdict, PyString_FromString("circuit"), pytoplevel(topobject));
   return rdict;
}
#endif

/*--------------------------------------------------------------*/
/* Load the specified file                            */
/*--------------------------------------------------------------*/

static PyObject *xc_load(PyObject *self, PyObject *args)
{
   char *filename;
   int pageno = 0, savepage;

   if (!PyArg_ParseTuple(args, "s|i:load", &filename, &pageno))
      return PyInt_FromLong((long)areastruct.page);

   if (--pageno < 0) pageno = areastruct.page;
   savepage = areastruct.page;

   strcpy(_STR2, filename);
   if (savepage != pageno) newpage(pageno);
   startloadfile();
   if (savepage != pageno) newpage(savepage);

   return PyString_FromString(filename);
}

/*--------------------------------------------------------------*/
/* Go to the specified page                           */
/*--------------------------------------------------------------*/

static PyObject *xc_page(PyObject *self, PyObject *args)
{
   int pageno;

   if (!PyArg_ParseTuple(args, "i:page", &pageno))
      return PyInt_FromLong((long)areastruct.page);

   newpage(pageno - 1);
   return PyInt_FromLong((long)pageno);
}

/*--------------------------------------------------------------*/
/* Zoom                                               */
/*--------------------------------------------------------------*/

static PyObject *xc_zoom(PyObject *self, PyObject *args)
{
   float factor, save;

   if (!PyArg_ParseTuple(args, "f:zoom", &factor))
      return NULL;

   if (factor <= 0) {
      PyErr_SetString(PyExc_TypeError, "Argument must be positive.");
      return NULL;
   }
   
   save = areastruct.zoomfactor;

   if (factor < 1.0) {
      areastruct.zoomfactor = 1.0 / factor;
      zoomout(NULL, NULL, NULL);
   }
   else {
      areastruct.zoomfactor = factor;
      zoomin(NULL, NULL, NULL);
   }

   areastruct.zoomfactor = save;
   return PyFloat_FromDouble((double)topobject->viewscale);
}

/*--------------------------------------------------------------*/
/* Pan                                                */
/*--------------------------------------------------------------*/

static PyObject *xc_pan(PyObject *self, PyObject *args)
{
   PyObject *cornerpos, *xobj, *yobj = NULL, *pval;
   int x, y;
   XButtonEvent bevent;
   XPoint upoint, wpoint;

   if (!PyArg_ParseTuple(args, "O|O:pan", &xobj, &yobj))
      return NULL;

   if (yobj == NULL) {
      if (PyTuple_Check(xobj) && PyTuple_Size(xobj) == 2) {
         if ((pval = PyTuple_GetItem(xobj, 0)) != NULL)
            upoint.x = PyInt_AsLong(pval);
         if ((pval = PyTuple_GetItem(xobj, 1)) != NULL)
            upoint.y = PyInt_AsLong(pval);
      }
      else return NULL;
   }
   else {
      upoint.x = PyInt_AsLong(xobj);
      upoint.y = PyInt_AsLong(yobj);
   }

   user_to_window(upoint, &wpoint);
   bevent.x = wpoint.x;
   bevent.y = wpoint.y;
   panbutton((u_int)5, &bevent);

   cornerpos = PyTuple_New(2);
   PyTuple_SetItem(cornerpos, 0, PyInt_FromLong((long)areastruct.pcorner->x));
   PyTuple_SetItem(cornerpos, 1, PyInt_FromLong((long)areastruct.pcorner->y));

   return cornerpos;
}

/*--------------------------------------------------------------*/
/* Get the window size                                */
/*--------------------------------------------------------------*/

static PyObject *xc_getwindow()
{
   PyObject *windowsize;

   windowsize = PyTuple_New(2);
   PyTuple_SetItem(windowsize, 0, PyInt_FromLong((long)areastruct.width));
   PyTuple_SetItem(windowsize, 1, PyInt_FromLong((long)areastruct.height));

   return windowsize;
}

/*--------------------------------------------------------------*/
/* Get the cursor position                            */
/*--------------------------------------------------------------*/

static PyObject *xc_getcursor()
{
   PyObject *cursorpos;
   XPoint newpos;

   newpos = UGetCursorPos();
   u2u_snap(&newpos);
   
   cursorpos = PyTuple_New(2);
   PyTuple_SetItem(cursorpos, 0, PyInt_FromLong((long)newpos.x));
   PyTuple_SetItem(cursorpos, 1, PyInt_FromLong((long)newpos.y));

   return cursorpos;
}

/*--------------------------------------------------------------*/
/* Get the properties of an element (returned as a dictionary)    */
/*--------------------------------------------------------------*/

static PyObject *xc_getattr(PyObject *self, PyObject *args)
{
   genericptr *gelem;
   PyObject *ehandle, *rdict, *dlist, *lstr, *sdict, *stup;
   int i, llen;
   char *tstr;
   stringpart *strptr;
   

   if (!PyArg_ParseTuple(args, "O:getattr", &ehandle))
      return NULL;

   /* Check to make sure that handle exists! */
   if ((gelem = CheckHandle(ehandle)) == NULL) {
      PyErr_SetString(PyExc_TypeError,
            "Argument must be a valid handle to an element.");
      return NULL;
   }

   /* return the element's properties as a dictionary */

   rdict = PyDict_New();
   tstr = type_to_string((*gelem)->type);
   if (tstr == NULL) {
      PyErr_SetString(PyExc_TypeError,
            "Element type is unknown.");
      return NULL;
   }
   PyDict_SetItem(rdict, PyString_FromString("type"),
        PyString_FromString(tstr));
   free(tstr);
   lstr = PyIndexToRGB((*gelem)->color);
   if (lstr != NULL)
      PyDict_SetItem(rdict, PyString_FromString("color"), lstr);

   switch((*gelem)->type) {
      case LABEL:
       PyDict_SetItem(rdict, PyString_FromString("position"), 
            make_pair(&(TOLABEL(gelem)->position)));
       PyDict_SetItem(rdict, PyString_FromString("rotation"),
            PyInt_FromLong((long)TOLABEL(gelem)->rotation));
       PyDict_SetItem(rdict, PyString_FromString("scale"),
            PyFloat_FromDouble((double)TOLABEL(gelem)->scale));
       PyDict_SetItem(rdict, PyString_FromString("justify"),
            PyInt_FromLong((long)TOLABEL(gelem)->justify));
#ifdef SCHEMA
       PyDict_SetItem(rdict, PyString_FromString("pin"),
            PyInt_FromLong((long)TOLABEL(gelem)->pin));
#endif
       lstr = PyGetStringParts(TOLABEL(gelem)->string);

       PyDict_SetItem(rdict, PyString_FromString("string"), lstr);
       break;
      case POLYGON:
       PyDict_SetItem(rdict, PyString_FromString("style"), 
            PyInt_FromLong((long)TOPOLY(gelem)->style));
       PyDict_SetItem(rdict, PyString_FromString("linewidth"),
            PyFloat_FromDouble((double)TOPOLY(gelem)->width));
       dlist = PyList_New(TOPOLY(gelem)->number);
       for (i = 0; i < TOPOLY(gelem)->number; i++) {
          PyList_SetItem(dlist, i, make_pair(&(TOPOLY(gelem)->points[i])));
       }
       PyDict_SetItem(rdict, PyString_FromString("points"), dlist);
       break;
      case ARC:
       PyDict_SetItem(rdict, PyString_FromString("style"), 
            PyInt_FromLong((long)TOARC(gelem)->style));
       PyDict_SetItem(rdict, PyString_FromString("linewidth"),
            PyFloat_FromDouble((double)TOARC(gelem)->width));
       PyDict_SetItem(rdict, PyString_FromString("radius"), 
            PyInt_FromLong((long)TOARC(gelem)->radius));
       PyDict_SetItem(rdict, PyString_FromString("minor axis"), 
            PyInt_FromLong((long)TOARC(gelem)->yaxis));
       PyDict_SetItem(rdict, PyString_FromString("start angle"),
            PyFloat_FromDouble((double)TOARC(gelem)->angle1));
       PyDict_SetItem(rdict, PyString_FromString("end angle"),
            PyFloat_FromDouble((double)TOARC(gelem)->angle2));
       PyDict_SetItem(rdict, PyString_FromString("position"),
            make_pair(&(TOARC(gelem)->position)));
       break;
      case SPLINE:
       PyDict_SetItem(rdict, PyString_FromString("style"), 
            PyInt_FromLong((long)TOSPLINE(gelem)->style));
       PyDict_SetItem(rdict, PyString_FromString("linewidth"),
            PyFloat_FromDouble((double)TOSPLINE(gelem)->width));
       dlist = PyList_New(4);
       for (i = 0; i < 4; i++) {
          PyList_SetItem(dlist, i, make_pair(&(TOSPLINE(gelem)->ctrl[i])));
       }
       PyDict_SetItem(rdict, PyString_FromString("control points"), dlist);
       break;
      case PATH:
       PyDict_SetItem(rdict, PyString_FromString("style"), 
            PyInt_FromLong((long)TOPATH(gelem)->style));
       PyDict_SetItem(rdict, PyString_FromString("linewidth"),
            PyFloat_FromDouble((double)TOPATH(gelem)->width));
       dlist = PyList_New(TOPATH(gelem)->parts);
       for (i = 0; i < TOPATH(gelem)->parts; i++) {
          PyList_SetItem(dlist, i,
            PyInt_FromLong((long)(*(TOPATH(gelem)->plist + i))));
       }
       PyDict_SetItem(rdict, PyString_FromString("parts"), dlist);
       break;
      case OBJECT:
       PyDict_SetItem(rdict, PyString_FromString("position"), 
            make_pair(&(TOOBJINST(gelem)->position)));
       PyDict_SetItem(rdict, PyString_FromString("rotation"),
            PyInt_FromLong((long)TOOBJINST(gelem)->rotation));
       PyDict_SetItem(rdict, PyString_FromString("scale"),
            PyFloat_FromDouble((double)TOOBJINST(gelem)->scale));
       PyDict_SetItem(rdict, PyString_FromString("name"),
            PyString_FromString(TOOBJINST(gelem)->thisobject->name));
       break;
   }
   return rdict;
}

/*--------------------------------------------------------------*/
/* Set properties of an element (supplied as a dictionary)  */
/*--------------------------------------------------------------*/

static PyObject *xc_setattr(PyObject *self, PyObject *args)
{
   genericptr *gelem;
   PyObject *ehandle, *attrdict, *dval, *pval, *qval;
   int i;

   if (!PyArg_ParseTuple(args, "OO:setattr", &ehandle, &attrdict))
      return NULL;

   /* Check to make sure that handle exists! */
   if ((gelem = CheckHandle(ehandle)) == NULL) return NULL;

   /* Is the argument a dictionary? */
   if (!PyDict_Check(attrdict)) { 
      PyErr_SetString(PyExc_TypeError,
            "setatrr() 2nd argument must be a dictionary");
      return NULL;
   }

   /* First, make sure no attempt is made to change the object type. */

   if ((dval = PyDict_GetItemString(attrdict, "type")) != NULL) {
      int dtype = string_to_type(PyString_AsString(dval));
      if (dtype < 0) return NULL;
      if (dtype != (*gelem)->type) {
         PyErr_SetString(PyExc_TypeError,
            "Attempt to change the type of an object.");
         return NULL;
      }
   }

   /* Next, look for dictionary strings containing values which apply */
   /* to a number of different elements (position, color, etc.)         */

   if ((dval = PyDict_GetItemString(attrdict, "color")) != NULL) {
      (*gelem)->color = (short)PyRGBToIndex(dval);
   }

   if ((dval = PyDict_GetItemString(attrdict, "position")) != NULL) {
      if (PyTuple_Check(dval) && PyTuple_Size(dval) == 2) {
         if ((pval = PyTuple_GetItem(dval, 0)) != NULL) {
            short xpos = (short)PyInt_AsLong(pval);
            switch((*gelem)->type) {
             case ARC:
                TOARC(gelem)->position.x = xpos;
                calcarc(TOARC(gelem));
                break;
             case LABEL:
                TOLABEL(gelem)->position.x = xpos;
                break;
             case OBJECT:
                TOOBJINST(gelem)->position.x = xpos;
                break;
               default:
                PyErr_SetString(PyExc_TypeError,
                  "attempt to set position on Spline, Polygon, or Path");
          }
         }

         if ((pval = PyTuple_GetItem(dval, 1)) != NULL) {
            short ypos = (short)PyInt_AsLong(pval);
            switch((*gelem)->type) {
             case ARC:
                TOARC(gelem)->position.y = ypos;
                calcarc(TOARC(gelem));
                break;
             case LABEL:
                TOLABEL(gelem)->position.y = ypos;
                break;
             case OBJECT:
                TOOBJINST(gelem)->position.y = ypos;
                break;
               default:
                PyErr_SetString(PyExc_TypeError,
                  "attempt to set position on Spline, Polygon, or Path");
          }
         }
      }
      else {
         PyErr_SetString(PyExc_TypeError,
            "position must be a tuple containing two integer values");
      }
   }

   if ((dval = PyDict_GetItemString(attrdict, "style")) != NULL) {
      short dstyle = (short)PyInt_AsLong(dval);
      switch((*gelem)->type) {
         case POLYGON:
          TOPOLY(gelem)->style = dstyle;
          break;
         case PATH:
          TOPATH(gelem)->style = dstyle;
          break;
         case ARC:
          TOARC(gelem)->style = dstyle;
          break;
         case SPLINE:
          TOSPLINE(gelem)->style = dstyle;
          break;
         default:
            PyErr_SetString(PyExc_TypeError,
            "attempt to set style on an Object Instance or Label");
      }
   }

   if ((dval = PyDict_GetItemString(attrdict, "linewidth")) != NULL) {
      float dwidth = (float)PyFloat_AsDouble(dval);
      switch((*gelem)->type) {
         case POLYGON:
          TOPOLY(gelem)->width = dwidth;
          break;
         case PATH:
          TOPATH(gelem)->width = dwidth;
          break;
         case ARC:
          TOARC(gelem)->width = dwidth;
          break;
         case SPLINE:
          TOSPLINE(gelem)->width = dwidth;
          break;
         default:
            PyErr_SetString(PyExc_TypeError,
            "attempt to set linewidth on an Object Instance or Label");
      }
   }

   if ((dval = PyDict_GetItemString(attrdict, "scale")) != NULL) {
      float dscale = (float)PyFloat_AsDouble(dval);
      switch((*gelem)->type) {
         case LABEL:
          TOLABEL(gelem)->scale = dscale;
          break;
         case OBJECT:
          TOOBJINST(gelem)->scale = dscale;
          break;
         default:
            PyErr_SetString(PyExc_TypeError,
            "attempt to set scale on something not a Label or Object Instance");
      }
   }

   if ((dval = PyDict_GetItemString(attrdict, "rotation")) != NULL) {
      float drot = (float)PyFloat_AsDouble(dval);
      switch((*gelem)->type) {
         case LABEL:
          TOLABEL(gelem)->rotation = drot;
          break;
         case OBJECT:
          TOOBJINST(gelem)->rotation = drot;
          break;
         default:
            PyErr_SetString(PyExc_TypeError,
            "attempt to set rotation on something not a Label or Object Instance");
      }
   }

   /* Dictionary entries specific to certain xcircuit types */

   switch((*gelem)->type) {
      case LABEL:
       if ((dval = PyDict_GetItemString(attrdict, "justify")) != NULL) {
          TOLABEL(gelem)->justify = PyInt_AsLong(dval);
       }
       if ((dval = PyDict_GetItemString(attrdict, "string")) != NULL) {
          freelabel(TOLABEL(gelem)->string);
          TOLABEL(gelem)->string = PySetStringParts(dval);
       }
#ifdef SCHEMA
       if ((dval = PyDict_GetItemString(attrdict, "pin")) != NULL) {
          TOLABEL(gelem)->pin = PyInt_AsLong(dval);
       }
#endif
       break;
      case ARC:
       if ((dval = PyDict_GetItemString(attrdict, "start angle")) != NULL) {
          TOARC(gelem)->angle1 = (float)PyFloat_AsDouble(dval);
       }
       if ((dval = PyDict_GetItemString(attrdict, "end angle")) != NULL) {
          TOARC(gelem)->angle2 = (float)PyFloat_AsDouble(dval);
       }
       if ((dval = PyDict_GetItemString(attrdict, "radius")) != NULL) {
          TOARC(gelem)->radius = PyInt_AsLong(dval);
       }
       if ((dval = PyDict_GetItemString(attrdict, "minor axis")) != NULL) {
          TOARC(gelem)->yaxis = PyInt_AsLong(dval);
       }
       break;
      case SPLINE:
       if ((dval = PyDict_GetItemString(attrdict, "control points")) != NULL) {
            if (PyList_Check(dval) && PyList_Size(dval) == 4) {
             for (i = 0; i < 4; i++) {
              pval = PyList_GetItem(dval, i);
              if (PyTuple_Check(pval) && PyTuple_Size(pval) == 2) {
                 qval = PyTuple_GetItem(pval, 0);
                 TOSPLINE(gelem)->ctrl[i].x = (short)PyInt_AsLong(qval);
                 qval = PyTuple_GetItem(pval, 1);
                 TOSPLINE(gelem)->ctrl[i].y = (short)PyInt_AsLong(qval);
              }
              else {
                     PyErr_SetString(PyExc_TypeError,
                        "must have a tuple of 2 values per point");
                   break;
              }
             }
          }
          else {
               PyErr_SetString(PyExc_TypeError,
                  "must have 4 control points in a list");
             break;
          }
       }
       break;
      case POLYGON:
       if ((dval = PyDict_GetItemString(attrdict, "points")) != NULL) {
            if (PyList_Check(dval)) {
             int number = PyList_Size(dval);
             if (TOPOLY(gelem)->number != number) {
              TOPOLY(gelem)->points = (pointlist)realloc(
                  TOPOLY(gelem)->points, number * sizeof(XPoint));
              TOPOLY(gelem)->number = number;
             }
             for (i = 0; i < number; i++) {
              pval = PyList_GetItem(dval, i);
              if (PyTuple_Check(pval) && PyTuple_Size(pval) == 2) {
                 qval = PyTuple_GetItem(pval, 0);
                 TOPOLY(gelem)->points[i].x = (short)PyInt_AsLong(qval);
                 qval = PyTuple_GetItem(pval, 1);
                 TOPOLY(gelem)->points[i].y = (short)PyInt_AsLong(qval);
              }
              else {
                     PyErr_SetString(PyExc_TypeError,
                        "must have a tuple of 2 values per point");
                   break;
              }
             }
          }
          else {
               PyErr_SetString(PyExc_TypeError,
                  "points must be in a list of tuples");
             break;
          }
       }
       break;
   }

   calcbbox(areastruct.topinstance);
   return PyInt_FromLong((long)(*gelem));
}
/*--------------------------------------------------------------*/
/* Set various options through the "set" command.           */
/* "set <option> on|off" supercedes "enable|disable <option>"     */
/*--------------------------------------------------------------*/

static PyObject *xc_set(PyObject *self, PyObject *args)
{
   const char *sarg1, *sarg2;
   int i;
   Boolean a = True; 
   short cpage = areastruct.page;

   if (!PyArg_ParseTuple(args, "ss:set", &sarg1, &sarg2))
      return NULL;
   
   if (!strcmp(sarg2, "On") || !strcmp(sarg2, "on") || !strcmp(sarg2, "True")
      || !strcmp(sarg2, "true"))
      a = False;     /* has to be backwards; toggle() inverts the value! */

   if (!strcmp(sarg1, "font")) {
      for (i = 0; i < fontcount; i++)
       if (!strcmp(fonts[i].psname, sarg2)) break;

      if (i == fontcount)
       loadfontfile((char *)sarg2);

      /* loadfontfile() may load multiple fonts, so we have to check again */
      /* to see which one matches.                             */

      for (i = 0; i < fontcount; i++) {
       if (!strcmp(fonts[i].psname, sarg2)) {
            areastruct.psfont = i;
            setdefaultfontmarks();
          break;
       }
      }
   }
   else if (!strcmp(sarg1, "fontscale")) {
      sscanf(sarg2, "%f", &areastruct.textscale);
   }
   else if (!strcmp(sarg1, "axis") || !strcmp(sarg1, "axes")) {
      areastruct.axeson = a;
      toggle(GridAxesButton, &areastruct.axeson, NULL);
   }
   else if (!strcmp(sarg1, "grid")) {
      areastruct.gridon = a;
      toggle(GridGridButton, &areastruct.gridon, NULL);
   }  
   else if (!strcmp(sarg1, "snap") || !strcmp(sarg1, "snap-to")) {
      areastruct.snapto = a;
      toggle(SnaptoSnaptoButton, &areastruct.snapto, NULL);
   }
   else if (!strcmp(sarg1, "gridspace")) {
      sscanf(sarg2, "%f", &xobjs.pagelist[cpage]->gridspace);
   }
   else if (!strcmp(sarg1, "snapspace")) {
      sscanf(sarg2, "%f", &xobjs.pagelist[cpage]->snapspace);
   }
   else if (!strcmp(sarg1, "pagestyle")) {
      if (!strcmp(sarg2, "encapsulated") || !strcmp(sarg2, "eps"))
       xobjs.pagelist[cpage]->pmode = 0;
      else
       xobjs.pagelist[cpage]->pmode = 1;
   }
   else if (!strcmp(sarg1, "boxedit")) {
      if (!strcmp(sarg2, "rhomboid-x")) boxedit(NULL, RHOMBOIDX, NULL);
      else if (!strcmp(sarg2, "rhomboid-y")) boxedit(NULL, RHOMBOIDY, NULL);
      else if (!strcmp(sarg2, "rhomboid-a")) boxedit(NULL, RHOMBOIDA, NULL);
      else if (!strcmp(sarg2, "manhattan")) boxedit(NULL, MANHATTAN, NULL);
      else if (!strcmp(sarg2, "normal")) boxedit(NULL, NORMAL, NULL);
   }
   else if (!strcmp(sarg1, "linewidth")) {
      sscanf(sarg2, "%f", &areastruct.linewidth);
   }
   else if (!strcmp(sarg1, "colorscheme")) {
      if (!strcmp(sarg2, "inverse"))
         areastruct.invert = False;
      inversecolor(NULL, &areastruct.invert, NULL);
   }
   else if (!strcmp(sarg1, "coordstyle")) {
      if (!strcmp(sarg2, "cm") || !strcmp(sarg2, "centimeters")) {
         xobjs.pagelist[cpage]->coordstyle = CM;
         xobjs.pagelist[cpage]->pagesize.x = 595;  /* A4 size */
         xobjs.pagelist[cpage]->pagesize.y = 842;
         togglegrid((u_short)xobjs.pagelist[cpage]->coordstyle);
      }
   }
   else if (!strcmp(sarg1, "orient")) {   /* "orient" or "orientation" */
      if (!strcmp(sarg2, "landscape"))
         xobjs.pagelist[cpage]->orient = 90; /* Landscape */
      else
         xobjs.pagelist[cpage]->orient = 0;  /* Portrait */
   }

#ifdef SCHEMA
   else if (!strcmp(sarg1, "xschema") || !strcmp(sarg1, "schema")) {
      areastruct.schemon = a;
      doxschema(OptionsEnableXSchemaButton, 1, NULL);
   }
#endif
#ifdef HAVE_XPM
   else if (!strcmp(sarg1, "toolbar")) {
      areastruct.toolbar_on = a;
      dotoolbar(OptionsDisableToolbarButton, NULL, NULL);
    }
#endif

   return PyString_FromString(sarg2);
}

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

static PyObject *xc_override(PyObject *self, PyObject *args)
{
   const char *sarg1;

   if (!PyArg_ParseTuple(args, "s:override", &sarg1))
      return NULL;

   if (!strcmp(sarg1, "library") || !strcmp(sarg1, "libraries"))
      flags |= LIBOVERRIDE;
   else if (!strcmp(sarg1, "color") || !strcmp(sarg1, "colors"))
      flags |= COLOROVERRIDE;
   else if (!strcmp(sarg1, "font") || !strcmp(sarg1, "fonts"))
      flags |= FONTOVERRIDE;
   if (!strcmp(sarg1, "key") || !strcmp(sarg1, "keybindings"))
      flags |= KEYOVERRIDE;
   
   return PyInt_FromLong(0L);
}

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

static PyObject *xc_library(PyObject *self, PyObject *args)
{
   const char *libname;
   int libnum = 1;

   if (!PyArg_ParseTuple(args, "s|i:library", &libname, &libnum))
      return NULL;

   /* if loading of default libraries is not overridden, load them first */

   if (!(flags & (LIBOVERRIDE | LIBLOADED))) {
      defaultscript();
      flags |= LIBLOADED;   /* Pass through a Python variable? */
   }

   if (libnum >= xobjs.numlibs || libnum < 0)
      libnum = createlibrary();
   else
      libnum += LIBRARY - 1;

   strcpy(_STR, libname);
   loadlibrary(libnum);
   return PyInt_FromLong((long)libnum);
}

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

static PyObject *xc_font(PyObject *self, PyObject *args)
{
   const char *fontname;

   if (!PyArg_ParseTuple(args, "s:font", &fontname))
      return NULL;

   if (!(flags & FONTOVERRIDE)) {
      loadfontfile("Helvetica");
      flags |= FONTOVERRIDE;
   }
   loadfontfile((char *)fontname);
   return PyString_FromString(fontname);
}

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

static PyObject *xc_color(PyObject *self, PyObject *args)
{
   PyObject *pcolor;
   int cidx;

   if (!PyArg_ParseTuple(args, "O:color", &pcolor))
      return NULL;

   cidx = PyRGBToIndex(pcolor);
   return PyIndexToRGB(cidx);
}

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

static PyObject *xc_bind(PyObject *self, PyObject *args)
{
   const char *keyname = NULL;
   const char *function = NULL;
   PyObject *retobj;
   int keywstate;

   if (!PyArg_ParseTuple(args, "|ss:bind", &keyname, &function))
      return NULL;

   /* If we're in .xcircuitrc and we're not overriding the key        */
   /* bindings, then the first binding function should precipitate  */
   /* calling the defaults, then specify the bindings as overridden */
   if (!(flags & KEYOVERRIDE)) {
      default_keybindings();
      flags |= KEYOVERRIDE;
   }

   /* No arguments?  Return a dictionary of bound pairs */
   if (!keyname) {
      PyObject *key, *value;
      char *keyname, *funcname;
      keybinding *ksearch;

      retobj = PyDict_New();
      for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding) {
       keyname = key_to_string(ksearch->keywstate);
       if (ksearch->value >= 0) {
          funcname = malloc(strlen(func_to_string(ksearch->function)) + 5);
          sprintf(funcname, "%s %d", func_to_string(ksearch->function),
            ksearch->value);
       }
       else
          funcname = func_to_string(ksearch->function);
       key = PyString_FromString(keyname);
       value = PyString_FromString(funcname);
       PyDict_SetItem(retobj, key, value);
       free(keyname);
       if (ksearch->value >= 0) free(funcname);
      }
   }
   /* One argument?  Argument is a key or a function */
   else if (!function) {
      char *binding;
      int func = -1;
      keywstate = string_to_key(keyname);
      if (keywstate == 0) { /* first argument (keyname) is function? */
       keywstate = -1;
       func = string_to_func(keyname, NULL);
      }
      binding = binding_to_string(keywstate, func);
      retobj = PyString_FromString(binding);
      free(binding);
   }
   else {
      if (add_keybinding(keyname, function) < 0) {
       /* Function may be a Python function, not a C function */
         keywstate = string_to_key(keyname);
       sprintf(_STR2, "keydict[%d] = %s\n", keywstate, function);
       PyRun_SimpleString(_STR2);
      }
      retobj = PyString_FromString(keyname);
   }
   return retobj;
}

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

static PyObject *xc_unbind(PyObject *self, PyObject *args)
{
   const char *keyname = NULL;
   const char *function = NULL;

   if (!PyArg_ParseTuple(args, "ss:unbind", &keyname, &function))
      return NULL;

   /* If we're in .xcircuitrc and we're not overriding the key        */
   /* bindings, then the first binding function should precipitate  */
   /* calling the defaults, then specify the bindings as overridden */
   if (!(flags & KEYOVERRIDE)) {
      default_keybindings();
      flags |= KEYOVERRIDE;
   }

   remove_keybinding(keyname, function);
   return PyString_FromString(keyname);
}

/*--------------------------------------------------------------*/
/* active delay                                 */
/*--------------------------------------------------------------*/
   
static PyObject *xc_pause(PyObject *self, PyObject *args)
{
   float delay;

   if (!PyArg_ParseTuple(args, "f:pause", &delay))
      return NULL;

   usleep((int)(1e6 * delay));
   return PyInt_FromLong((int)(1e6 * delay));
}

/*--------------------------------------------------------------*/
/* active refresh                               */
/*--------------------------------------------------------------*/
         
static PyObject *xc_refresh(PyObject *self, PyObject *args)
{
   XEvent event;

   if (!PyArg_ParseTuple(args, ":refresh"))
      return NULL;
      
   refresh(NULL, NULL, NULL);

   while(XCheckWindowEvent(dpy, areastruct.areawin, ~NoEventMask, &event))
         XtDispatchEvent(&event);

   return PyInt_FromLong(0L);
}

/*--------------------------------------------------------------*/
/* Callback procedure for Python-generated buttons          */
/*--------------------------------------------------------------*/

int pybutton(Widget w, caddr_t clientdata, caddr_t calldata)
{  
   int status;

   sprintf(_STR2, "buttondict[%d]()\n", w);
   status = PyRun_SimpleString(_STR2);
   refresh(NULL, NULL, NULL);
   return status;
}

/*--------------------------------------------------------------*/
/* Add a new button to the specified (by name) cascade menu */
/*--------------------------------------------------------------*/

static PyObject *xc_newbutton(PyObject *self, PyObject *args)
{
   const char *pname = NULL;
   const char *newbname = NULL;
   const char *pfunction = NULL;
   int status;
   Arg      wargs[1];
   int  i, n = 0;
   Widget newbutton, cascade, tbutton;

   if (!PyArg_ParseTuple(args, "sss:newbutton", &pname, &newbname, &pfunction))
      return NULL;

   for (i = 0; i < MaxMenuWidgets; i++) {
      tbutton = menuwidgets[i];
      cascade = XtParent(tbutton);
      if (!strcmp(XtName(cascade), pname)) break;
   }
   if (i == MaxMenuWidgets) {
      Wprintf("Cannot find specified menu.");
      return;
   }

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

   XtAddCallback (newbutton, XtNselect, (XtCallbackProc)pybutton, newbutton);
   XtManageChild(newbutton);

   sprintf(_STR2, "buttondict[%d] = %s\n", newbutton, pfunction);
   status = PyRun_SimpleString(_STR2);
   return PyInt_FromLong((long)status);
}

#ifdef HAVE_XPM

/*--------------------------------------------------------------*/
/* Callback procedure for Python-generated toolbar tool           */
/*--------------------------------------------------------------*/

int pytool(Widget w, caddr_t clientdata, caddr_t calldata)
{  
   int status;

   sprintf(_STR2, "tooldict[%d]()\n", w);
   status = PyRun_SimpleString(_STR2);
   refresh(NULL, NULL, NULL);
   return status;
}

/*--------------------------------------------------------------*/
/* Add a new tool to the toolbar                      */
/*--------------------------------------------------------------*/

static PyObject *xc_newtool(PyObject *self, PyObject *args)
{
   const char *thint = NULL;
   const char *pixfile = NULL;
   const char *newtname = NULL;
   const char *pfunction = NULL;
   int status;
   Arg      wargs[8];
   int  i, n = 0;
   Widget newtool, cascade, tbutton;
   XImage *iret;
   XpmAttributes attr;

   attr.valuemask = XpmSize | XpmCloseness;
   attr.closeness = 65536;

   if (!PyArg_ParseTuple(args, "sss|s:newtool", &newtname, &pfunction, &pixfile,
            &thint))
      return NULL;

   if (XpmReadFileToImage(dpy, (char *)pixfile, &iret, NULL, &attr) < 0) {
      sprintf(_STR, "Cannot find or open pixmap file \'%s\'", pixfile);
      Wprintf(_STR);
      XpmCreateImageFromData(dpy, q_xpm, &iret, NULL, &attr);
   }

   XtnSetArg(XtNlabelType, XwIMAGE);
   XtnSetArg(XtNlabelImage, iret);
   XtnSetArg(XtNwidth, attr.width + 4);
   XtnSetArg(XtNheight, attr.height + 4);
   XtnSetArg(XtNborderWidth, TBBORDER);
   XtnSetArg(XtNnoPad, True);
   if (thint != NULL) {
      XtnSetArg(XtNhint, thint);
      XtnSetArg(XtNhintProc, Wprintf);
   }
   newtool = XtCreateWidget(newtname, XwmenubuttonWidgetClass, toolbar, wargs, n);

   XtAddCallback (newtool, XtNselect, (XtCallbackProc)pybutton, newtool);
   XtManageChild(newtool);

   sprintf(_STR2, "tooldict[%d] = %s\n", newtool, pfunction);
   status = PyRun_SimpleString(_STR2);
   return PyInt_FromLong((long)status);
}

/*------------------------------------------------------------------*/
/* Return a list of the widgets in tooldict                     */
/* (memory for list is allocated here and must be free'd elsewhere) */
/*------------------------------------------------------------------*/
 
Widget *pytoolbuttons(int *num_tools)
{
   Widget *ptools;
   PyObject *rval;
   int i;

   if (xcmod == NULL) {
      *num_tools = 0;
      return NULL;
   }
   rval = PyRun_ObjString("len(tooldict)");
   if (!rval) {
      PyErr_Clear();
      return NULL;
   }
   *num_tools = (int)PyInt_AsLong(rval);
   Py_DECREF(rval);
   /* fprintf(stderr, "num_tools = %d\n", *num_tools); */

   if (*num_tools <= 0) return NULL;

   ptools = (Widget *)malloc(*num_tools * sizeof(Widget));

   for (i = 0; i < *num_tools; i++) {
      sprintf(_STR2, "tooldict[%d]", i);
      rval = PyRun_ObjString(_STR2);
      if (!rval) {
         PyErr_Clear();
       return NULL;
      }
      ptools[i] = (Widget)((int)PyInt_AsLong(rval));
      Py_DECREF(rval);
   }
   return ptools;
}

#endif

/*----------------------------------------------------------------*/
/* Execute a python command from the key-function pair dictionary */
/*----------------------------------------------------------------*/

int python_key_command(int keystate)
{
   PyObject *rval;
   int status;
   sprintf(_STR2, "keydict[%d]\n", keystate);
   rval = PyRun_ObjString(_STR2);
   if (!rval) {
      PyErr_Clear();
      return -1;
   }
   Py_DECREF(rval);

   sprintf(_STR2, "keydict[%d]()\n", keystate);
   PyRun_SimpleString(_STR2);
   refresh(NULL, NULL, NULL);
   return 0;
}

/*--------------------------------------------------------------*/
/* Declaration of the xcircuit Python functions             */
/*--------------------------------------------------------------*/

static PyMethodDef xc_methods[] = {
   {"set",        xc_set,           1},
   {"override",         xc_override,      1},
   {"library",          xc_library, 1},
   {"font",       xc_font,    1},
   {"color",            xc_color,   1},
   {"newelement", xc_new,           1},
   {"getpage",          xc_getpage, 1},
   {"getlibrary", xc_getlibrary,    1},
   {"getobject",  xc_getobject,     1},
   {"getattr",          xc_getattr, 1},
   {"setattr",          xc_setattr, 1},
   {"refresh",          xc_refresh, 1},
   {"pause",            xc_pause,   1},
   {"bind",       xc_bind,    1},
   {"unbind",           xc_unbind,  1},
   {"getcursor",  xc_getcursor,     1},
   {"getwindow",  xc_getwindow,     1},
   {"zoom",       xc_zoom,    1},
   {"pan",        xc_pan,           1},
   {"popupprompt",      xc_popupprompt, 1},
   {"filepopup",  xc_filepopup,     1},
   {"simplepopup",      xc_simplepopup,   1},
   {"newbutton",  xc_newbutton,     1},
   {"reset",            xc_reset,   1},
   {"page",       xc_page,    1},
   {"load",       xc_load,    1},
#ifdef HAVE_XPM
   {"newtool",          xc_newtool, 1},
#endif
#ifdef SCHEMA
   {"netlist",          xc_netlist, 1},
#endif
   {NULL,   NULL}             /* sentinel */
};

/*--------------------------------------------------------------*/
/* Initialize Python interpreter and load all xcircuit methods    */
/*--------------------------------------------------------------*/
void init_interpreter()
{
   Py_SetProgramName("XCircuit");
   Py_Initialize();
   PyImport_AddModule("xc");
   xcmod = Py_InitModule("xc", xc_methods);
   PyRun_SimpleString("from xc import *\n");
   PyRun_SimpleString("keydict = {}\n");  /* initialize key/function pairs */
   PyRun_SimpleString("buttondict = {}\n");  /* initialize button/function pairs */
   PyRun_SimpleString("tooldict = {}\n");  /* initialize tool/function pairs */
   sprintf(_STR, "xc_version = %2.1f\n", PROG_VERSION);
   PyRun_SimpleString(_STR);
}

/*--------------------------------------------------------------*/
/* Exit the Python interpreter                              */
/*--------------------------------------------------------------*/

void exit_interpreter()
{
   Py_Exit(0);
}

/*--------------------------------------------------------------*/
/* Replace the functions of the simple rcfile.c interpreter.      */
/*--------------------------------------------------------------*/

/*----------------------------------------------------------------------*/
/* Execute a single command from a script or from the command line      */
/*----------------------------------------------------------------------*/

short execcommand(short pflags, char *cmdptr)
{
   flags = pflags;
   PyRun_SimpleString(cmdptr);
   refresh(NULL, NULL, NULL);
   return flags;
}

/*----------------------------------------------------------------------*/
/* Load the default script (like execscript() but don't allow recursive */
/* loading of the startup script)                                       */
/*----------------------------------------------------------------------*/

void defaultscript()
{
   FILE *fd;
   char *tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR");

   flags = LIBOVERRIDE | LIBLOADED | FONTOVERRIDE;

   if (!tmp_s) tmp_s = BUILTINS_DIR; 
   sprintf(_STR2, "%s/%s", tmp_s, STARTUP_FILE);

   if ((fd = fopen(_STR2, "r")) == NULL) {
      sprintf(_STR2, "%s/%s", BUILTINS_DIR, STARTUP_FILE);
      if ((fd = fopen(_STR2, "r")) == NULL) {
         sprintf(_STR, "Failed to open startup script \"%s\"\n", STARTUP_FILE);
         Wprintf(_STR);
       return;
      }
   }
   PyRun_SimpleFile(fd, _STR2);
}

/*----------------------------------------------------------------------*/
/* Execute a script                                                     */
/*----------------------------------------------------------------------*/

void execscript()
{
   FILE *fd;

   flags = 0;

   xc_tilde_expand(_STR2);
   if ((fd = fopen(_STR2, "r")) != NULL) {
      PyRun_SimpleFile(fd, _STR2);
      refresh(NULL, NULL, NULL);
   }
   else {
      sprintf(_STR, "Failed to open script file \"%s\"\n", _STR2);
      Wprintf(_STR);
   }  
}

/*----------------------------------------------------------------------*/
/* Execute the .xcircuitrc startup script                               */
/*----------------------------------------------------------------------*/

void loadrcfile()
{
   char *userdir = getenv((const char *)"HOME");
   FILE *fd;
   short i;

   /* Initialize flags */

   flags = 0;

   sprintf(_STR2, "%s", USER_RC_FILE);     /* Name imported from Makefile */
      
   /* try first in current directory, then look in user's home directory */
      
   xc_tilde_expand(_STR2);
   if ((fd = fopen(_STR2, "r")) == NULL) {
      if (userdir != NULL) {
         sprintf(_STR2, "%s/%s", userdir, USER_RC_FILE);
         fd = fopen(_STR2, "r");
      }
   }  
   if (fd != NULL)
      PyRun_SimpleFile(fd, _STR2);

   /* Add the default font if not loaded already */

   if (!(flags & FONTOVERRIDE)) {
      loadfontfile("Helvetica");
      if (areastruct.psfont == -1)
         for (i = 0; i < fontcount; i++)
          if (!strcmp(fonts[i].psname, "Helvetica")) {
             areastruct.psfont = i;
             break;
          }
   }

   if (areastruct.psfont == -1) areastruct.psfont = 0;

   setdefaultfontmarks();
      
   /* arrange the loaded libraries */
      
   if (!(flags & (LIBOVERRIDE | LIBLOADED)))
      defaultscript();
      
   /* Add the default colors */
      
   if (!(flags & COLOROVERRIDE)) {
      addnewcolorentry(xc_alloccolor("Gray40"));
      addnewcolorentry(xc_alloccolor("Gray60"));
      addnewcolorentry(xc_alloccolor("Gray80"));
      addnewcolorentry(xc_alloccolor("Gray90"));
      addnewcolorentry(xc_alloccolor("Red"));
      addnewcolorentry(xc_alloccolor("Blue"));
      addnewcolorentry(xc_alloccolor("Green2"));
      addnewcolorentry(xc_alloccolor("Yellow"));
      addnewcolorentry(xc_alloccolor("Purple"));
      addnewcolorentry(xc_alloccolor("SteelBlue2"));
      addnewcolorentry(xc_alloccolor("Red3"));
      addnewcolorentry(xc_alloccolor("Tan"));
      addnewcolorentry(xc_alloccolor("Brown"));
   }  

#ifdef SCHEMA
   /* These colors must be enabled whether or not colors are overridden, */
   /* because they are needed by the schematic capture system.           */

   addnewcolorentry(xc_getlayoutcolor(LOCALPINCOLOR));
   addnewcolorentry(xc_getlayoutcolor(GLOBALPINCOLOR));
   addnewcolorentry(xc_getlayoutcolor(INFOLABELCOLOR));
#endif
   addnewcolorentry(xc_getlayoutcolor(BBOXCOLOR));

   if (!(flags & KEYOVERRIDE))
      default_keybindings();
}

#endif
/* #endif HAVE_PYTHON and !TCL_WRAPPER */
/*--------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index