/* AmiReq2.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 <stdlib.h>
#include <string.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 "xlvar.h"
#include "xlsvar.h"
#include "amivar.h"

static int jump_to_top;

/* layout definitions */
#define FONT_HEIGHT 16
#define FONT_WIDTH_GUESS 9
#define FONT_POINTS 12

#define BUTTON_HEIGHT 20
#define BUTTON_WIDTH 90
#define BUTTON_PAD 15

#define TOGGLE_PAD 20
#define TOGGLE_HEIGHT FONT_HEIGHT - 2

#define EDIT_TEXT_PAD 20
#define EDIT_TEXT_HEIGHT 20
#define STATIC_TEXT_PAD 20
#define STATIC_TEXT_HEIGHT FONT_HEIGHT

#define CHOICE_HEIGHT 20
#define CHOICE_PAD 20

#define SCROLL_WIDTH 180
#define SCROLL_HEIGHT 16
/*
#define LIST_ITEM_HEIGHT 16
#define MAX_LIST_ROWS 12
*/
int max_line_size(char *);
LVAL DialogTextItemText(LVAL,int,char *);

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

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

static Point StringSize(char *s){
   Point pt;
   pt.v=window->RPort->TxHeight;
   pt.h=window->RPort->TxWidth*strlen(s);
   return(pt);}

/***********************************************************************/
/**                                                                   **/
/**                         Callback Functions                        **/
/**                                                                   **/
/***********************************************************************/

static void clear_dialog(DialogPtr theDialog){
   struct RastPort *rp;
   rp=theDialog->RPort;
   SetAPen(rp,WHITE);
   SetOPen(rp,BLACK);
   RectFill(rp,2,10,theDialog->Width-3,theDialog->Height-3);
   BNDRYOFF(rp);}

static void SetClusterValue(DialogPtr theDialog,int item){
   int i,n,ns,leader;
   DialogItemData *data;
   struct Gadget *lgad;
   data=GetDialogItemData(theDialog);
   leader=data[item].clusterLeader;
   ns=data[item].clusterSize;
   n=ns+leader;
   lgad=data[leader].itemPtr;
   RemoveGList(theDialog,lgad,ns);
   for(i=leader;i<n;i++){
      if(i!=item)data[i].itemPtr->Flags=GADGHIMAGE;
      else data[i].itemPtr->Flags=GADGHIMAGE|SELECTED;}
   AddGList(theDialog,lgad,leader,ns,0);
   clear_dialog(theDialog);
   RefreshGList(data->itemPtr,theDialog,0,-1);}

/* Action proc for scroll bars. */

void doDialog(int theItem,DialogPtr theDialog){
   LVAL item, args;
   DialogItemData *data;
   int type;
   struct Gadget *itemPtr;
   data=GetDialogItemData(theDialog);
   item=data[theItem].object;
   type=data[theItem].type;
   itemPtr=data[theItem].itemPtr;
   if(type==TOGGLE_ITEM)set_slot_value(item,s_value,itemPtr->Flags&SELECTED?s_true:NIL);
   else if(type==CHOICE_ITEM)SetClusterValue(theDialog,theItem);
   if(objectp(item)&&theItem){
      xlsave1(args);
      args=list2(item,sk_do_action);
      xsapplysubr(xmsend,args);
      xlpop();}}

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

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

LVAL DialogGetModalItem(LVAL dialog){
   DialogPtr theDialog;
   LVAL item;
   short itemNumber;
   int type;
   DialogItemData *data;
   struct IntuiMessage *message;
   struct Gadget *address;
   int class;
   if(!(theDialog=GETDIALOGADDRESS(dialog)))xlfail("the dialog is not visible");
   for(;;){
      Wait(1<<theDialog->UserPort->mp_SigBit);
      if(message=(struct IntuiMessage *)GetMsg(theDialog->UserPort)){
         class=message->Class;
         address=(struct Gadget *)message->IAddress;
         ReplyMsg((struct Message *)message);
         if(class==GADGETUP||class==GADGETDOWN)break;}}
   itemNumber=address->GadgetID;
   if(itemNumber<0||itemNumber>DialogItemCount(theDialog))xlfail("invalid item number");
   data=GetDialogItemData(theDialog);
   type=data[itemNumber].type;
   item=data[itemNumber].object;
   if(type==TOGGLE_ITEM)set_slot_value(item,s_value,address->Flags&SELECTED?s_true:NIL);
   else if(type==CHOICE_ITEM)SetClusterValue(theDialog,itemNumber);
   return(item);}

/***********************************************************************/
/**                                                                   **/
/**                         Button Item Functions                     **/
/**                                                                   **/
/***********************************************************************/

void DialogButtonGetDefaultSize(LVAL item,int *width,int *height){
   LVAL text=slot_value(item, s_text);
   Point sz;
   if(!stringp(text))xlerror("not a string",text);
   sz=StringSize(getstring(text));
   if(width)*width=max(sz.h+BUTTON_PAD,BUTTON_WIDTH);
   if(height)*height=BUTTON_HEIGHT;}

/***********************************************************************/
/**                                                                   **/
/**                         Toggle Item Functions                     **/
/**                                                                   **/
/***********************************************************************/

void DialogToggleGetDefaultSize(LVAL item,int *width,int *height){
   Point sz;
   sz=StringSize(getstring(slot_value(item,s_text)));
   if(width)*width=sz.h+TOGGLE_PAD;
   if(height)*height=TOGGLE_HEIGHT;}

LVAL DialogToggleItemValue(LVAL item,int set,LVAL value){
   LVAL dialog;
   struct Gadget *gad;
   DialogPtr theDialog;
   dialog=slot_value(item,s_dialog);
   if(theDialog=GETDIALOGADDRESS(dialog)){
      gad=FindItemData(theDialog,item)->itemPtr;
      if(set){
         set_slot_value(item,s_value,value?s_true:NIL);
         if(value)gad->Flags|=SELECTED;
         else gad->Flags&=~SELECTED;
         RefreshGList(gad,theDialog,0,1);}}
   return(slot_value(item,s_value));}

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

void DialogTextGetDefaultSize(LVAL item,int *width,int *height){
   Point sz;
   LVAL text=slot_value(item,s_text);
   LVAL text_length=slot_value(item,xlenter("TEXT-LENGTH"));
   int w=0;
   char *s;
   if(stringp(text)){
      w=max_line_size(getstring(text));
      s=(char *)getstring(text);
      for(sz.v=STATIC_TEXT_HEIGHT;*s!='\0';s++)if(*s=='\n'||*s=='\r')sz.v+=STATIC_TEXT_HEIGHT;}
   if(fixp(text_length)){
      sz=StringSize("M");
      w=max((int)(getfixnum(text_length)*sz.h),w);}
   if(slot_value(item,s_editable)){
      if(width)*width=w+EDIT_TEXT_PAD;
      if(height)*height=EDIT_TEXT_HEIGHT;}
   else {
      if(width)*width=w+STATIC_TEXT_PAD;
      if(height)*height=sz.v;}}           /* STATIC_TEXT_HEIGHT */

LVAL DialogTextItemText(LVAL item,int set,char *text){
   DialogItemData *itemData;
   DialogPtr theDialog;
   struct Gadget *gad;
   theDialog=(DialogPtr)GETDIALOGADDRESS(slot_value(item,s_dialog));
   if(theDialog){
      itemData=FindItemData(theDialog,item);
      gad=itemData->itemPtr;
      if(set){
         if(slot_value(item,s_editable)){
            strcpy(((struct StringInfo *)gad->SpecialInfo)->Buffer,text);}
         else {
            clear_dialog(theDialog);
            gad->GadgetText->IText=text;}
         RefreshGList(GetDialogData(theDialog)->gadl,theDialog,0,-1);}
      else set_slot_value(item,s_text,make_string(((struct StringInfo *)itemData->itemPtr->SpecialInfo)->Buffer));}
   return(slot_value(item,s_text));}

/***********************************************************************/
/**                                                                   **/
/**                         Choice Item Functions                     **/
/**                                                                   **/
/***********************************************************************/

void DialogChoiceGetDefaultSize(LVAL item,int *width,int *height){
   Point sz,pt;
   LVAL text=slot_value(item,s_text);
   for(sz.h=0,sz.v=0;consp(text);text=cdr(text)){
      pt=StringSize(getstring(car(text)));
      sz.h=max(sz.h,pt.h);
      sz.v+=CHOICE_HEIGHT;}
   if(width)*width=sz.h+CHOICE_PAD;
   if(height)*height=sz.v;}

LVAL DialogChoiceItemValue(LVAL item,int set,int value){
   LVAL result;
   DialogItemData *itemData,*data;
   DialogPtr theDialog;
   int leader,i,n;
   if(theDialog=(DialogPtr)GETDIALOGADDRESS(slot_value(item,s_dialog))){
      data=GetDialogItemData(theDialog);
      itemData=FindItemData(theDialog,item);
      leader=itemData->clusterLeader;
      n=itemData->clusterSize;
      if(set){
         if(value<0||value>=n)xlfail("value out of range");
         for(i=0;i<n;i++)data[leader+i].itemPtr->Flags&=~SELECTED;
         data[leader+value].itemPtr->Flags|=SELECTED;
         RefreshGList(data[leader].itemPtr,theDialog,0,n);
         result=cvfixnum((FIXTYPE)value);}
      else {
         result=NIL;
         for(i=0;i<n&&!result;i++) {  
            result=(data[leader+i].itemPtr->Flags&SELECTED)?cvfixnum((FIXTYPE)i):NIL;}}
      set_slot_value(item, s_value, result);}
   return(slot_value(item, s_value));}

/***********************************************************************/
/**                                                                   **/
/**                         Scroll Item Functions                     **/
/**                                                                   **/
/***********************************************************************/

void DialogScrollGetDefaultSize(LVAL item,int *width,int *height){
   if(width)*width=SCROLL_WIDTH;
   if(height)*height=SCROLL_HEIGHT;}

static LVAL scroll_item_value(LVAL item,int set,int value,int which){
   LVAL result;
   DialogItemData *itemData;
   DialogPtr theDialog;
   struct Gadget *gad;
   struct PropInfo *pi;
   int max,min;
   if(theDialog=GETDIALOGADDRESS(slot_value(item,s_dialog))){
      itemData=FindItemData(theDialog,item);
      gad=itemData->itemPtr;
      max=getfixnum(slot_value(item,s_max_value));
      min=getfixnum(slot_value(item,s_min_value));
      pi=(struct PropInfo *)gad->SpecialInfo;
      switch (which) {
         case 'H': {
            if(!set)value=max;
            result=cvfixnum((FIXTYPE)value);
            set_slot_value(item,s_max_value,result);
            break;}
         case 'L': {
            if(!set)value=min;
            result=cvfixnum((FIXTYPE)value);
            set_slot_value(item,s_min_value,result);
            break;}
         case 'V': {
            if(set){
               if(gad->Height>gad->Width){
                  NewModifyProp(gad,theDialog,0,AUTOKNOB|FREEVERT,MAXPOT,
                  ((value-min)*MAXPOT)/(max-min),MAXBODY,MAXBODY/(max-min),1);}
               else {
                  NewModifyProp(gad,theDialog,0,AUTOKNOB|FREEHORIZ,
                  ((value-min)*MAXPOT)/(max-min),MAXPOT,MAXBODY/(max-min),
                  MAXBODY,1);}}
            else {
               value=(gad->Height>gad->Width)?pi->VertPot:pi->HorizPot;
               value=(value*(max-min))/MAXPOT+min;}
            result=cvfixnum((FIXTYPE)value);
            set_slot_value(item,s_value,result);
            break;}}}
   return(result);}

LVAL DialogScrollItemValue(LVAL item,int set,int value){
  return(scroll_item_value(item,set,value,'V'));}

LVAL DialogScrollItemMax(LVAL item,int set,int value){
  return(scroll_item_value(item,set,value,'H'));}

LVAL DialogScrollItemMin(LVAL item,int set,int value){
  return(scroll_item_value(item,set,value,'L'));}

/***********************************************************************/
/**                                                                   **/
/**                          List Item Functions                      **/
/**                                                                   **/
/***********************************************************************/

void DialogListGetDefaultSize(LVAL item,int *width,int *height){
   /*LVAL columns=slot_value(item,s_columns);
   LVAL data=slot_value(item,s_list_data);
   Point sz;
   if(listp(data))sz.v=LIST_ITEM_HEIGHT*llength(data);
   else if(simplevectorp(data))sz.v=LIST_ITEM_HEIGHT*getsize(data);
   else if(matrixp(data))sz.v=16*getfixnum(getelement(displacedarraydim(data),0));
   sz.v=min(sz.v,MAX_LIST_ROWS*LIST_ITEM_HEIGHT);
   if(matrixp(data)&&getfixnum(getelement(displacedarraydim(data),1))>getfixnum(columns))sz.v+=15;
   sz.h=150*getfixnum(columns);
   if (width)*width=sz.h;
   if(height)*height=sz.v;}*/
   xlfail("not supported 48"); }

void DialogListItemSetText(LVAL item,LVAL index,char *text){
   /*LVAL listData;
   DialogItemData *itemData;
   DialogPtr theDialog;
   Point cell;
   int temp;
   listData=slot_value(item,s_list_data);
   if(matrixp(listData)){
      cell=ListToPoint(index);
  	   temp=cell.h;
      cell.h=cell.v;
      cell.v=temp;}
   else {
      if(!fixp(index))xlerror("not an integer",index);
      cell.h=0;
      cell.v=getfixnum(index);}
   theDialog=(DialogPtr)GETDIALOGADDRESS(slot_value(item,s_dialog));
   if(theDialog) {
      itemData=FindItemData(theDialog,item);
      itemData->itemPtr->GadgetText->IText=text;}}*/
    xlfail("not supported 49"); }

LVAL DialogListItemSelection(LVAL item,int set,LVAL index){
   xlfail("not supported 50");
   return(0);}

static max_line_size(char *s){
   char *bp;
   int w;
   Point sz;
   for(w=0;*s!='\0';*s++){
      for(bp=buf;*s!='\0'&&*s!='\r'&&*s!='\n';s++,bp++)*bp=*s;
      *bp='\0';
      sz=StringSize(buf);
      w=max(w, sz.h);}
   return(w);}
