#include "xlisp.h"
#include "xlstat.h"
#include "xlgraph.h"

/* forward declarations */
LOCAL VOID BufferMarkPoint _((unsigned x, unsigned y, unsigned sym, unsigned color));
LOCAL VOID make_picture _((StGWWinInfo *gwinfo, char *fname));
LOCAL VOID SetDrawingPort _((WindowPtr w));
LOCAL VOID MakeSymbols _((void));

/**************************************************************************/
/**                                                                      **/
/**                            Global Variables                          **/
/**                                                                      **/
/**************************************************************************/

static CGrafPort CWP;
static CGrafPtr CWorkPort;
static GrafPort WP;
static GrafPtr WorkPort;
static char **WorkRowStarts;
static int WorkPortWidth, WorkPortHeight;

static int buffering = FALSE;
static int copying_to_clip = FALSE;
static int bufflevel = 0;

static WindowPtr get_port() { return(thePort); }

static VOID set_port(WindowPtr port)
{
  if (port != thePort) SetPort(port);
}

VOID reset_clip_rect(StGWWinInfo *gwinfo)
{
  WindowPtr w;
  GrafPtr savePort;
  Rect r;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;  
  GetPort(&savePort);
  SetPort(w);
  r = w->portRect;
  r.right -= 15;
  if (gwinfo->hasHscroll) r.bottom -= 15;
  if (gwinfo->clipped) {
    gwinfo->clip_rect.right = min(gwinfo->clip_rect.right, r.right);
    gwinfo->clip_rect.bottom = min(gwinfo->clip_rect.bottom, r.bottom);
    r = gwinfo->clip_rect;
  }
  ClipRect(&r);
  if (buffering) {
    SetDrawingPort(w);
    ClipRect(&r);
  }
  SetPort(savePort);
}

/**************************************************************************/
/**                                                                      **/
/**                       Initialization Functions                       **/
/**                                                                      **/
/**************************************************************************/

VOID initialize_static_globals(void)
{
  int portSize;
  long i;  /* long to force row start calculation to be done long */
  
  WorkPort = ℘
  OpenPort(WorkPort);
  WorkPort->portBits = screenBits;
  portSize = WorkPort->portBits.bounds.bottom 
           - WorkPort->portBits.bounds.top;
  WorkPort->portBits.baseAddr = 
      StCalloc(portSize, WorkPort->portBits.rowBytes);
  if (WorkPort->portBits.baseAddr == nil) {
  	xlfail("Buffer allocation failed");
  	exit(1);
  }
  WorkPortWidth = WorkPort->portBits.bounds.right 
                - WorkPort->portBits.bounds.left;
  WorkPortHeight = WorkPort->portBits.bounds.bottom 
                 - WorkPort->portBits.bounds.top;
  WorkRowStarts = (char **) StCalloc(sizeof(char *), portSize);
  for (i = 0; i < portSize; i++)
    WorkRowStarts[i] = WorkPort->portBits.baseAddr
                     + i * WorkPort->portBits.rowBytes;
  MakeSymbols();

  CWorkPort = (CGrafPtr) WorkPort;
  if (StScreenHasColor()) {
    char *p;
    Rect r;
    struct row_bytes {
      unsigned int flags : 3;
      unsigned int data  : 13;
    } *rowBytes;
    int size;

    CWorkPort = &CWP;
    OpenCPort(CWorkPort);
    
    r = (*CWorkPort->portPixMap)->bounds;
    rowBytes = (struct row_bytes *) &(*CWorkPort->portPixMap)->rowBytes;
    portSize = r.bottom - r.top;
    size = portSize * rowBytes->data;
    
    if ((nnodes < 20000 && FreeMem() < size + 900000) || /* no workspace */
         (nnodes >= 20000 && FreeMem() < size + 500000)) {
      xoserror("not enough memory to allocate color port");
      CWorkPort = (CGrafPtr) WorkPort;
    }
    else {
      HLock((Handle) CWorkPort->portPixMap);
      /* I do't know why I need this two-step business */
      p = StCalloc(portSize, rowBytes->data);
      (*CWorkPort->portPixMap)->baseAddr = p;
      HUnlock((Handle) CWorkPort->portPixMap);
    }
  }
}

VOID adjust_graph_workport(StGWWinInfo *gwinfo)
{
  GrafPtr savePort, workPort;
  WindowPtr w;
  
  if (buffering && gwinfo != nil && (w = gwinfo->window) != nil) {
    workPort = (StGWUseColor(gwinfo)) ? (GrafPtr) CWorkPort : WorkPort;
    GetPort(&savePort);
    SetPort(workPort);
    if (gwinfo->lineType != 0) PenPat(&gray);
    else PenPat(&black);
    PenMode(w->pnMode);
    TextMode(w->txMode);
    PenSize(w->pnSize.h, w->pnSize.h);
    set_fore_color(gwinfo);
    set_back_color(gwinfo);
    SetPort(savePort);
  }
}

/**************************************************************************/
/**                                                                      **/
/**                  Line and Rectangle Drawing Functions                **/
/**                                                                      **/
/**************************************************************************/

LOCAL VOID SetDrawingPort(WindowPtr w)
{
  StGWWinInfo *gwinfo = (StGWWinInfo *) GetWRefCon(w);

  set_port((buffering) ? ((gwinfo->use_color) ? (GrafPtr) CWorkPort: WorkPort) : w);
}

static VOID draw_object(StGWWinInfo *gwinfo, int what, int how, int a, int b, int c, int d)
{
  GrafPtr savePort;
  Rect r;
  WindowPtr w;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  savePort = get_port();
  SetDrawingPort(w);
	
  switch (what) {
  case 'L': MoveTo(a, b); LineTo(c, d); break;
  case 'P': MoveTo(a, b); Line(0, 0); break;
  case 'R':
    SetRect(&r, a, b, a + c, b + d);
    switch (how) {
    case 'E': EraseRect(&r); break;
    case 'F': FrameRect(&r); break;
    case 'P': PaintRect(&r); break;
    }
    break;
  case 'O':
    SetRect(&r, a, b, a + c, b + d);
    switch (how) {
    case 'E': EraseOval(&r); break;
    case 'F': FrameOval(&r); break;
    case 'P': PaintOval(&r); break;
    }
    break;
  }  
  set_port(savePort);
}

static VOID draw_arc(StGWWinInfo *gwinfo, int how, int a, int b, int c, int d,
                     double angle1, double angle2)
{
  GrafPtr savePort;
  Rect r;
  int a1, a2;
  WindowPtr w;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  savePort = get_port();
  SetDrawingPort(w);
	
  SetRect(&r, a, b, a + c, b + d);
  a1 = 90 - angle1;
  a2 = -angle2;
  switch (how) {
  case 'E': EraseArc(&r, a1, a2); break;
  case 'F': FrameArc(&r, a1, a2); break;
  case 'P': PaintArc(&r, a1, a2); break;
  }
  set_port(savePort);
}

static VOID draw_poly(StGWWinInfo *gwinfo, int how, int n, short *p, int from_origin)
{
  GrafPtr savePort;
  int i;
  WindowPtr w;
  PolyHandle poly;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil || n <= 0) return;
  savePort = get_port();
  SetDrawingPort(w);
  
  poly = OpenPoly();
  MoveTo((int) p[0], (int) p[1]);
  for (i = 1; i < n; i++) {
    if (from_origin) LineTo((int) p[2 * i], (int) p[2 * i + 1]);
    else Line((int) p[2 * i], (int) p[2 * i + 1]);
  }
  ClosePoly();
  switch (how) {
  case 'E': ErasePoly(poly); break;
  case 'F': FramePoly(poly); break;
  case 'P': PaintPoly(poly); break;
  }
  KillPoly(poly);
  set_port(savePort);
}	
  
VOID StGWDrawLine(StGWWinInfo *gwinfo, int x1, int y1, int x2, int y2)
{
  draw_object(gwinfo, 'L', '0', x1, y1, x2, y2);
}

VOID StGWDrawPoint(StGWWinInfo *gwinfo, int x, int y)
{
  draw_object(gwinfo, 'P', '0', x, y, 0, 0);
}

VOID StGWEraseRect(StGWWinInfo *gwinfo, int left, int top, int width, int height)
{
  draw_object(gwinfo, 'R', 'E', left, top, width, height);
}

VOID StGWFrameRect(StGWWinInfo *gwinfo, int left, int top, int width, int height)
{
  draw_object(gwinfo, 'R', 'F', left, top, width, height);
} 

VOID StGWPaintRect(StGWWinInfo *gwinfo, int left, int top, int width, int height)
{
  draw_object(gwinfo, 'R', 'P', left, top, width, height);
}

VOID StGWEraseOval(StGWWinInfo *gwinfo, int left, int top, int width, int height)
{
  draw_object(gwinfo, 'O', 'E', left, top, width, height);
}

VOID StGWFrameOval(StGWWinInfo *gwinfo, int left, int top, int width, int height)
{
  draw_object(gwinfo, 'O', 'F', left, top, width, height);
}

VOID StGWPaintOval(StGWWinInfo *gwinfo, int left, int top, int width, int height)
{
  draw_object(gwinfo, 'O', 'P', left, top, width, height);
}

VOID StGWEraseArc(StGWWinInfo *gwinfo, int left, int top, int width, int height,
                  double angle1, double angle2)
{
  draw_arc(gwinfo, 'E', left, top, width, height, angle1, angle2);
}

VOID StGWFrameArc(StGWWinInfo *gwinfo, int left, int top, int width, int height,
                  double angle1, double angle2)
{
  draw_arc(gwinfo, 'F', left, top, width, height, angle1, angle2);
}

VOID StGWPaintArc(StGWWinInfo *gwinfo, int left, int top, int width, int height,
                  double angle1, double angle2)
{
  draw_arc(gwinfo, 'P', left, top, width, height, angle1, angle2);
}

VOID StGWErasePoly(StGWWinInfo *gwinfo, int n, short *p, int from_origin)
{
  draw_poly(gwinfo, 'E', n, p, from_origin);
}

VOID StGWFramePoly(StGWWinInfo *gwinfo, int n, short *p, int from_origin)
{
  draw_poly(gwinfo, 'F', n, p, from_origin);
}

VOID StGWPaintPoly(StGWWinInfo *gwinfo, int n, short *p, int from_origin)
{
  draw_poly(gwinfo, 'P', n, p, from_origin);
}

/**************************************************************************/
/**                                                                      **/
/**                            Text Functions                            **/
/**                                                                      **/
/**************************************************************************/

static text_size(StGWWinInfo *gwinfo, int ascent)
{
  WindowPtr w;
  GrafPtr savePort;
  FontInfo info;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil)
  	w = (StGWUseColor(gwinfo)) ? (WindowPtr) CWorkPort : (WindowPtr) WorkPort;

  savePort = get_port();
  set_port(w);
	
  GetFontInfo(&info);
  
  set_port(savePort);
  return((ascent) ? info.ascent : info.descent);
}

int StGWTextAscent(StGWWinInfo *gwinfo)  { return(text_size(gwinfo, TRUE));  }
int StGWTextDescent(StGWWinInfo *gwinfo) { return(text_size(gwinfo, FALSE)); }

int StGWTextWidth(StGWWinInfo *gwinfo, char *text)
{
  WindowPtr w;
  GrafPtr savePort;
  int string_width;
  Str255 pbuf;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil)
  	w = (StGWUseColor(gwinfo)) ? (WindowPtr) CWorkPort : (WindowPtr) WorkPort;

  savePort = get_port();
  set_port(w);
	
  CintoPstring(text, pbuf, sizeof pbuf, FALSE);
  string_width = StringWidth(pbuf);
  
  set_port(savePort);
  return(string_width);
}

VOID StGWDrawString(StGWWinInfo *gwinfo, char *s, int x, int y)
{
  GrafPtr savePort;
  WindowPtr w;
  Str255 pbuf;
  
  if (s == nil || gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  savePort = get_port();
  SetDrawingPort(w);
	
  MoveTo(x, y);
  CintoPstring(s, pbuf, sizeof pbuf, FALSE);
  DrawString(pbuf);
  
  set_port(savePort);
}

VOID StGWDrawText(StGWWinInfo *gwinfo, char *text, int x, int y, int h, int v)
{
  int FontAscent, string_width;

  if (text == nil || gwinfo == nil) return;
  
  FontAscent = StGWTextAscent(gwinfo);
  string_width = StGWTextWidth(gwinfo, text);

  if (v == 1) y += FontAscent;
  if (h == 1) x -= string_width / 2;
  if (h == 2) x -= string_width;

  StGWDrawString(gwinfo, text, x, y);
}

static VOID DrawCharUp(WindowPtr w, char myChar, int x, int y)
{
  static GrafPort gp;
  static GrafPtr charPort;
  static is_open = FALSE;
  char myImage[32], myTarget[32];
  BitMap myMap, saveBits;
  GrafPtr savePort;
  Rect r;
  
  register int i, j;
  
  savePort = get_port();
  
  if (! is_open) {
    charPort = &gp;
    OpenPort(charPort);
    is_open = TRUE;
  }
  set_port(charPort);
  
  charPort->txFont = w->txFont;
  charPort->txFace = w->txFace;
  charPort->txMode = w->txMode;
  charPort->txSize = w->txSize;
  charPort->spExtra = w->spExtra;
  
  myMap.baseAddr = myImage;
  myMap.rowBytes = 2;
  SetRect(&myMap.bounds, 0, 0, 15, 15);
  
  for (i = 0; i < 32; i++) {
    myImage[i] = '\0';
    myTarget[i] = '\0';
  }
  
  saveBits = charPort->portBits;
  charPort->portBits = myMap;
  MoveTo(3, 12);
  DrawChar(myChar);
  charPort->portBits = saveBits;
  set_port(savePort);
  
  for (i = 0; i < 16; i++)
    for (j = 0; j < 16; j++)
      if (BitTst(myImage, 16 * i + j)) BitSet(myTarget, i + (16 - j) * 16);
  
  r = myMap.bounds;
  OffsetRect(&r, x - 12, y + 3 - (r.bottom - r.top));
  myMap.baseAddr = myTarget;
  
  SetDrawingPort(w);
  CopyBits(&myMap, &thePort->portBits, &myMap.bounds,  &r, w->txMode, nil);
  set_port(savePort);
}

VOID StGWDrawStringUp(StGWWinInfo *gwinfo, char *s, int x, int y)
{
  WindowPtr w;
  char str[2];
  int n;
  
  if (s == nil || gwinfo == nil || (w = gwinfo->window) == nil) return;
  str[1] = '\0';
  
  for (n = strlen(s); n > 0; n--, s++) {
  	DrawCharUp(w, *s, x, y);
  	str[0] = *s;
  	y -= StGWTextWidth(gwinfo, str);
  }
}
 
VOID StGWDrawTextUp(StGWWinInfo *gwinfo, char *text, int x, int y, int h, int v)
{
  int FontAscent, string_width;

  if (text == nil || gwinfo == nil) return;
  
  FontAscent = StGWTextAscent(gwinfo);
  string_width = StGWTextWidth(gwinfo, text);

  if (v == 1) x += FontAscent;
  if (h == 1) y += string_width / 2;
  if (h == 2) y += string_width;

  StGWDrawStringUp(gwinfo, text, x, y);
}

/**************************************************************************/
/**                                                                      **/
/**                           Symbol Functions                           **/
/**                                                                      **/
/**************************************************************************/

#define NUMSYMBOLS 18
#define SYMROWS 5

typedef struct {
  unsigned bit0 :1;
  unsigned bit1 :1;
  unsigned bit2 :1;
  unsigned bit3 :1;
  unsigned bit4 :1;
} SymBits;

typedef struct {
  BitMap map;
  int left, top;
  short image[SYMROWS];
  long refcon;
} Symbol;

static Symbol Symbols[NUMSYMBOLS];

VOID StGWSetSymRefCon(unsigned int index, long rc)
{
  if (index < NUMSYMBOLS) Symbols[index].refcon = rc;
}

long StGWGetSymRefCon(unsigned int index)
{	
  if (index < NUMSYMBOLS) return(Symbols[index].refcon);
  else return((long) nil);
}

static VOID InitSymbol(int sym, int left, int top, int width, int height)
{
  SetRect(&Symbols[sym].map.bounds, 0, 0, width, height);
  Symbols[sym].map.rowBytes = 2;
  Symbols[sym].map.baseAddr = (Ptr) &Symbols[sym].image;
  Symbols[sym].left = left;
  Symbols[sym].top = top;
}

VOID StGWGetSymbolSize(int sym, int *width, int *height)
{
  *width = Symbols[sym].map.bounds.right;
  *height = Symbols[sym].map.bounds.bottom;
}

static VOID SetSymbolData(int sym, int row, int bit0, int bit1, int bit2, int bit3, int bit4)
{
  SymBits *d = (SymBits *) &(Symbols[sym].image[row]);
  d->bit0 = bit0;
  d->bit1 = bit1;
  d->bit2 = bit2;
  d->bit3 = bit3;
  d->bit4 = bit4;
}

static VOID MakeSymbols(void)
{
  InitSymbol(0, 0, 0, 2, 2);
  SetSymbolData(0, 0, 1, 0, 0, 0, 0);
  SetSymbolData(0, 1, 0, 0, 0, 0, 0);
  SetSymbolData(0, 2, 0, 0, 0, 0, 0);
  SetSymbolData(0, 3, 0, 0, 0, 0, 0);
  SetSymbolData(0, 4, 0, 0, 0, 0, 0);

  InitSymbol(1, 0, 0, 2, 2);
  SetSymbolData(1, 0, 1, 1, 0, 0, 0);
  SetSymbolData(1, 1, 0, 0, 0, 0, 0);
  SetSymbolData(1, 2, 0, 0, 0, 0, 0);
  SetSymbolData(1, 3, 0, 0, 0, 0, 0);
  SetSymbolData(1, 4, 0, 0, 0, 0, 0);

  InitSymbol(2, 0, 0, 2, 2);
  SetSymbolData(2, 0, 1, 1, 0, 0, 0);
  SetSymbolData(2, 1, 1, 0, 0, 0, 0);
  SetSymbolData(2, 2, 0, 0, 0, 0, 0);
  SetSymbolData(2, 3, 0, 0, 0, 0, 0);
  SetSymbolData(2, 4, 0, 0, 0, 0, 0);

  InitSymbol(3, 0, 0, 2, 2);
  SetSymbolData(3, 0, 1, 1, 0, 0, 0);
  SetSymbolData(3, 1, 1, 1, 0, 0, 0);
  SetSymbolData(3, 2, 0, 0, 0, 0, 0);
  SetSymbolData(3, 3, 0, 0, 0, 0, 0);
  SetSymbolData(3, 4, 0, 0, 0, 0, 0);

  InitSymbol(4, 2, 2, 4, 4);
  SetSymbolData(4, 0, 0, 1, 1, 0, 0);
  SetSymbolData(4, 1, 1, 0, 0, 1, 0);
  SetSymbolData(4, 2, 1, 0, 0, 1, 0);
  SetSymbolData(4, 3, 0, 1, 1, 0, 0);
  SetSymbolData(4, 4, 0, 0, 0, 0, 0);

  InitSymbol(5, 2, 2, 4, 4);
  SetSymbolData(5, 0, 0, 1, 1, 0, 0);
  SetSymbolData(5, 1, 1, 1, 1, 1, 0);
  SetSymbolData(5, 2, 1, 1, 1, 1, 0);
  SetSymbolData(5, 3, 0, 1, 1, 0, 0);
  SetSymbolData(5, 4, 0, 0, 0, 0, 0);

  InitSymbol(6, 3, 3, 5, 5);
  SetSymbolData(6, 0, 0, 0, 1, 0, 0);
  SetSymbolData(6, 1, 0, 1, 0, 1, 0);
  SetSymbolData(6, 2, 1, 0, 0, 0, 1);
  SetSymbolData(6, 3, 0, 1, 0, 1, 0);
  SetSymbolData(6, 4, 0, 0, 1, 0, 0);

  InitSymbol(7, 3, 3, 5, 5);
  SetSymbolData(7, 0, 0, 0, 1, 0, 0);
  SetSymbolData(7, 1, 0, 1, 1, 1, 0);
  SetSymbolData(7, 2, 1, 1, 1, 1, 1);
  SetSymbolData(7, 3, 0, 1, 1, 1, 0);
  SetSymbolData(7, 4, 0, 0, 1, 0, 0);

  InitSymbol(8, 3, 3, 5, 5);
  SetSymbolData(8, 0, 0, 0, 1, 0, 0);
  SetSymbolData(8, 1, 0, 0, 1, 0, 0);
  SetSymbolData(8, 2, 1, 1, 1, 1, 1);
  SetSymbolData(8, 3, 0, 0, 1, 0, 0);
  SetSymbolData(8, 4, 0, 0, 1, 0, 0);

  InitSymbol(9, 3, 3, 5, 5);
  SetSymbolData(9, 0, 0, 1, 1, 1, 0);
  SetSymbolData(9, 1, 1, 0, 1, 0, 1);
  SetSymbolData(9, 2, 1, 1, 1, 1, 1);
  SetSymbolData(9, 3, 1, 0, 1, 0, 1);
  SetSymbolData(9, 4, 0, 1, 1, 1, 0);

  InitSymbol(10, 2, 2, 4, 4);
  SetSymbolData(10, 0, 1, 1, 1, 1, 0);
  SetSymbolData(10, 1, 1, 0, 0, 1, 0);
  SetSymbolData(10, 2, 1, 0, 0, 1, 0);
  SetSymbolData(10, 3, 1, 1, 1, 1, 0);
  SetSymbolData(10, 4, 0, 0, 0, 0, 0);

  InitSymbol(11, 2, 2, 4, 4);
  SetSymbolData(11, 0, 1, 1, 1, 1, 0);
  SetSymbolData(11, 1, 1, 1, 1, 1, 0);
  SetSymbolData(11, 2, 1, 1, 1, 1, 0);
  SetSymbolData(11, 3, 1, 1, 1, 1, 0);
  SetSymbolData(11, 4, 0, 0, 0, 0, 0);

  InitSymbol(12, 3, 3, 5, 5);
  SetSymbolData(12, 0, 0, 1, 1, 1, 0);
  SetSymbolData(12, 1, 1, 0, 0, 0, 1);
  SetSymbolData(12, 2, 1, 0, 0, 0, 1);
  SetSymbolData(12, 3, 0, 1, 0, 1, 0);
  SetSymbolData(12, 4, 0, 0, 1, 0, 0);

  InitSymbol(13, 3, 3, 5, 5);
  SetSymbolData(13, 0, 0, 1, 1, 1, 0);
  SetSymbolData(13, 1, 1, 1, 1, 1, 1);
  SetSymbolData(13, 2, 1, 1, 1, 1, 1);
  SetSymbolData(13, 3, 0, 1, 1, 1, 0);
  SetSymbolData(13, 4, 0, 0, 1, 0, 0);

  InitSymbol(14, 3, 3, 5, 5);
  SetSymbolData(14, 0, 0, 0, 1, 0, 0);
  SetSymbolData(14, 1, 0, 1, 0, 1, 0);
  SetSymbolData(14, 2, 1, 0, 0, 0, 1);
  SetSymbolData(14, 3, 1, 0, 0, 0, 1);
  SetSymbolData(14, 4, 0, 1, 1, 1, 0);

  InitSymbol(15, 3, 3, 5, 5);
  SetSymbolData(15, 0, 0, 0, 1, 0, 0);
  SetSymbolData(15, 1, 0, 1, 1, 1, 0);
  SetSymbolData(15, 2, 1, 1, 1, 1, 1);
  SetSymbolData(15, 3, 1, 1, 1, 1, 1);
  SetSymbolData(15, 4, 0, 1, 1, 1, 0);

  InitSymbol(16, 3, 3, 5, 5);
  SetSymbolData(16, 0, 1, 0, 0, 0, 1);
  SetSymbolData(16, 1, 0, 1, 0, 1, 0);
  SetSymbolData(16, 2, 0, 0, 1, 0, 0);
  SetSymbolData(16, 3, 0, 1, 0, 1, 0);
  SetSymbolData(16, 4, 1, 0, 0, 0, 1);

  InitSymbol(17, 3, 3, 5, 5);
  SetSymbolData(17, 0, 1, 1, 0, 1, 1);
  SetSymbolData(17, 1, 1, 1, 0, 1, 1);
  SetSymbolData(17, 2, 0, 0, 1, 0, 0);
  SetSymbolData(17, 3, 1, 1, 0, 1, 1);
  SetSymbolData(17, 4, 1, 1, 0, 1, 1);
}

VOID StGWDrawSymbol(StGWWinInfo *gwinfo, int sym, int x, int y)
{
  WindowPtr w;
  GrafPtr savePort;
  Rect r;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  savePort = get_port();
  SetDrawingPort(w);
  
  if (! copying_to_clip && buffering && sym < 4 && ! gwinfo->use_color) {
    BufferMarkPoint(x - gwinfo->view_h, y - gwinfo->view_v, 
                    sym, gwinfo->drawColor);
    return;
  }

  if (sym < 0 || sym >= NUMSYMBOLS) return;
  x -= Symbols[sym].left;
  y -= Symbols[sym].top;
  r = Symbols[sym].map.bounds;
  OffsetRect(&r, x, y);
  
  CopyBits(&Symbols[sym].map, &thePort->portBits,
           &Symbols[sym].map.bounds, &r, gwinfo->symbolMode, nil);
           
  set_port(savePort);
}

static VOID BufferMarkPoint(unsigned x, unsigned y, unsigned sym, unsigned color)
{
  if (x >= WorkPortWidth || y >= WorkPortHeight) return;
  if (color != 0) {
    BitSet(WorkRowStarts[y], x);     if (sym == 0) return;
    BitSet(WorkRowStarts[y], x + 1); if (sym == 1) return;
    BitSet(WorkRowStarts[y + 1], x); if (sym == 2) return;
    BitSet(WorkRowStarts[y + 1], x + 1);
  }
  else {
    BitClr(WorkRowStarts[y], x);     if (sym == 0) return;
    BitClr(WorkRowStarts[y], x + 1); if (sym == 1) return;
    BitClr(WorkRowStarts[y + 1], x); if (sym == 2) return;
    BitClr(WorkRowStarts[y + 1], x + 1);
  }  
}

VOID StGWReplaceSymbol(StGWWinInfo *gwinfo, unsigned oldsym, unsigned newsym, int x, int y)
{
  int oldwidth, oldheight, newwidth, newheight;
  
  if (oldsym >= NUMSYMBOLS || newsym >= NUMSYMBOLS) return;
  
  StGWGetSymbolSize(oldsym, &oldwidth, &oldheight);
  StGWGetSymbolSize(newsym, &newwidth, &newheight);
  if (oldwidth > newwidth || oldheight > newheight)
    StGWEraseRect(gwinfo, x - Symbols[oldsym].left, y - Symbols[oldsym].top,
                  oldwidth, oldheight);
  StGWDrawSymbol(gwinfo, newsym, x, y);
}

/**************************************************************************/
/**                                                                      **/
/**                           Bitmap Functions                           **/
/**                                                                      **/
/**************************************************************************/

static make_bitmap(int width, int height, char *image, BitMap *pbm)
{
  int i, j;
  char *rowdata;
  
  pbm->rowBytes = 2 * ((width % 16 == 0) ? width / 16 : width / 16 + 1);
  if (pbm->rowBytes * height <= 0) return(FALSE);
  
  pbm->baseAddr = StCalloc(pbm->rowBytes * height, 1);
  if (pbm->baseAddr == nil) return(FALSE);
  
  for (i = 0; i < height; i++) {
    rowdata = pbm->baseAddr + i * pbm->rowBytes;
    for (j = 0; j < width; j++)
      if (image[i * width + j] != 0) BitSet(rowdata, j);
  }
  pbm->bounds.left = 0; pbm->bounds.top = 0;
  pbm->bounds.right = width; pbm->bounds.bottom = height;
  return(TRUE);
}

static VOID free_bitmap(BitMap *pbm)
{
  if (pbm->baseAddr != nil) StFree(pbm->baseAddr);
}
    
VOID StGWDrawBitmap(StGWWinInfo *gwinfo, int left, int top, int width, int height, char *image)
{
  WindowPtr w;
  BitMap bm;
  GrafPtr savePort;
  Rect r;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  savePort = get_port();
  SetDrawingPort(w);
  if (make_bitmap(width, height, image, &bm)) {
    r = bm.bounds;
    OffsetRect(&r, left, top);
    CopyBits(&bm, &thePort->portBits, &bm.bounds, &r, gwinfo->symbolMode, nil);
    free_bitmap(&bm);
  }
  set_port(savePort);
}

/**************************************************************************/
/**                                                                      **/
/**                         Buffering Functions                          **/
/**                                                                      **/
/**************************************************************************/

VOID StGWStartBuffering(StGWWinInfo *gwinfo)
{
  WindowPtr w;
  GrafPtr savePort;
  Rect r;

  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  bufflevel++;
  if (buffering) return;
  buffering = TRUE;
  
  GetPort(&savePort);
  SetPort((StGWUseColor(gwinfo)) ? (GrafPtr) CWorkPort : WorkPort);

  set_fore_color(gwinfo);
  set_back_color(gwinfo);
  if (gwinfo->lineType != 0) PenPat(&gray);
  else PenPat(&black);
  
  PenMode(w->pnMode);
  TextFont(w->txFont);
  TextFace(w->txFace);
  TextMode(w->txMode);
  TextSize(w->txSize);
  SpaceExtra(w->spExtra);
  SetOrigin(gwinfo->view_h, gwinfo->view_v);
  r = w->portRect;
  r.right -= 15;
  if (gwinfo->hasHscroll) r.bottom -= 15;
  ClipRect(&r);
  CopyRgn(w->visRgn, thePort->visRgn);
  CopyRgn(w->clipRgn, thePort->clipRgn);
  SetPort(savePort);
}

VOID StGWBufferToScreen(StGWWinInfo *gwinfo, int left, int top, int width, int height)
{
  WindowPtr w;
  GrafPtr savePort, workPort;
  Rect r;

  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  if (bufflevel > 0) bufflevel--;
  if (bufflevel > 0) return;
  if (! buffering) return;
  
  buffering = FALSE;  
  workPort = (StGWUseColor(gwinfo)) ? (GrafPtr) CWorkPort : WorkPort;
  
  GetPort(&savePort);
  SetPort(w);
  ForeColor(blackColor);
  BackColor(whiteColor);
  SetRect(&r, left, top, left + width, top + height);
  if (SectRect(&r, &w->portRect, &r))
    CopyBits(&workPort->portBits,&w->portBits, &r, &r, srcCopy, nil);
  set_fore_color(gwinfo);
  set_back_color(gwinfo);
  SetPort(savePort);
}

/**************************************************************************/
/**                                                                      **/
/**                         Miscelaneous Functions                       **/
/**                                                                      **/
/**************************************************************************/

VOID StGWCopyToClip(StGWWinInfo *gwinfo)
{
  make_picture(gwinfo, nil);
}

VOID StGWDumpImage(StGWWinInfo *gwinfo, char *fname, double scale)
{
  make_picture(gwinfo, fname);
}

static VOID make_picture(StGWWinInfo *gwinfo, char *fname)
{
  PicHandle pic;
  Rect r;
  long result, mytype;
  GrafPtr savePort, workPort;
  LVAL object;
  WindowPtr w;
/*  int vRefNum, i;
  FILE *fptr; */
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  object = StGWGetObject(gwinfo);
  workPort = (StGWUseColor(gwinfo)) ? (GrafPtr) CWorkPort : WorkPort;

  StGWStartBuffering(gwinfo);
  r = w->portRect;
  r.right -= 15;
  if (StGWHasHscroll(gwinfo)) r.bottom -= 15;
  
  copying_to_clip = TRUE;
  GetPort(&savePort);
  SetPort(workPort);
  pic = OpenPicture(&r);
  SetPort(savePort);
  
  StGWObRedraw(object);

  GetPort(&savePort);
  SetPort(workPort);
  ClosePicture();
  SetPort(savePort);
  
  StGWBufferToScreen(gwinfo, 0, 0, 0, 0);
  
  if (fname == nil) {
    ZeroScrap();
    mytype = 'PICT';
    HLock((Handle) pic);
    result = PutScrap(GetHandleSize((Handle) pic), mytype, (Ptr) *pic);
    HUnlock((Handle) pic);
  }
  else {
/*    if ((fptr = fopen(fname, "w")) == nil)
      xlfail("can't open file");
    GetPort(&savePort);
    SetPort(WorkPort);
    DrawPicture(pic, &r);
    SetPort(savePort);
    writemacp(fptr, WorkRowStarts, r.right - r.left, r.bottom - r.top);
	xlfail("operation not supported");
	spoolpict(fname, pic, WorkPort);*/
  }
  KillPicture(pic);
  copying_to_clip = FALSE;
}

VOID StGWResetBuffer(void)
{
  copying_to_clip = FALSE;
  bufflevel = 0;
  buffering = FALSE;  
}


syntax highlighted by Code2HTML, v. 0.9.1