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

/* forward declarations */
LOCAL VOID DoCursor _((StGWWinInfo *gwinfo));

StGWWinInfo *IViewWindowWinInfo(WindowPtr w)
{
  return((w != nil) ? (StGWWinInfo *) GetWRefCon(w) : nil);
}

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

VOID StInitGraphics()
{
  initialize_static_globals();
  init_mac_colors();
  init_mac_cursors();
}

/**************************************************************************/
/**                                                                      **/
/**                       Window Creation Functions                      **/
/**                                                                      **/
/**************************************************************************/

Rect scroll_bar_bounds(WindowPtr w, int which)
{
  Rect r;
  
  r = w->portRect;
  switch(which) {
  case 'H':
    r.top = r.bottom - 15; r.right -= 14; r.left--; r.bottom++; break;
  case 'V':
    r.left = r.right - 15; r.bottom -= 14; r.top--; r.right++; break;
  }
  return(r);
}

static int which_scroll, old_view_h, old_view_v;

static pascal void scroll_action(ControlHandle theCtl, short part)
{
  StGWWinInfo *gwinfo = (StGWWinInfo *) GetWRefCon(thePort);
  int value = GetControlValue(theCtl);
  int left, top, width, height, inc, pageInc;
  WindowPtr w = thePort;
  LVAL object;
  
  object = StGWGetObject(gwinfo);

  if (gwinfo == nil) return;
  
  switch (which_scroll) {
  case 'H': 
    inc = gwinfo->h_scroll_inc[0];
    pageInc = gwinfo->h_scroll_inc[1];
    break;
  case 'V': 
    inc = gwinfo->v_scroll_inc[0];
    pageInc = gwinfo->v_scroll_inc[1];
    break;
  }
  
  switch(part) {
  case kControlUpButtonPart: value -= inc; break;
  case kControlDownButtonPart: value += inc; break;
  case kControlPageUpPart: value -= pageInc; break;
  case kControlPageDownPart: value += pageInc; break;
  }
  switch (which_scroll) {
  case 'H': StGWSetScroll(gwinfo, value, gwinfo->view_v, FALSE); break;
  case 'V': StGWSetScroll(gwinfo, gwinfo->view_h, value, FALSE); break;
  }
  reset_clip_rect(gwinfo);
  StGWGetViewRect(gwinfo, &left, &top, &width, &height);
  StGWStartBuffering(gwinfo);
  StGWObRedraw(object);
  StGWBufferToScreen(gwinfo, left, top, width, height);
  SetPort(w);
  SetOrigin(old_view_h, old_view_v);
  ClipRect(&w->portRect);
}

static ControlActionUPP scroll_actionUPP = NULL;

static pascal void mouse_action(Point pt, long t, short mods)
{
  WindowPtr w = FrontWindow();
  StGWWinInfo *gwinfo = (StGWWinInfo *) GetWRefCon(w);
  ControlHandle theCtl;
  int part, h, v, option, extend;
  Rect r;
  LVAL object;
  
  object = StGWGetObject(gwinfo);

  if (gwinfo == nil) return;
  
  if ((part = FindControl(pt, w, &theCtl)) != 0) {
    if (theCtl == gwinfo->hscroll) which_scroll = 'H';
    else which_scroll = 'V';
    ClipRect(&w->portRect);
    if (part != 0 && part != kControlIndicatorPart) {
      old_view_h = GetControlValue(gwinfo->hscroll);
      old_view_v = GetControlValue(gwinfo->vscroll);
      if (! scroll_actionUPP)
        scroll_actionUPP = NewControlActionProc((ProcPtr) scroll_action);
      TrackControl(theCtl, pt, scroll_actionUPP);
      SetPort(w);
      SetOrigin(gwinfo->view_h, gwinfo->view_v);
      SetRect(&r, 0, 0, 0, 0);
      ClipRect(&r);
      r = scroll_bar_bounds(w, 'H');
      MoveControl(gwinfo->hscroll, r.left, r.top);
      r = scroll_bar_bounds(w, 'V');
      MoveControl(gwinfo->vscroll, r.left, r.top);
    }
    else if (part == kControlIndicatorPart) 
      if (TrackControl(theCtl, pt, nil) == kControlIndicatorPart) {
        h = GetControlValue(gwinfo->hscroll);
        v = GetControlValue(gwinfo->vscroll);
        StGWSetScroll(gwinfo, h, v, TRUE);
        reset_clip_rect(gwinfo);
        StGWObRedraw(object);
      }
    reset_clip_rect(gwinfo);
  }
  else {
    extend = BitAnd(mods, shiftKey);
    option = BitAnd(mods, optionKey);
    mods = (extend) ? ExtendModifier : NoModifiers;
    if (option) mods += 2;
    StGWObDoMouse(gwinfo->Object, pt.h, pt.v, MouseClick, (MouseClickModifier) mods);
  }
  SetPort(w);                 /* I don't know why this is here - maybe it is needed */
/*  ValidRect(&w->portRect);*//* This definitely causes problems */
}

static pascal void key_action(short key, short code, short mods)
{
  StGWWinInfo *gwinfo = (StGWWinInfo *) GetWRefCon(FrontWindow());
  LVAL object;
  int shift, opt;
  
  if (gwinfo == nil) return;
  object = StGWGetObject(gwinfo);
  shift = BitAnd(mods, shiftKey);
  opt = BitAnd(mods, optionKey);
  if (key == '\r') key = '\n';
  StGWObDoKey(object, key, shift, opt);
}

VOID graph_update_action(StGWWinInfo *gwinfo, int resized)
{
  WindowPtr w;
  int width, height;
  GrafPtr savePort;
  Rect r;
  LVAL object;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  object = StGWGetObject(gwinfo);

  GetPort(&savePort);
  SetPort(w);
  if (! gwinfo->initialized) {
    resized = TRUE;
    gwinfo->initialized = TRUE;
  }
  
  if (resized) {
    reset_clip_rect(gwinfo);
    EraseRect(&w->portRect);
    StGWGetViewRect(gwinfo, nil, nil, &width, &height);
    
    if (gwinfo->hasHscroll) {
      HideControl(gwinfo->hscroll);
      width = (gwinfo->canvasWidth > width) ? gwinfo->canvasWidth - width : 0;
      SetControlMaximum(gwinfo->hscroll, width);
      SetControlValue(gwinfo->hscroll, gwinfo->view_h);
      gwinfo->view_h = GetControlValue(gwinfo->hscroll);
    }

    if (gwinfo->hasVscroll) {
      HideControl(gwinfo->vscroll);
      height = (gwinfo->canvasHeight > height) ? gwinfo->canvasHeight - height : 0;
      SetControlMaximum(gwinfo->vscroll, height);
      SetControlValue(gwinfo->vscroll, gwinfo->view_v);
      gwinfo->view_v = GetControlValue(gwinfo->vscroll);
    }

    SetOrigin(gwinfo->view_h, gwinfo->view_v);
    r = scroll_bar_bounds(w, 'H');
    MoveControl(gwinfo->hscroll, r.left, r.top);
    SizeControl(gwinfo->hscroll, r.right - r.left, r.bottom - r.top);
    r = scroll_bar_bounds(w, 'V');
    MoveControl(gwinfo->vscroll, r.left, r.top);
    SizeControl(gwinfo->vscroll, r.right - r.left, r.bottom - r.top);
    ClipRect(&w->portRect);
    if (gwinfo->hasHscroll) ShowControl(gwinfo->hscroll);
    if (gwinfo->hasVscroll) ShowControl(gwinfo->vscroll);

    reset_clip_rect(gwinfo);
  }
  
  if (resized && gwinfo != nil) {
    if (! gwinfo->hasHscroll)
      gwinfo->canvasWidth = w->portRect.right
                         - w->portRect.left - 15;
    if (! gwinfo->hasVscroll) {
      gwinfo->canvasHeight = w->portRect.bottom
                          - w->portRect.top;
      if (gwinfo->hasHscroll) gwinfo->canvasHeight -= 15;
    }
    StGWObResize(object);
  }
  
  StGWObRedraw(object);
  
  DrawGWGrowBox(gwinfo);
  SetPort(w);
  ClipRect(&w->portRect);
  DrawControls(w);
  reset_clip_rect(gwinfo);
  SetHardwareState(gwinfo);
  SetPort(w);
}

VOID DrawGWGrowBox(StGWWinInfo *gwinfo)
{
  WindowPtr w;
  Rect r;
  GrafPtr savePort;
  int reverse;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  reverse = (StGWBackColor(gwinfo) != 0);
  GetPort(&savePort);
  SetPort(w);
    
  r = w->portRect;
  r.left = r.right - 15;
  ClipRect(&r);
  ForeColor(blackColor);
  BackColor(whiteColor);
  if (! StGWHasVscroll(gwinfo)) EraseRect(&r);
  DrawGrowIcon(w);
  if (reverse) {
    r = w->portRect;
    r.left = r.right - 15;
    if (StGWHasVscroll(gwinfo)) r.top = r.bottom - 15;
    if (! StGWHasVscroll(gwinfo)) InvertRect(&r);
  }
  set_fore_color(gwinfo);
  set_back_color(gwinfo);
  
  reset_clip_rect(gwinfo);
  SetHardwareState(gwinfo);
  SetPort(savePort);
}

VOID graph_activate_action(StGWWinInfo *gwinfo, int active)
{
  WindowPtr w;
  Point pt;
  int value;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  if (active) DoCursor(gwinfo);
  else SetCursor(&arrow);
  
  GetMouse(&pt);
  gwinfo->mouse_x = pt.h; gwinfo->mouse_y = pt.v;
  DrawGWGrowBox(gwinfo);
  
  SetPort(w);
  ClipRect(&w->portRect);
  value = (active) ? 0 : 255;
  HiliteControl(gwinfo->hscroll, value);
  HiliteControl(gwinfo->vscroll, value);
  reset_clip_rect(gwinfo);
}

static pascal void clobber_action(void)
{
  WindowPtr wind = thePort;
  StGWWinInfo *gwinfo = (StGWWinInfo *) GetWRefCon(thePort);

  if (IViewInternalIsLinked(wind)) IViewUnlinkWindow(wind);
  StGWObDoClobber(gwinfo->Object);
  if (gwinfo != nil && gwinfo->FreeMem != nil) (*gwinfo->FreeMem)(wind);

  gwinfo->window = nil;

  DisposeWindow(wind);
}

static pascal void idle_action(void) 
{
  WindowPtr wind = thePort;
  StGWWinInfo *gwinfo = (StGWWinInfo *) GetWRefCon(wind);
  Point pt;
  int old_x, old_y;
  
  if (gwinfo != nil && wind == FrontWindow()) {
    DoCursor(gwinfo);
    old_x = gwinfo->mouse_x; old_y = gwinfo->mouse_y;
    GetMouse(&pt);
    gwinfo->mouse_x = pt.h; gwinfo->mouse_y = pt.v;
    if ((abs(gwinfo->mouse_x - old_x) > MOUSE_TOLERANCE
         || abs(gwinfo->mouse_y - old_y) > MOUSE_TOLERANCE)) {
      GetMouse(&pt);
      gwinfo->mouse_x = pt.h; gwinfo->mouse_y = pt.v;     
      StGWObDoMouse(gwinfo->Object, pt.h, pt.v, MouseMove, NoModifiers);
    }
  }
  if (gwinfo != nil && gwinfo->idleOn) StGWObDoIdle(gwinfo->Object);
}

int StGWWinInfoSize(void) { return(sizeof(StGWWinInfo)); }

VOID StGWInitWinInfo(LVAL object)
{
  StGWWinInfo *gwinfo = (StGWWinInfo *) StGWObWinInfo(object);

  gwinfo->Object = object;
  gwinfo->initialized = FALSE;
  gwinfo->symbolMode = srcCopy;
  gwinfo->canvasWidth = 0;
  gwinfo->canvasHeight = 0;
  gwinfo->hasHscroll = FALSE;
  gwinfo->hasVscroll = FALSE;
  gwinfo->view_h = 0;
  gwinfo->view_v = 0;
  gwinfo->h_scroll_inc[0] = 1; gwinfo->h_scroll_inc[1] = 50;
  gwinfo->v_scroll_inc[0] = 1; gwinfo->v_scroll_inc[1] = 50;
  gwinfo->lineType = 0;
  gwinfo->drawMode = 0;
  gwinfo->backColor = 0;
  gwinfo->drawColor = 1;
  gwinfo->lineWidth = 1;
  gwinfo->window = nil;
  gwinfo->idleOn = FALSE;
  gwinfo->use_color = FALSE;
  gwinfo->cursor = 0;
  gwinfo->RefCon = (long) nil;
}

WindowPtr IViewWindowNew(LVAL object, int is_GW)
{
  char *title;
  int left, top, width, height, goAway;
  StGWWinInfo *gwinfo;
  WindowPtr wind;
  Rect r;
  Str255 pbuf;
  
  StGWGetAllocInfo(object, &title, &left, &top, &width, &height, &goAway);
  if (title == nil || strlen(title) <= 0) title = "Graph Window";
  
  top += GetMBarHeight();
  SetRect(&r, left, top, left + width + 15, top + height);
              
  CintoPstring(title, pbuf, sizeof pbuf, FALSE);
  if (StScreenHasColor())
    wind = NewCWindow(nil, &r, pbuf, FALSE, 8, (WindowPtr) -1L, goAway, 0L);
  else  
    wind = NewWindow(nil, &r, pbuf, FALSE, 8, (WindowPtr) -1L, goAway, 0L);
  if (wind == nil) xlfail("allocation Failed");
  
  if (! SkelWindow(wind, mouse_action, key_action, mac_update_action, 
	               mac_activate_action, mac_close_action, clobber_action,
	               idle_action, FALSE))
    xlfail("allocation Failed");
     
  SetPort(wind);
  if (graphFontNum)
    TextFont(graphFontNum);
  TextSize(graphFontSize);
  TextFace(graphFontStyle);
  TextMode(srcXor);
  
  gwinfo = (StGWWinInfo *) StGWObWinInfo(object);
  
  r = wind->portRect;
  r.top = r.bottom - 15; r.right -= 14; r.left--; r.bottom++;
  gwinfo->hscroll = NewControl(wind, &r, "\p", FALSE, 0, 0, 0, 
                              scrollBarProc, 0L);
  r = wind->portRect;
  r.left = r.right - 15; r.bottom -= 14; r.top--; r.right++;
  gwinfo->vscroll = NewControl(wind, &r, "\p", FALSE, 0, 0, 0, 
                              scrollBarProc, 0L);

  gwinfo->window = wind;
  if (! gwinfo->hasHscroll) gwinfo->canvasWidth = width;
  if (! gwinfo->hasVscroll) gwinfo->canvasHeight = height;
  gwinfo->initialized = FALSE;

  SetWRefCon(wind, (long) gwinfo);
  SetHardwareState(gwinfo);
  StGWEraseRect(gwinfo, 0, 0, width, height);
  StGWSetClipRect(gwinfo, FALSE, 0, 0, 0, 0);
  
  if (is_GW) set_iview_window_address((CPTR) wind, object);
  else set_iview_address((CPTR) wind, object);

  return(wind);
}

/**************************************************************************/
/**                                                                      **/
/**                          Clipping  Functions                         **/
/**                                                                      **/
/**************************************************************************/

VOID StGWSetClipRect(StGWWinInfo *gwinfo, int clipped, int left, int top, int width, int height)
{
  if (gwinfo == nil) return;
  gwinfo->clipped = clipped;
  if (clipped) {
    SetRect(&gwinfo->clip_rect, left, top, left + width, top + height);
  }
  reset_clip_rect(gwinfo);
}

int StGWGetClipRect(StGWWinInfo *gwinfo, int *left, int *top, int *width, int *height)
{
  if (gwinfo == nil) return(FALSE);
  if (gwinfo->clipped) {
    if (left != nil) *left = gwinfo->clip_rect.left;
    if (top != nil) *top = gwinfo->clip_rect.top;
    if (width != nil) *width = gwinfo->clip_rect.right - gwinfo->clip_rect.left;
    if (height != nil) *height = gwinfo->clip_rect.bottom - gwinfo->clip_rect.top;
  }
  return(gwinfo->clipped);
}

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

#define NumBasicCursors 9
static int NumCursors;

typedef struct {
  CursHandle curs;
  long refcon;
} cursor_entry;

static cursor_entry *curstab;

VOID init_mac_cursors(void)
{
  NumCursors = NumBasicCursors;
  curstab = (cursor_entry *) StCalloc(NumCursors, sizeof(cursor_entry));
}

VOID StGWSetCursRefCon(unsigned int index, long rc)
{
  if (index < NumCursors) curstab[index].refcon = rc;
}

long StGWGetCursRefCon(unsigned int index)
{	
  if (index < NumCursors) return(curstab[index].refcon);
  else return((long) nil);
}

static VOID set_image_bits(int n, char *bits, char *image)
{
  int i;
  
  for (i = 0; i < n; i++) {
    if (image[i] == 0) BitClr(bits, i);
    else BitSet(bits, i);
  }
}

static VOID clear_image_bits(int n, char *bits)
{
  int i;
  
  for (i = 0; i < n; i++) BitClr(bits, i);
}
  
int StGWMakeCursor(int n, char *image, char *mask, int h, int v, long refcon)
{
  int index;
  char *temp;
  CursHandle curs;
  
  if (n != 16 || image == nil) return(-1);
  for (index = 0; index < NumCursors && StGWGetCursRefCon(index) != (long) nil; index++);
  if (index >= NumCursors) {
    temp = realloc(curstab, (NumCursors + 1) * sizeof(cursor_entry));
    if (temp == nil) return(-1);
    curstab = (cursor_entry *) temp;
    NumCursors++;
    curstab[index].curs = nil;
    curstab[index].refcon = (long) nil;
  }
  if (curstab[index].curs != nil) DisposeHandle((Handle) curstab[index].curs);
  curs = (CursHandle) NewHandle(sizeof(Cursor));
  if (curs == nil) return(-1);
  
  if (mask == nil) clear_image_bits(n * n, (char *) (*curs)->mask);
  else set_image_bits(n * n, (char *) (*curs)->mask, mask);
  set_image_bits(n * n, (char *) (*curs)->data, image);
  (*curs)->hotSpot.h = h;
  (*curs)->hotSpot.v = v;
  curstab[index].curs = curs;
  curstab[index].refcon = refcon;
  return(index);
}

int StGWMakeResCursor(char *name, int num, long refcon)
{
  int index;
  char *temp;
  CursHandle curs, curs_copy;
  
  for (index = 0; index < NumCursors && StGWGetCursRefCon(index) != (long) nil; index++);
  if (index >= NumCursors) {
    temp = realloc(curstab, (NumCursors + 1) * sizeof(cursor_entry));
    if (temp == nil) return(-1);
    curstab = (cursor_entry *) temp;
    NumCursors++;
    curstab[index].curs = nil;
    curstab[index].refcon = (long) nil;
  }
  if (curstab[index].curs != nil) DisposeHandle((Handle) curstab[index].curs);
  
  if (name != nil) {
    Str255 pbuf;
    CintoPstring(name, pbuf, sizeof pbuf, FALSE);
    curs = (CursHandle) GetNamedResource('CURS', pbuf);
  }
  else curs = GetCursor(num);
  
  if (curs == nil) return(-1);
  curs_copy = (CursHandle) NewHandle(sizeof(Cursor));
  if (curs_copy == nil) return(-1);
  **curs_copy = **curs;
  
  curstab[index].curs = curs_copy;
  curstab[index].refcon = refcon;
  return(index);
}

VOID StGWFreeCursor(unsigned int index)
{
  if (index < NumCursors && index >= NumBasicCursors) {
    if (curstab[index].curs != nil)
      DisposeHandle((Handle) curstab[index].curs);
    curstab[index].curs = nil;
    curstab[index].refcon = (long) nil;
  }
  else xlfail("can't free standard cursor");
}

static CursHandle get_cursor(unsigned int index)
{
  CursHandle curs = nil;
  
  if (index < NumBasicCursors) {
    switch (index) {
    case ARROW_CURSOR:      curs = nil;                    break;
    case WATCH_CURSOR:      curs = GetCursor(watchCursor); break;
    case CROSS_CURSOR:      curs = GetCursor(crossCursor); break;
    case BRUSH_CURSOR:      curs = GetCursor(BRUSH_RES);   break;
    case HAND_CURSOR:       curs = GetCursor(HAND_RES);    break;
    case FINGER_CURSOR:     curs = GetCursor(FINGER_RES);  break;
    case HOUR_GLASS_CURSOR: curs = GetCursor(GLASS_RES);   break;
    case TRASH_BAG_CURSOR:  curs = GetCursor(BAG_RES);     break;
    case TRASH_CAN_CURSOR:  curs = GetCursor(CAN_RES);     break;
    }
  }
  else if (index < NumCursors)
    curs = curstab[index].curs;
    
  return(curs);
}
  
VOID mac_do_cursor(StGWWinInfo *gwinfo) { DoCursor(gwinfo); }

LOCAL VOID DoCursor(StGWWinInfo *gwinfo)
{
  int left, top, width, height;
  Point pt;
  CursHandle curs = nil;
  WindowPtr w;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  GetMouse(&pt);
  StGWGetViewRect(gwinfo, &left, &top, &width, &height);
    
  if (w == FrontWindow()
      && pt.h > left && pt.h < left + width
      && pt.v > top && pt.v < top + height)
    curs = get_cursor(StGWCursor(gwinfo));
  if (curs != nil) SetCursor(*curs);
  else SetCursor(&arrow);
}

VOID StGWSetCursor(StGWWinInfo *gwinfo, int cursor)
{
  if (gwinfo == nil) return;
  gwinfo->cursor = cursor;
}

int StGWCursor(StGWWinInfo *gwinfo)
{
  if (gwinfo == nil) return(FALSE);
  return(gwinfo->cursor);
}



syntax highlighted by Code2HTML, v. 0.9.1