/* AmiIviewWindow2.c - Low Level Window Objects for Amiga              */
/* Copyright (c) 1990 by J.K. Lindsey                                  */
/* Additions to 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 <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <graphics/gfxmacros.h>
#include <stdlib.h>
#include "xlisp.h"
#include "osdef.h"
#include "xlproto.h"
#include "xlsproto.h"
#include "iviewproto.h"
#include "Stproto.h"
#include "osproto.h"
#include "amivar.h"
#include "xlsvar.h"

static void remove_scroll(struct Window *w,struct Gadget *gad){
   if(gad){
      RemoveGadget(w,gad);
      free(gad->GadgetRender);
      free(gad->SpecialInfo);
      free(gad);
      RefreshWindowFrame(w);}}

/**************************************************************************/
/**                                                                      **/
/**                    Action Installation Functions                     **/
/**                                                                      **/
/**************************************************************************/

void StGWSetFreeMem(StGWWinInfo *gwinfo,void(*FreeMem)(IVIEW_WINDOW)){}

int StGWIdleOn(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->idleOn:0);}

void StGWSetIdleOn(StGWWinInfo *gwinfo,int on){
   if(gwinfo)gwinfo->idleOn=on;}

/**************************************************************************/
/**                                                                      **/
/**                      Window Management Functions                     **/
/**                                                                      **/
/**************************************************************************/

void StGWShowWindow(StGWWinInfo *gwinfo){
   struct Window *w;
   if(gwinfo&&(w=gwinfo->window)){
      if(!gwinfo->initialized)send_message_1L(gwinfo->Object,sk_update,s_true);
      WindowToFront(w);}}

void StGWRemove(StGWWinInfo *gwinfo){
   struct Window *w,*ww;
   if(gwinfo&&(ww=gwinfo->window)){
      w=screen->FirstWindow;
      while(w&&w!=ww)w=w->NextWindow;
      if(!w)return;
      if(gwinfo->hasVscroll)remove_scroll(w,gwinfo->vscroll);
      if(gwinfo->hasHscroll)remove_scroll(w,gwinfo->hscroll);
      ClearMenuStrip(w);
      CloseWindow(w);
      gwinfo->window=0;}}

void StGWWhileButtonDown(StGWWinInfo *gwinfo,void(*action)(struct Window *,int,int),int motionOnly){
   struct Window *w;
   struct IntuiMessage *message;
   unsigned short code;
   unsigned long class;
   int x,y;
   if(!gwinfo||!(w=gwinfo->window))return;
   for(;;){
      if(message=(struct IntuiMessage *)GetMsg(w->UserPort)){
         class=message->Class;
         code=message->Code;
         ReplyMsg((struct Message *)message);
         if(class==MOUSEBUTTONS&&code==SELECTUP)break;}
      x=w->MouseX;
      y=w->MouseY;
      if(x<=0||x>=w->Width||y<=0||y>=w->Height)break;
      ami_do_cursor(gwinfo);
      x-=w->BorderLeft;
      y-=w->BorderTop;
      if(gwinfo->mouse_x!=x||gwinfo->mouse_y!=y||!motionOnly){
         gwinfo->mouse_x=x;
         gwinfo->mouse_y=x;
         scroll_clip(gwinfo,&x,&y);
         if(action)(*action)(w,x,y);}}}

void StWSetLocation(IVIEW_WINDOW w,int left,int top,int frame){
   if(w){
      if(!frame){
         left-=w->BorderLeft;
         top-=w->BorderTop;}
      if(left<0)left=0;;
      if(top<0)top=0;
      if(left+w->Width>screenw)left=screenw-w->Width;
      if(top+w->Height>screenh)top=screenh-w->Height;
      MoveWindow(w,(short)(left-w->LeftEdge),(short)(top-w->TopEdge));}}

void StWGetLocation(struct Window *w,int *left,int *top,int frame){
   if(w){
      if(frame){
         if(left)*left=w->LeftEdge;
         if(top)*top=w->TopEdge;}
      else {
         if(left)*left=w->LeftEdge+w->BorderLeft;
         if(top)*top=w->TopEdge+w->BorderTop;}}}

void StGWSetSize(StGWWinInfo *gwinfo,int width,int height,int frame){
   struct Window *w;
   if(gwinfo&&(w=gwinfo->window))StWSetSize(w,width,height,frame);}

void StWSetSize(struct Window *w,int width,int height,int frame){
   if(w){
      if(!frame){
         width+=w->BorderLeft+w->BorderRight;
         height+=w->BorderTop+w->BorderBottom;}
      SizeWindow(w,(short)(width-w->Width),(short)(height-w->Height));}}

void StWGetSize(struct Window *w,int *width,int *height,int frame){
   if(w){
      if(frame){
         if(width)*width=w->Width;
         if(height)*height=w->Height;}
      else {
         if(width)*width=w->Width-w->BorderLeft-w->BorderRight;
         if(height)*height=w->Height-w->BorderTop-w->BorderBottom;}}}

/**************************************************************************/
/**                                                                      **/
/**             Window State Access and Mutation Functions               **/
/**                                                                      **/
/**************************************************************************/

void SetHardwareState(StGWWinInfo *gwinfo){
   struct Window *w;
   if(!gwinfo||(!(w=gwinfo->window)))return;
   rp=(dbflag)?&dbrp:w->RPort;
   SetAPen(&dbrp,gwinfo->drawColor);
   SetAPen(w->RPort,gwinfo->drawColor);
   SetBPen(&dbrp,gwinfo->backColor);
   SetBPen(w->RPort,gwinfo->backColor);
   if(gwinfo->drawMode==JAM1)gwinfo->symbolMode=JAM2;
   else gwinfo->drawMode=gwinfo->symbolMode=COMPLEMENT;
   SetDrMd(&dbrp,gwinfo->drawMode);
   SetDrMd(w->RPort,gwinfo->drawMode);

   /* set line type */
   if(gwinfo->lineType&&gwinfo->drawColor!=gwinfo->backColor){
      SetDrPt(rp,0xAAAA);} /* requires brackets for macro ! */
   else {
      SetDrPt(rp,0xFFFF);}

   /* set pen size - not implemented in present Amiga ROM */
   rp->PenWidth=rp->PenHeight=gwinfo->lineWidth;}

StGWCanvasWidth(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->canvasWidth:0);}

StGWCanvasHeight(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->canvasHeight:0);}

StGWLineType(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->lineType:0);}

StGWDrawMode(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->drawMode:0);}

ColorCode StGWDrawColor(StGWWinInfo *gwinfo){
  return((ColorCode)(gwinfo?gwinfo->drawColor:0));}

ColorCode StGWBackColor(StGWWinInfo *gwinfo){
  return((ColorCode)(gwinfo?gwinfo->backColor:0));}

StGWUseColor(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->use_color:0);}

void StGWGetLineWidth(StGWWinInfo *gwinfo,int *width){
  if(!gwinfo)return;
  if(width)*width=gwinfo->lineWidth;}

static void set_state(StGWWinInfo *gwinfo,int which,int value){
   int changed;
   if(!gwinfo)return;
   switch (which) {
      case 'L': if((changed=(value!=gwinfo->lineType)))gwinfo->lineType=value;  break;
      case 'M': if((changed=(value!=gwinfo->drawMode)))gwinfo->drawMode=value;  break;
      case 'D': if((changed=(value!=gwinfo->drawColor)))gwinfo->drawColor=value; break;
      case 'B': if((changed=(value!=gwinfo->backColor)))gwinfo->backColor=value; break;
      case 'C': if((changed=(value!=gwinfo->use_color)))gwinfo->use_color =value;break;}
   if(changed)SetHardwareState(gwinfo);}

void StGWSetLineType(StGWWinInfo *gwinfo,int type){
  set_state(gwinfo,'L',type);}

void StGWSetDrawMode(StGWWinInfo *gwinfo,int mode){
  set_state(gwinfo,'M',mode);}

void StGWSetDrawColor(StGWWinInfo *gwinfo,ColorCode color){
  set_state(gwinfo,'D',color);}

void StGWSetBackColor(StGWWinInfo *gwinfo,ColorCode color){
  set_state(gwinfo,'B',color);}

void StGWSetUseColor(StGWWinInfo *gwinfo,ColorCode use){
  set_state(gwinfo,'C',use);}

void StGWSetLineWidth(StGWWinInfo *gwinfo,int width){
   int changed;
   if(!gwinfo)return;
   changed=(width!=gwinfo->lineWidth);
   if(changed){
      gwinfo->lineWidth=width;
      SetHardwareState(gwinfo);}}

void StGWReverseColors(StGWWinInfo *gwinfo){
   ColorCode backColor,drawColor;
   backColor=StGWBackColor(gwinfo);
   drawColor=StGWDrawColor(gwinfo);
   if(backColor!=drawColor){
      StGWSetBackColor(gwinfo,drawColor);
      StGWSetDrawColor(gwinfo,backColor);
      StGWObRedraw(gwinfo->Object);}}

void StGWGetViewRect(StGWWinInfo *gwinfo,int *left,int *top,int *width,int *height){
   struct Window *w;
   if(gwinfo&&(w=gwinfo->window)){
      if(left)*left=0;
      if(top)*top=0;
      StWGetSize(w,width,height,0);
      if(width&&gwinfo->hasVscroll)*width-=8;}}

/**************************************************************************/
/**                                                                      **/
/**                       Window Scrolling Functions                     **/
/**                                                                      **/
/**************************************************************************/

static void set_has_scroll(StGWWinInfo *gwinfo,char which,int has,int size){
   struct Window *w;
   int view_size,old_has;
   short s;
   struct Gadget *gad;
   struct PropInfo *pinfo;
   if(!gwinfo||!(w=gwinfo->window))return;
   if(has&&size<=0)StPerror("size must be positive");
   old_has=(which=='H')?gwinfo->hasHscroll:gwinfo->hasVscroll;
   if(which=='H'){
      gwinfo->hasHscroll=has;
      if(has){
         gwinfo->canvasWidth=size;
         if(old_has){
            StGWGetViewRect(gwinfo,0,0,&view_size,0);
            gwinfo->view_h=0;
            s=(size-view_size>0)?(MAXBODY*view_size)/size:MAXBODY;
            NewModifyProp(gwinfo->hscroll,w,0,AUTOKNOB|FREEHORIZ,0,MAXPOT,s,MAXBODY,1);
            return;}}}
   else {
      gwinfo->hasVscroll=has;
      if(has){
         gwinfo->canvasHeight=size;
         if(old_has){
            StGWGetViewRect(gwinfo,0,0,0,&view_size);
            gwinfo->view_v=0;
            s=(size-view_size>0)?(MAXBODY*view_size)/size:MAXBODY;
            NewModifyProp(gwinfo->vscroll,w,0,AUTOKNOB|FREEVERT,MAXPOT,0,MAXBODY,s,1);
            return;}}}
   if(has){
      if(!(gad=calloc(1,sizeof(struct Gadget))))xlfail("scroll bar not allocated");
      gad->Flags=GADGIMAGE;
      gad->Activation=RELVERIFY;
      gad->GadgetType=PROPGADGET;
      if(!(gad->GadgetRender=malloc(sizeof(struct Image))))xlfail("slider image allocation failed");
      if(!(pinfo=(struct PropInfo *)gad->SpecialInfo=malloc(sizeof(struct PropInfo))))
      xlfail("slider SpecialInfo allocation failed");
      pinfo->Flags=AUTOKNOB;
      if(which=='H'){
         gad->LeftEdge=1;
         gad->TopEdge=-8;
         gad->Width=-14;
         gad->Height=8;
         gad->Flags|=GRELBOTTOM|GRELWIDTH;
         gad->Activation|=BOTTOMBORDER;
         gad->GadgetID=0;
         gwinfo->hscroll=gad;
         StGWGetViewRect(gwinfo,0,0,&view_size,0);
         pinfo->Flags|=FREEHORIZ;
         gwinfo->view_h=pinfo->HorizPot=0;
         pinfo->HorizBody=(size-view_size>0)?(MAXBODY*view_size)/size:MAXBODY;
         pinfo->VertPot=MAXPOT;
         pinfo->VertBody=MAXBODY;}
      else {
         gad->LeftEdge=-11;
         gad->TopEdge=10;
         gad->Width=11;
         gad->Height=-19;
         gad->GadgetID=1;
         gad->Flags|=GRELRIGHT|GRELHEIGHT;
         gad->Activation|=RIGHTBORDER;
         gwinfo->vscroll=gad;
         StGWGetViewRect(gwinfo,0,0,0,&view_size);
         pinfo->Flags|=FREEVERT;
         gwinfo->view_v=pinfo->VertPot=0;
         pinfo->VertBody=(size-view_size>0)?(MAXBODY*view_size)/size:MAXBODY;
         pinfo->HorizPot=MAXPOT;
         pinfo->HorizBody=MAXBODY;}
      AddGList(w,gad,-1,1,0);
      RefreshGList(gad,w,0,1);}
   else {
      if(which=='H'){
         StGWGetViewRect(gwinfo,0,0,&gwinfo->canvasWidth,0);
         if(old_has){
            remove_scroll(w,gwinfo->hscroll);
            gwinfo->hscroll=0;}
         gwinfo->view_h=0;}
      else {
         StGWGetViewRect(gwinfo,0,0,0,&gwinfo->canvasHeight);
         if(old_has){
            remove_scroll(w,gwinfo->vscroll);
            gwinfo->vscroll=0;}
         if(gwinfo->hasHscroll){
            StGWGetViewRect(gwinfo,0,0,&view_size,0);
            NewModifyProp(gwinfo->hscroll,w,0,GADGIMAGE|GRELBOTTOM|GRELWIDTH,0,MAXPOT,
            (gwinfo->canvasWidth-view_size)?(MAXBODY*view_size)/gwinfo->canvasWidth:MAXBODY,
            MAXBODY,1);}
         gwinfo->view_v=0;}}
   StGWObResize(gwinfo->Object);}

void StGWSetHasHscroll(StGWWinInfo *gwinfo,int has,int size){
   set_has_scroll(gwinfo,'H',has,size);}

void StGWSetHasVscroll(StGWWinInfo *gwinfo,int has,int size){
   set_has_scroll(gwinfo,'V',has,size);}

int StGWHasHscroll(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->hasHscroll:0);}

int StGWHasVscroll(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->hasVscroll:0);}

void StGWSetScroll(StGWWinInfo *gwinfo,int h,int v,int move){
   if(!gwinfo||!gwinfo->window)return;
   if(gwinfo->hasHscroll)gwinfo->view_h=h;
   if(gwinfo->hasVscroll)gwinfo->view_v=v;}

void StGWGetScroll(StGWWinInfo *gwinfo,int *h,int *v){
   if(gwinfo){
      if(h)*h=gwinfo->view_h;
      if(v)*v=gwinfo->view_v;}}

void StGWSetHscrollIncs(StGWWinInfo *gwinfo,int inc,int pageInc){
   if(!gwinfo)return;
   gwinfo->h_scroll_inc[0]=inc;
   gwinfo->h_scroll_inc[1]=pageInc;}

void StGWGetHscrollIncs(StGWWinInfo *gwinfo,int *inc,int *pageInc){
   if(!gwinfo)return;
   if(inc)*inc=gwinfo->h_scroll_inc[0];
   if(pageInc)*pageInc=gwinfo->h_scroll_inc[1];}

void StGWSetVscrollIncs(StGWWinInfo *gwinfo,int inc,int pageInc){
   if(!gwinfo)return;
   gwinfo->v_scroll_inc[0]=inc;
   gwinfo->v_scroll_inc[1]=pageInc;}

void StGWGetVscrollIncs(StGWWinInfo *gwinfo,int *inc,int *pageInc){
   if(!gwinfo)return;
   if(inc)*inc=gwinfo->v_scroll_inc[0];
   if(pageInc)*pageInc=gwinfo->v_scroll_inc[1];}

/**************************************************************************/
/**                                                                      **/
/**                    Graph Window RefCon Functions                     **/
/**                                                                      **/
/**************************************************************************/

void StGWSetRefCon(StGWWinInfo *gwinfo,IView x){
   if(gwinfo)gwinfo->RefCon=x;}

IView StGWGetRefCon(StGWWinInfo *gwinfo){
   return(gwinfo?gwinfo->RefCon:0);}
 
void StGWSetObject(StGWWinInfo *gwinfo,LVAL x){
   if(gwinfo)gwinfo->Object=x;}

LVAL IViewWindowGetObject(struct Window *w){
   StGWWinInfo *gwinfo=(StGWWinInfo *)w->UserData;
   return(gwinfo?gwinfo->Object:0);}

void StGWInitialDraw(StGWWinInfo *gwinfo){
   if(gwinfo)graph_update_action(gwinfo,1);}

/**************************************************************************/
/**                                                                      **/
/**                    Graph Window Color Functions                      **/
/**                                                                      **/
/**************************************************************************/

#define NumBasicColors 8
#define NumRGBColors   8

static int NumColors;

typedef struct {
   long old;
   LVAL refcon;
}  ctab_entry;

static ctab_entry *ctable;

void init_ami_colors(void){
   int i;
   NumColors=NumBasicColors+NumRGBColors;
   ctable=(ctab_entry *)StCalloc(NumColors,sizeof(ctab_entry));
   for(i=0;i<NumColors;i++)ctable[i].old=i;}

ColorCode StGWMakeColor(double red,double green,double blue,LVAL refcon){
   ColorCode index;
   for(index=NumBasicColors;index<NumColors&&StGWGetColRefCon(index);index++);
   if(index>=NumColors)return(-1);
   else {
      StGWSetColRefCon(index,refcon);
      SetRGB4(&screen->ViewPort,index,(char)(15*red),(char)(15*green),(char)(15*blue));
      return(index);}}

void StGWFreeColor(ColorCode index){
   if(index<NumColors)StGWSetColRefCon(index,0);}

void StGWSetColRefCon(ColorCode index,LVAL rc){
   if(index<NumColors)ctable[index].refcon=rc;}

LVAL StGWGetColRefCon(ColorCode index){
   return((index<NumColors)?ctable[index].refcon:0);}
