/* xsiviewinter - XLISP interface to IVIEW dynamic graphics package.   */
/* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney                  */
/* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz    */
/* You may give out copies of this software; for conditions see the    */
/* file COPYING included with this distribution.                       */

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

/* external variables */
extern LVAL s_true, sk_adjust_points_in_rect, sk_unselect_all_points,
  k_set_mode_cursor;

/* symbol pointers */
extern LVAL s_selecting, s_brushing, s_user, sk_mark_points_in_rect,
  sk_adjust_screen, s_number_of_variables, sk_variable_labels,
  s_variable_labels, sk_allocate, sk_showing_labels, s_showing_labels,
  s_fixed_aspect, s_mouse_mode, sk_overlay_click, sk_new_menu,
  s_invisible, s_normal, s_hilited, s_selected, sk_show, sk_show_window,
  sk_adjust_screen_point, s_hardware_address, s_mode_list, s_scale_type,
  sk_scale, s_fixed, s_variable, sk_draw, sk_resize, sk_redraw;

/*forward declarations */
LOCAL VOID get_iview_keys _((LVAL object));
LOCAL int OverlayMouse _((LVAL object));
LOCAL VOID set_mode_cursor _((LVAL object));
LOCAL LVAL encode_point_state _((PointState state));


/**************************************************************************/
/**                                                                      **/
/**                       IView Creation Functions                       **/
/**                                                                      **/
/**************************************************************************/

/* :ISNEW message for GRAPH-PROTO */
LVAL iview_isnew(V)
{
  LVAL object, vars, args;
  int show;
  
  object = xlgaobject();
  vars = xlgafixnum();
  if (getfixnum(vars) < 0) xlerror("not a nonnegative integer", vars);
  else set_slot_value(object, s_number_of_variables, vars);
  show = (xsboolkey(sk_show, TRUE)) ? TRUE : FALSE;

  object_isnew(object);
  get_iview_keys(object);
  initialize_graph(object);
  send_message(object, sk_new_menu);

  xlsave1(args);
  if (show) args = consa(s_true);
  else args = consa(NIL);
  args = cons(sk_show, args);
  apply_send(object, sk_allocate, args);
  xlpop();
  
  return(object);
}

VOID initialize_graph P1C(LVAL, object)
{
  initialize_graph_window(object);
}

/* :ALLOCATE message for GRAPH-PROTO */
LVAL iview_allocate(V)
{
  LVAL object;
  IVIEW_WINDOW w;
  int show;
  
  object = xlgaobject();
  show = xsboolkey(sk_show, TRUE);

  w = IViewNew(object);
  initialize_iview(w, object);
  /* use StShowWindow to show (map) window but NOT send :resize or :redraw */
  if (show) StShowWindow(w);
  
  return(object);
}

LOCAL VOID get_iview_keys P1C(LVAL, object)
{
  LVAL arg;
  
  if (xlgetkeyarg(sk_variable_labels, &arg))
    set_slot_value(object, s_variable_labels, coerce_to_tvec(arg, s_true));
  if (xlgetkeyarg(sk_scale, &arg)) {
    if (arg != s_fixed && arg != s_variable) arg = NIL;
    set_slot_value(object, s_scale_type, arg);
  }
}

VOID IViewMarkPointsInRect P5C(IVIEW_WINDOW, w, int, left, int, top, int, width, int, height)
{
  LVAL argv[6], Lleft, Ltop, Lwidth, Lheight;
  
  xlstkcheck(4);
  xlsave(Lleft);
  xlsave(Ltop);
  xlsave(Lwidth);
  xlsave(Lheight);
  argv[0] = IViewWindowGetObject(w);
  argv[1] = sk_mark_points_in_rect;
  argv[2] = Lleft = cvfixnum((FIXTYPE) left);
  argv[3] = Ltop = cvfixnum((FIXTYPE) top);
  argv[4] = Lwidth = cvfixnum((FIXTYPE) width);
  argv[5] = Lheight = cvfixnum((FIXTYPE) height);
  xscallsubrvec(xmsend, 6, argv);
  xlpopn(4);
}

LOCAL LVAL encode_point_state P1C(PointState, state)
{
  switch (state) {
  case pointInvisible: return(s_invisible);
  case pointNormal:    return(s_normal);
  case pointHilited:   return(s_hilited);
  case pointSelected:  return(s_selected);
  default: xlfail("unknown point state");
  }
  /* not reached */
  return(NIL);
}

VOID IViewAdjustPointsInRect P6C(IVIEW_WINDOW, w, int, left, int, top, int, width, int, height,
                                 PointState, state)
{
  LVAL argv[7], Lleft, Ltop, Lwidth, Lheight;
  
  xlstkcheck(4);
  xlsave(Lleft);
  xlsave(Ltop);
  xlsave(Lwidth);
  xlsave(Lheight);
  argv[0] = IViewWindowGetObject(w);
  argv[1] = sk_adjust_points_in_rect;
  argv[2] = Lleft = cvfixnum((FIXTYPE) left);
  argv[3] = Ltop = cvfixnum((FIXTYPE) top);
  argv[4] = Lwidth = cvfixnum((FIXTYPE) width);
  argv[5] = Lheight = cvfixnum((FIXTYPE) height);
  argv[6] = encode_point_state(state);
  xscallsubrvec(xmsend, 7, argv);
  xlpopn(4);
}

VOID IViewAdjustOwnScreenPoint P2C(IVIEW_WINDOW, w, int, point)
{
  LVAL object = IViewWindowGetObject(w);

  if (objectp(object))
    send_message1(object, sk_adjust_screen_point, point);
}

VOID IViewUnselectAllPoints P1C(IVIEW_WINDOW, w)
{
  LVAL object = IViewWindowGetObject(w);

  if (objectp(object))
    send_message(object, sk_unselect_all_points);
}

VOID initialize_iview P2C(IVIEW_WINDOW, w, LVAL, object)
{
  int i, vars, len;
  LVAL labels;
  MouseMode mode;
  
  labels = slot_value(object, s_variable_labels);
  if (vectorp(labels)) {
    len = getsize(labels);
    vars = IViewNumVariables(w);
    for (i = 0; i < len && i < vars; i++) {
      if (stringp(getelement(labels, i)))
        IViewSetVariableLabel(w, i, (char *) getstring(getelement(labels, i)));
    }
  }
  else set_slot_value(object, s_variable_labels, NIL);
  
  if (slot_value(object, s_showing_labels) != NIL)
    IViewSetShowingLabels(w, TRUE);

  IViewSetFixedAspect(w, (slot_value(object, s_fixed_aspect) != NIL));

  if (slot_value(object, s_mouse_mode) == s_brushing) mode = brushing;
  else if (slot_value(object, s_mouse_mode) == s_selecting) mode = selecting;
  else mode = usermode;
  IViewSetMouseMode(w, mode);
}

VOID get_iview_ivars P2C(LVAL, object, int *, vars)
{
  LVAL num_variables;
  
  num_variables = slot_value(object, s_number_of_variables);
  if (! fixp(num_variables) || getfixnum(num_variables) < 0)
	xlerror("number of variables is not a nonnegative integer", num_variables);
  *vars = getfixnum(num_variables);
}

/* :SHOW-WINDOW message for GRAPH-PROTO */
LVAL iview_window_show_window(V)
{
  LVAL object = xlgaobject();
  IVIEW_WINDOW w = (IVIEW_WINDOW) GETWINDOWADDRESS(object);
  StGWWinInfo *gwinfo = StGWObWinInfo(object);

  if (IVIEW_WINDOW_NULL(w)) send_message(object, sk_allocate);
  StGWShowWindow(gwinfo);
  return(NIL);
}

/**************************************************************************/
/**                                                                      **/
/**                 IView State Accessors and Mutators                   **/
/**                                                                      **/
/**************************************************************************/

static LVAL state_access P1C(int, which)
{
  StGWWinInfo *gwinfo;
  LVAL list, arg, object;
  int a, b, c, d, set = FALSE;
  
  object = xlgaobject();
  gwinfo = StGWObWinInfo(object);
  if (moreargs()) {
    set = TRUE;
    a = getfixnum(xlgafixnum());
    b = getfixnum(xlgafixnum());
    if (which == 'R' || which == 'M') {
      c = getfixnum(xlgafixnum());
      d = getfixnum(xlgafixnum());
    }
  }
  
  if (set)
    switch (which) {
    case 'R': StGrSetContentRect(gwinfo, a, b, c, d); break;
    case 'O': StGrSetContentOrigin(gwinfo, a, b);     break;
    case 'V': StGrSetContentVariables(gwinfo, a, b);  break;
    case 'C': StGrSetClickRange(gwinfo, a, b);        break;
    case 'M': 
      StGrSetMargin(gwinfo, a, b, c, d);
      if (! xlgetkeyarg(sk_draw, &arg)) arg = s_true;
      if (arg != NIL) send_message(object, sk_resize);
      if (arg != NIL) send_message(object, sk_redraw);  
      break;
    }
    
  switch (which) {
  case 'R': StGrGetContentRect(gwinfo, &a, &b, &c, &d); break;
  case 'O': StGrGetContentOrigin(gwinfo, &a, &b);       break;
  case 'V': StGrGetContentVariables(gwinfo, &a, &b);    break;
  case 'C': StGrGetClickRange(gwinfo, &a, &b);          break;
  case 'M': StGrGetMargin(gwinfo, &a, &b, &c, &d);      break;
  }
  
  if (which == 'R' || which == 'M') list = integer_list_4(a, b, c, d);
  else list = integer_list_2(a, b);
  
  return(list);
}

LVAL iview_content_rect(V)      { return(state_access('R')); }
LVAL iview_content_origin(V)    { return(state_access('O')); }
LVAL iview_content_variables(V) { return(state_access('V')); }
LVAL iview_click_range(V)       { return(state_access('C')); }

LVAL iview_mouse_mode(V)
{
  IVIEW_WINDOW w;
  LVAL modesym, object;
  MouseMode mode;
  int set = FALSE;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) get_iview_address(object);
  if (moreargs()) {
    set = TRUE;
    modesym = xlgasymbol();
  }
  else modesym = NIL; /* to keep compiler happy */
  xllastarg();
  
  if (set) {
    if (modesym == s_selecting) mode = selecting;
    else if (modesym == s_brushing) mode = brushing;
    else mode = usermode;
    set_slot_value(object, s_mouse_mode, modesym);
    IViewSetMouseMode(w, mode);
    set_mode_cursor(object);
  }
  
  switch (IViewMouseMode(w)) {
  case selecting: modesym = s_selecting; break;
  case brushing:  modesym = s_brushing;  break;
  default: modesym = slot_value(object, s_mouse_mode);
  }
  
  return(modesym);
}

LVAL iview_showing_labels(V)
{
  IVIEW_WINDOW w;
  int set = FALSE, show = FALSE;
  LVAL object;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) get_iview_address(object);
  if (moreargs()) {
    set = TRUE;
    show = (xlgetarg() != NIL) ? TRUE : FALSE;
  }
  xllastarg();
  
  if (set)  IViewSetShowingLabels(w, show);
  return((IViewShowingLabels(w)) ? s_true : NIL);
}

LVAL iview_margin(V)      { return(state_access('M')); }

LVAL iview_fixed_aspect(V)
{
  IVIEW_WINDOW w;
  LVAL object;
  int set = FALSE, fixed = FALSE;
  
  object = xlgaobject();
  if (moreargs()) {
    set = TRUE;
    fixed = (xlgetarg() != NIL) ? TRUE : FALSE;
  }
  xllastarg();
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  
  if (! IVIEW_WINDOW_NULL(w)) {
    if (set) {
      set_slot_value(object, s_fixed_aspect, (fixed) ? s_true : NIL);
      IViewSetFixedAspect(w, fixed);
      StGWObResize(object);
      StGWObRedraw(object);
    }
    return((IViewFixedAspect(w)) ? s_true : NIL);
  }
  else {
    if (set) set_slot_value(object, s_fixed_aspect, (fixed) ? s_true : NIL);
    return(slot_value(object, s_fixed_aspect));
  }
}

LVAL iview_dirty(V)
{
  StGWWinInfo *gwinfo;
  LVAL object;
  int set;
  
  object = xlgaobject();
  set = moreargs();
  gwinfo = StGWObWinInfo(object);
  if (set) StGrSetDirty(gwinfo, (xlgetarg() != NIL) ? TRUE : FALSE);
  return(StGrDirty(gwinfo) ? s_true : NIL);
}

/**************************************************************************/
/**                                                                      **/
/**                            Axis Functions                            **/
/**                                                                      **/
/**************************************************************************/

static LVAL iview_axis P1C(int, which)
{
  IVIEW_WINDOW w;
  int showing, labeled, ticks, set = FALSE, draw;
  LVAL object, temp, result;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) get_iview_address(object);
  if (moreargs()) {
    set = TRUE;
    showing = (xlgetarg() != NIL) ? TRUE : FALSE;
    labeled = (moreargs() && xlgetarg() != NIL) ? TRUE : FALSE;
    ticks = (moreargs()) ? getfixnum(xlgafixnum()) : 4;
  }
  
  if (set) {
    switch (which) {
    case 'X': IViewSetXaxis(w, showing, labeled, ticks); break;
    case 'Y': IViewSetYaxis(w, showing, labeled, ticks); break;
    }
    draw = draw_key_arg(TRUE);
    StGWObResize(object);
    check_redraw(object, draw, FALSE);
  }
  
  switch(which) {
  case 'X': IViewGetXaxis(w, &showing, &labeled, &ticks); break;
  case 'Y': IViewGetYaxis(w, &showing, &labeled, &ticks); break;
  }
  
  xlstkcheck(2);
  xlsave(result);
  xlsave(temp);
  temp = cvfixnum((FIXTYPE) ticks); result = consa(temp);
  temp = (labeled) ? s_true : NIL; result = cons(temp, result);
  temp = (showing) ? s_true : NIL; result = cons(temp, result);
  xlpopn(2);

  return(result);
}

LVAL iview_x_axis(V) { return(iview_axis('X')); }
LVAL iview_y_axis(V) { return(iview_axis('Y')); }

/**************************************************************************/
/**                                                                      **/
/**                           Brush Functions                            **/
/**                                                                      **/
/**************************************************************************/

static LVAL brush P1C(int, which)
{
  IVIEW_WINDOW w;
  int x, y, width, height, set = FALSE;
  LVAL result;
  
  w = (IVIEW_WINDOW) get_iview_address(xlgaobject());
  if (which == 'B' && moreargs()) {
    set = TRUE;
    x = getfixnum(xlgafixnum());
    y = getfixnum(xlgafixnum());
    width = getfixnum(xlgafixnum());
    height = getfixnum(xlgafixnum());
  }
  else if (which == 'M') {
    x = getfixnum(xlgafixnum());
    y = getfixnum(xlgafixnum());
  }
  xllastarg();
  
  if (set) IViewSetBrush(w, x, y, width, height);
  
  switch (which) {
  case 'B': IViewGetBrush(w, &x, &y, &width, &height); break;
  case 'E': IViewEraseBrush(w); break;
  case 'D': IViewDrawBrush(w); break;
  case 'M': IViewMoveBrush(w, x, y); break;
  }
  
  if (which == 'B') result = integer_list_4(x, y, width, height);
  else result = NIL;
  
  return(result);
}

LVAL iview_brush(V)       { return(brush('B')); }
LVAL iview_erase_brush(V) { return(brush('E')); }
LVAL iview_draw_brush(V)  { return(brush('D')); }
LVAL iview_move_brush(V)  { return(brush('M')); }

LVAL iview_resize_brush(V)
{
  IVIEW_WINDOW w;
  int x, y, width, height, new_width, new_height, changed;
  
  w = (IVIEW_WINDOW) get_iview_address(xlgaobject());
  changed = IViewGetNewBrushSize(w, &new_width, &new_height);
  if (changed) {
    IViewGetBrush(w, &x, &y, &width, &height);
    IViewSetBrush(w, x, y, new_width, new_height);
  }
  return((changed) ? s_true : NIL);
}

/**************************************************************************/
/**                                                                      **/
/**                      Mouse Action Functions                          **/
/**                                                                      **/
/**************************************************************************/

static LVAL get_mouse_selector P2C(LVAL, object, MouseEventType, type)
{
  LVAL selector, mode, list, entry;
  
  mode = slot_value(object, s_mouse_mode);
  list = slot_value(object, s_mode_list);
  for (selector = NIL; consp(list); list = cdr(list)) {
    entry = car(list);
    if (consp(entry) && car(entry) == mode) {
      if (type == MouseClick) {
	if (llength(entry) >= 4) selector = car(cdr(cdr(cdr(entry))));
      }
      else {
	if (llength(entry) >= 5) selector = car(cdr(cdr(cdr(cdr(entry)))));
      }
      break;
    }
  }
  return(selector);
}
  
VOID IViewDoClick P1C(LVAL, object)
{
  LVAL selector = get_mouse_selector(object, MouseClick);

  if (! OverlayMouse(object) && !null(selector))
    send_message_stk(object, selector);
}

VOID IViewDoMotion P1C(LVAL, object)
{
  LVAL selector = get_mouse_selector(object, MouseMove);

  if (!null(selector))
    send_message_stk(object, selector);
}

LOCAL int OverlayMouse P1C(LVAL, object)
{
  LVAL argv[6], result;
  int i;

  argv[0] = object;
  argv[1] = sk_overlay_click;
  for (i = 0; i < 4 && i < xlargc; i++)
    argv[i + 2] = xlargv[i];
  result = xscallsubrvec(xmsend, 6, argv);
  return(result != NIL);  
}

LVAL iview_do_click(V)
{
  LVAL object;
  
  object = xlgaobject();
  IViewDoClick(object);
  return(NIL);
}

LVAL iview_do_motion(V)
{
  LVAL object;
  
  object = xlgaobject();
  IViewDoMotion(object);
  return(NIL);
}

static LVAL iview_std_mouse P1C(MouseEventType, type)
{
  IVIEW_WINDOW w;
  int x, y;
  MouseClickModifier mods;
  
  w = (IVIEW_WINDOW) get_iview_address(xlgaobject());
  x = getfixnum(xlgafixnum());
  y = getfixnum(xlgafixnum());
  if (type == MouseClick) {
    mods = (MouseClickModifier) ((xlgetarg() != NIL) ? 1 : 0);
    if (xlgetarg() != NIL) mods = (MouseClickModifier) (((int) mods) + 2);
  }
  else mods = (MouseClickModifier) 0; /* to keep compiler happy */
  xllastarg();
  
  IViewStdMouseAction(w, x, y, type, mods);
  return(NIL);
}

LVAL iview_std_click(V)  { return(iview_std_mouse(MouseClick)); }
LVAL iview_std_motion(V) { return(iview_std_mouse(MouseMove));  }

static LVAL multi_state_set P1C(int, which)
{
  IVIEW_WINDOW w;
  int result = FALSE;
  
  w = (IVIEW_WINDOW) get_iview_address(xlgaobject());
  xllastarg();
  
  switch (which) {
  case 'U': IViewStdUnselectAllPoints(w); break;
  case 'E': IViewEraseSelection(w);               break;
  case 'M': IViewMaskSelection(w);                break;
  case 'u': IViewUnmaskAllPoints(w);              break;
  case 'S': IViewShowAllPoints(w);                break;
  case 'A': result = IViewAllPointsShowing(w);    break;
  case 'a': result = IViewAllPointsUnmasked(w);   break;
  case 's': result = IViewAnyPointsSelected(w);   break;
  }
  return((result) ? s_true : NIL);
}

LVAL iview_unselect_all_points(V) { return(multi_state_set('U')); }
LVAL iview_erase_selection(V)     { return(multi_state_set('E')); }
LVAL iview_mask_selection(V)      { return(multi_state_set('M')); }
LVAL iview_unmask_all_points(V)   { return(multi_state_set('u')); }
LVAL iview_show_all_points(V)     { return(multi_state_set('S')); }
LVAL iview_all_points_showing(V)  { return(multi_state_set('A')); }
LVAL iview_all_points_unmasked(V) { return(multi_state_set('a')); }
LVAL iview_any_points_selected(V) { return(multi_state_set('s')); }

/*************************************************************************/
/**                                                                     **/
/**                      IView Linking Functions                        **/
/**                                                                     **/
/*************************************************************************/

extern LVAL s_linked_plots, sk_links, sk_linked;

int IViewInternalIsLinked P1C(IVIEW_WINDOW, w)
{
  return (IViewIsLinked(w));
}

LVAL iview_linked(V)
{
  LVAL object, temp;
  IVIEW_WINDOW w;
  int set = FALSE, linked = FALSE, i, n;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) get_iview_address(object);
  if (moreargs()) {
    set = TRUE;
    linked = (xlgetarg() != NIL) ? TRUE : FALSE;
  }
  xllastarg();
  
  if (set) {
    if (linked) {
	  setvalue(s_linked_plots, cons(object, getvalue(s_linked_plots)));
      IViewCheckLinks(w);
	  n = IViewNumPoints(w);
      for (i = 0; i < n; i++) IViewMatchPointState(w, i);
      IViewAdjustScreens(w);
	}
    else {
      temp = xlcallsubr2(xremove, object, getvalue(s_linked_plots));
      setvalue(s_linked_plots, temp);
      IViewCheckLinks(w);
	}
  }
  else IViewCheckLinks(w);
  
  return(IViewIsLinked(w) ? s_true : NIL);
}

LVAL iview_links(V)
{
  LVAL object, links, next;
  
  object = xlgaobject();
  links = getvalue(s_linked_plots);
  for (next = links; consp(next); next = cdr(next))
    if (object == car(next)) return(links);
  return(NIL);
}

VOID IViewUnlinkWindow P1C(IVIEW_WINDOW, w)
{
  LVAL object = IViewWindowGetObject(w);
  
  if (objectp(object)) send_message_1L(object, sk_linked, NIL);
  IViewCheckLinks(w);
}

LVAL iview_unlink_all_windows(V)
{
  LVAL links = getvalue(s_linked_plots);
  
  xllastarg();
  for (; consp(links); links = cdr(links))
    send_message_1L(car(links), sk_linked, NIL);
  return(NIL);
}

VOID IViewMatchPointState P2C(IVIEW_WINDOW, w, unsigned, p)
{
  IVIEW_WINDOW lw;
  LVAL links, object;
  
  for (links = IViewGetLinks(w); consp(links); links = cdr(links)) {
    object = car(links);
    lw = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
    if (w != lw && IViewPointState(w, p) != IViewPointState(lw, p)) {
	  IViewSetPointScreenState(lw, p, IViewPointState(lw, p));
      IViewDataSetPointState(IViewDataPtr(lw), p, IViewPointState(w, p));
      send_message1(object, sk_adjust_screen_point, p);
	  IViewSetPointScreenState(lw, p, IViewPointState(lw, p));
	}
  }
}

VOID IViewAdjustScreens P1C(IVIEW_WINDOW, w)
{
  LVAL links, object;

  object = IViewWindowGetObject(w);
  if (objectp(object)) send_message(object, sk_adjust_screen);
  if (IViewIsLinked(w)) {
    for (links = IViewGetLinks(w); consp(links); links = cdr(links)) {
      object = car(links);
      if (objectp(object)) send_message(object, sk_adjust_screen);
    }
  }
}

VOID IViewCheckLinks P1C(IVIEW_WINDOW, w)
{
  LVAL links;

  links = send_message(IViewWindowGetObject(w), sk_links);
  IViewSetLinks(w, links);
}

/*************************************************************************/
/**                                                                     **/
/**                      Miscellaneous Functions                        **/
/**                                                                     **/
/*************************************************************************/

LOCAL VOID set_mode_cursor P1C(LVAL, object)
{
  send_message(object, k_set_mode_cursor);
}


syntax highlighted by Code2HTML, v. 0.9.1