/* AmiReq1.c - Low Level Requester 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 <libraries/dosextens.h>
#include <string.h>
#include <stdlib.h>
#include "autil2.h"
#include "xlisp.h"
#include "osdef.h"
#include "xlproto.h"
#include "xlsproto.h"
#include "iviewproto.h"
#include "Stproto.h"
#include "osproto.h"
#include "xlsvar.h"
#include "amivar.h"

/* layout definitions */
#define MAX_ENTRY_LENGTH 30
#define SCROLL_MIN 0
#define SCROLL_MAX 100
#define CHOICE_HEIGHT 20

void zero_ptr(char *,int),InstallItemList(DialogPtr,LVAL);

/***********************************************************************/
/***********************************************************************/
/**                                                                   **/
/**                       Internal Dialog Functions                   **/
/**                                                                   **/
/***********************************************************************/
/***********************************************************************/

/***********************************************************************/
/**                                                                   **/
/**                          Utility Functions                        **/
/**                                                                   **/
/***********************************************************************/

DialogItemData *GetDialogItemData(DialogPtr theDialog){
   return(GetDialogData(theDialog)->pid);}

DialogItemData *FindItemData(DialogPtr theDialog,LVAL item){
   DialogItemData *data,*itemData;
   int n;
   n=DialogItemCount(theDialog);
   data=GetDialogItemData(theDialog);
   for(itemData=0;n&&!itemData;n--,data++)if(data->object==item)itemData=data;
   return(itemData);}

/***********************************************************************/
/**                                                                   **/
/**                         DIALOG-PROTO Methods                      **/
/**                                                                   **/
/***********************************************************************/

static void MakeDialogItemData(LVAL dialog,struct Gadget **items){
   DialogPtr theDialog;
   DialogData *p;
   int numItems;
   numItems=count_hardware_items(slot_value(dialog,s_items));
   if(!(theDialog=(DialogPtr)GETDIALOGADDRESS(dialog)))xlfail("dialog not allocated");
   p=GetDialogData(theDialog);
   if(!(*items=p->gadl=calloc(numItems,sizeof(struct Gadget))))xlfail("gadgets not allocated");
   if(!(p->pid=calloc(numItems,sizeof(DialogItemData))))xlfail("gadget data not allocated");}

static void SetDialogItemData(LVAL dialog){
   DialogPtr theDialog;
   DialogData *dialogData;
   LVAL items=slot_value(dialog,s_items);
   if(!(theDialog=(DialogPtr)GETDIALOGADDRESS(dialog)))xlfail("dialog not allocated");
   SetDialogObject(theDialog,dialog);
   dialogData=GetDialogData(theDialog);
   dialogData->count=0;
   InstallItemList(theDialog,items);}

static FindItemType(LVAL item){
   if (consp(item))return(ITEM_LIST);
   else if(button_item_p(item))return(BUTTON_ITEM);
   else if(toggle_item_p(item))return(TOGGLE_ITEM);
   else if(text_item_p(item))return(TEXT_ITEM);
   else if(choice_item_p(item))return(CHOICE_ITEM);
   else if(scroll_item_p(item))return(SCROLL_ITEM);
   else if(list_item_p(item))return(LIST_ITEM);
   else xlerror("item of unknown type",item);}

static struct Gadget *install_gadget(DialogPtr theDialog,LVAL item,int *i){
   struct Gadget *gad;
   struct IntuiText *itext;
   DialogData *dialogData;
   Point loc,size;
   dialogData=GetDialogData(theDialog);
   *i=(dialogData->count)++;
   gad=&(dialogData->gadl[*i]);
   loc=ListToPoint(slot_value(item,s_location));
   size=ListToPoint(slot_value(item,s_size));
   if(*i<DialogItemCount(theDialog)-1)gad->NextGadget=gad+1;
   else gad->NextGadget=0;
   gad->TopEdge=loc.v+theDialog->BorderTop;
   gad->LeftEdge=loc.h+theDialog->BorderLeft;
   gad->Height=size.v;
   gad->Width=size.h;
   gad->Flags=GADGHCOMP;
   gad->Activation=RELVERIFY;
   gad->GadgetRender=gad->SelectRender=0;
   if(!(itext=gad->GadgetText=calloc(1,sizeof(struct IntuiText))))
   xlfail("gadget IntuiText allocation failed");
   itext->FrontPen=BLACK;
   itext->BackPen=WHITE;
   itext->DrawMode=JAM2;
   itext->LeftEdge=2;
   itext->TopEdge=(size.v-8)/2;
   gad->GadgetID=*i;
   return(gad);}
   
static void InstallTextItem(DialogPtr theDialog,LVAL item){
   struct Gadget *gad;
   struct StringInfo *spinfo;
   struct Border *bd;
   struct IntuiText *itext,*oitext;
   short *bc;
   static char buf[80],ubuf[80];
   DialogItemData *data;
   char *text;
   int itemIndex;
   gad=install_gadget(theDialog,item,&itemIndex);
   if(!stringp(slot_value(item,s_text)))xlerror("not a string",slot_value(item,s_text));
   text=(char *)getstring(slot_value(item,s_text));
   if(slot_value(item,s_editable)){
      gad->GadgetType=STRGADGET;
      gad->TopEdge+=2;
      if(!(bd=(struct Border *)gad->GadgetRender=calloc(1,sizeof(struct Border))))
      xlfail("text border allocation failed");
      bd->LeftEdge=bd->TopEdge=-2;
      bd->FrontPen=BLACK;
      bd->DrawMode=JAM1;
      bd->Count=5;
      if(!(bc=bd->XY=calloc(1,10*sizeof(short))))xlfail("text point allocationfailed");
      bc[2]=bc[4]=gad->Width;
      bc[5]=bc[7]=12;
      if(!(spinfo=(struct StringInfo *)gad->SpecialInfo=malloc(sizeof(struct StringInfo))))
      xlfail("text SpecialInfo allocation failed");
      strcpy(buf,text);
      spinfo->Buffer=buf;
      spinfo->UndoBuffer=ubuf;
      spinfo->BufferPos=strlen(text);
      spinfo->MaxChars=79;
      spinfo->DispPos=0;
      free(gad->GadgetText);
      gad->GadgetText=0;}
   else {
      gad->Flags=GADGHNONE|GADGDISABLED;
      oitext=gad->GadgetText;
      oitext->IText=text;
      while(*text){
         if(*text==10){
            *text=0;
            if(++text==0)break;
            if(!(itext=calloc(1,sizeof(struct IntuiText))))xlfail("gadget IntuiText allocation failed");
            itext->FrontPen=BLACK;
            itext->BackPen=WHITE;
            itext->DrawMode=JAM2;
            itext->LeftEdge=2;
            itext->TopEdge=oitext->TopEdge+9;
            itext->IText=text;
            oitext->NextText=itext;
            oitext=itext;}
         text++;}
      gad->Width=gad->Height=0;}
   data=GetDialogItemData(theDialog);
   data[itemIndex].type=TEXT_ITEM;
   data[itemIndex].itemNumber=itemIndex;
   data[itemIndex].itemPtr=gad;
   data[itemIndex].object=item;}

static short xy1[]={1,1,7,1,7,7,1,7,1,1},xy2[]={0,0,8,0,8,8,0,8,0,0};
static struct Border di={-1,-1,BLACK,WHITE,JAM1,5,xy2,0},
                     si={-1,-1,BLACK,WHITE,JAM1,5,xy1,&di};

static void InstallChoiceItem(DialogPtr theDialog,LVAL item){
   LVAL titles,temp;
   DialogItemData *data;
   struct Gadget *gad;
   int i,itemIndex,clusterLeader,clusterSize,initial;
   titles=slot_value(item,s_text);
   if(!listp(titles))xlerror("not a list",titles);
   clusterLeader=GetDialogData(theDialog)->count;
   clusterSize=llength(titles);
   data=GetDialogItemData(theDialog);
   for(i=0;consp(titles);i++,titles=cdr(titles)) {
      if(!stringp(car(titles)))xlerror("not a string",car(titles));
      gad=install_gadget(theDialog,item,&itemIndex);
      gad->GadgetText->IText=(char *)getstring(car(titles));
      gad->GadgetText->TopEdge=0;
      gad->GadgetText->LeftEdge=12;
      gad->Flags=GADGHIMAGE;
      gad->GadgetType=BOOLGADGET;
      gad->Activation=GADGIMMEDIATE;
      gad->Height=gad->Width=7;
      gad->TopEdge+=CHOICE_HEIGHT*i;
      gad->GadgetRender=(APTR)&di;
      gad->SelectRender=(APTR)&si;
      data[itemIndex].type=CHOICE_ITEM;
      data[itemIndex].itemNumber=itemIndex;
      data[itemIndex].itemPtr=gad;
      data[itemIndex].object=item;
      data[itemIndex].clusterLeader=clusterLeader;
      data[itemIndex].clusterSize=clusterSize;}
   temp=slot_value(item,s_value);
   initial=(fixp(temp))?getfixnum(temp):0;
   initial=((initial>=0&&initial<clusterSize)?initial:0)+clusterLeader;
   data[initial].itemPtr->Flags|=SELECTED;}

static void InstallScrollItem(DialogPtr theDialog,LVAL item){
   DialogItemData *data;
   struct Gadget *gad;
   struct PropInfo *pinfo;
   int low,high,value,itemIndex,pos;
   LVAL temp;
   gad=install_gadget(theDialog,item,&itemIndex);
   temp=slot_value(item,s_min_value);
   low=fixp(temp)?getfixnum(temp):SCROLL_MIN;
   temp=slot_value(item,s_max_value);
   high=fixp(temp)?getfixnum(temp):SCROLL_MAX;
   temp=slot_value(item,s_value);
   value=(fixp(temp))?getfixnum(temp):low;
   gad->Flags=GADGIMAGE;
   gad->GadgetType=PROPGADGET;
   if(!(gad->GadgetRender=malloc(sizeof(struct Image))))xlfail("slider image allocation failed");
   free(gad->GadgetText);
   gad->GadgetText=0;
   if(!(pinfo=(struct PropInfo *)gad->SpecialInfo=malloc(sizeof(struct PropInfo))))
   xlfail("slider SpecialInfo allocation failed");
   pinfo->Flags=AUTOKNOB;
   pos=(MAXPOT*(value-low))/(high-low);
   if(gad->Height>gad->Width){
      pinfo->Flags|=FREEVERT;
      pinfo->VertPot=pos;
      pinfo->VertBody=MAXBODY/(high-low);
      pinfo->HorizPot=MAXPOT;
      pinfo->HorizBody=MAXBODY;}
   else{
      pinfo->Flags|=FREEHORIZ;
      pinfo->HorizPot=pos;
      pinfo->HorizBody=MAXBODY/(high-low);
      pinfo->VertPot=MAXPOT;
      pinfo->VertBody=MAXBODY;}
   data=GetDialogItemData(theDialog);
   data[itemIndex].type=SCROLL_ITEM;
   data[itemIndex].itemNumber=itemIndex;
   data[itemIndex].itemPtr=gad;
   data[itemIndex].object=item;}

static void InstallControlItem(DialogPtr theDialog,LVAL item,int which){
   DialogItemData *data;
   struct Gadget *gad;
   int itemIndex;
   struct Border *border;
   short *bc;
   gad=install_gadget(theDialog,item,&itemIndex);
   if(!(border=(struct Border *)gad->GadgetRender=calloc(1,sizeof(struct Border))))
   xlfail("gadget border allocation failed");
   border->FrontPen=BLACK;
   border->BackPen=WHITE;
   border->DrawMode=JAM1;
   border->Count=9;
   if(!(bc=border->XY=malloc(18*sizeof(short))))xlfail("gadget border points allocation failed");
   bc[0]=bc[5]=bc[10]=bc[15]=bc[16]=2;
   bc[1]=bc[3]=bc[12]=bc[14]=bc[17]=-2;
   bc[2]=bc[8]=gad->Width-2;
   bc[4]=bc[6]=gad->Width+2;
   bc[7]=bc[13]=gad->Height-2;
   bc[9]=bc[11]=gad->Height+2;
   if(which==TOGGLE_ITEM){
      if(slot_value(item,s_value))gad->Flags|=SELECTED;
      gad->Activation|=TOGGLESELECT;}
   if(!stringp(slot_value(item,s_text)))xlerror("not a string",slot_value(item, s_text));
   gad->GadgetText->IText=(char *)getstring(slot_value(item, s_text));
   gad->GadgetText->LeftEdge=(gad->Width-8*strlen(gad->GadgetText->IText))/2;
   gad->GadgetType=BOOLGADGET;
   data=GetDialogItemData(theDialog);
   data[itemIndex].type=which;
   data[itemIndex].itemNumber=itemIndex;
   data[itemIndex].itemPtr=gad;
   data[itemIndex].object=item;}

static void InstallButtonItem(DialogPtr theDialog,LVAL item){
  InstallControlItem(theDialog,item,BUTTON_ITEM);}

static void InstallToggleItem(DialogPtr theDialog,LVAL item){
  InstallControlItem(theDialog,item,TOGGLE_ITEM);}

static void InstallItem(DialogPtr theDialog,LVAL item){
   int type;
   if(!dialog_item_p(item))xlerror("not a dialog item",item);
   type=FindItemType(item);
   switch (type) {
      case BUTTON_ITEM: InstallButtonItem(theDialog,item); break;
      case TOGGLE_ITEM: InstallToggleItem(theDialog,item); break;
      case CHOICE_ITEM: InstallChoiceItem(theDialog,item); break;
      case MESSAGE_ITEM:
      case TEXT_ITEM:   InstallTextItem(theDialog,item);   break;
      case SCROLL_ITEM: InstallScrollItem(theDialog,item); break;
/*      case REAL_SCROLL_ITEM:
      case LIST_ITEM:   InstallListItem(theDialog,item);   break;*/
      default: xlfail("unkown item type");}}

static void InstallItemList(DialogPtr theDialog,LVAL items){
   for(;consp(items);items=cdr(items)){
      if(consp(car(items)))InstallItemList(theDialog,car(items));
      else InstallItem(theDialog,car(items));}}

static void zero_ptr(char *p,int n){
  while(n-->0)*p++=0;}

int count_hardware_items(LVAL items){
   LVAL temp;
   int n;
   if(!consp(items))return(0);
   else for(n=0;consp(items);items=cdr(items))
      switch(FindItemType(car(items))) {
         case CHOICE_ITEM: {
            temp=slot_value(car(items),s_text);
            if(!consp(temp))xlerror("not a list",temp);
            n+=llength(temp);
            break;}
         case ITEM_LIST: n+=count_hardware_items(car(items)); break;
         default: n += 1;}
  return(n);}

/***********************************************************************/
/***********************************************************************/
/**                                                                   **/
/**                       Public Dialog Functions                     **/
/**                                                                   **/
/***********************************************************************/
/***********************************************************************/

void DialogSetDefaultButton(LVAL dialog,LVAL item){
   if(item&&!button_item_p(item))xlerror("not a button item",item);
   set_slot_value(dialog,s_default_button,item);}

void DialogAllocate(LVAL dialog){
   DialogPtr theDialog;
   struct Gadget *items;
   struct NewWindow nw;
   StGWWinInfo *wi;
   Point loc,size;
   char *title;
   unsigned long flags,iflags;
   if(!stringp(slot_value(dialog,s_title)))xlerror("not a string",slot_value(dialog,s_title));
   title=(char *)getstring(slot_value(dialog,s_title));
   loc=ListToPoint(slot_value(dialog,s_location));
   size=ListToPoint(slot_value(dialog,s_size));
   iflags=GADGETUP|GADGETDOWN;
   flags=WINDOWDRAG|SMART_REFRESH|ACTIVATE|WINDOWDEPTH|NOCAREREFRESH;
   if(slot_value(dialog,s_go_away)){
      flags|=WINDOWCLOSE;
      iflags|=CLOSEWINDOW;}
   nw.LeftEdge=loc.h;
   nw.TopEdge=loc.v;
   nw.Width=size.h+2*screen->WBorLeft;
   nw.Height=size.v+screen->WBorLeft+screen->BarHeight+1;
   nw.DetailPen=WHITE;
   nw.BlockPen=BLACK;
   nw.IDCMPFlags=iflags;
   nw.Flags=flags;
   nw.FirstGadget=0;
   nw.CheckMark=0;
   nw.Title=title;
   nw.Screen=screen;
   nw.BitMap=0;
   nw.MinWidth=0;
   nw.MinHeight=0;
   nw.MaxWidth=0;
   nw.MaxHeight=0;
   nw.Type=screentype;
   if(!(theDialog=OpenWindow(&nw)))xlfatal("window allocation failed");
   if(!(wi=(StGWWinInfo *)theDialog->UserData=malloc(sizeof(StGWWinInfo))))
   xlfail("dialog wininfo allocation failed");
   wi->window=theDialog;
   wi->Object=dialog;
   set_dialog_address(theDialog,dialog);
   MakeDialogItemData(dialog,&items);
   SetDialogItemData(dialog);
   AddGList(theDialog,items,0,-1,0);
   RefreshGList(items,theDialog,0,-1);
   DialogSetDefaultButton(dialog,slot_value(dialog,s_default_button));}

void DialogRemove(LVAL dialog){
   struct Window *w;
   DialogPtr theDialog;
   int i,which;
   struct Gadget *gad,*gadl;
   struct IntuiText *itext,*oitext;
   DialogItemData *data;
   StGWWinInfo *gwinfo;
   if(check_dialog_address(dialog)){
      theDialog=GETDIALOGADDRESS(dialog);
      w=screen->FirstWindow;
      while(w&&w!=theDialog)w=w->NextWindow;
      if(!w)return;
      CloseWindow(theDialog);
      data=GetDialogItemData(theDialog);
      gadl=GetDialogData(theDialog)->gadl;
      gwinfo=(StGWWinInfo *)theDialog->UserData;
      for(i=0;i<DialogItemCount(theDialog);i++){
         which=data[i].type;
         gad=data[i].itemPtr;
         free(gad->SpecialInfo);
         if(which==BUTTON_ITEM||which==TOGGLE_ITEM||(which==TEXT_ITEM&&gad->GadgetRender))
         free(((struct Border *)gad->GadgetRender)->XY);
         if(which!=CHOICE_ITEM)free(gad->GadgetRender);
         itext=gad->GadgetText;
         while(itext){
            oitext=itext;
            itext=oitext->NextText;
            free(oitext);}}
      free(gadl);
      free(GetDialogItemData(theDialog));
      free(gwinfo);}
   if(objectp(dialog))standard_hardware_clobber(dialog);}

static LVAL get_file(char *prompt,char def_name[],char def_dir[]){
   LVAL result;
   struct Process *OurTask;
   APTR prwindowptr;
   char fname[100];
   OurTask=(struct Process *)FindTask(0);
   prwindowptr=(APTR)OurTask->pr_WindowPtr;
   OurTask->pr_WindowPtr=(APTR)window;
   if(!get_fname(window,screen,prompt,def_name,def_dir,""))return(0);
   OurTask->pr_WindowPtr=(APTR)prwindowptr;
   if(!def_name[0])return(0);
   strcpy(fname,def_dir);
   if(fname[strlen(fname)-1]!=':'&&fname[strlen(fname)-1]!='/'&&
   fname[0]!=0)strcat(fname,"/");
   strcat(fname,def_name);
   result=newstring(strlen(fname)+1);
   strcpy(getstring(result),fname);
   return(result);}

LVAL xsopenfiledialog(void){
   static unsigned char def_name[50],def_dir[50];
   return(get_file("Choose a file:",def_name,def_dir));}

LVAL xssetfiledialog(void){
   char def_name[50],*prompt;
   static unsigned char def_dir[50];
   prompt=(char *)getstring(xlgastring());
   def_name[0]=0;
   return(get_file(prompt,def_name,def_dir));}