/*
 * RequestString
 *
 * A program which requests a string from the user and prints it on
 * the standard output.
 *
 * Written by Peter Bengtsson who does not agree to be held responsible
 * for any damage or loss resulting from the use of this program.
 *
 * Use it as you wish for any and all nice things you can think of.
 */

#include <proto/exec.h>
#include <exec/memory.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/intuition_protos.h>
#include <pragmas/intuition_pragmas.h>
#include <clib/graphics_protos.h>
#include <pragmas/graphics_pragmas.h>
#include <clib/gadtools_protos.h>
#include <pragmas/gadtools_pragmas.h>
#include <graphics/gfxbase.h>
#include <utility/tagitem.h>
#include <utility/hooks.h>
#include <intuition/sghooks.h>
#include <intuition/intuition.h>
#include <clib/alib_protos.h>

#include <string.h>

LONG __saveds RequestString(void);
ULONG __saveds __asm HookFunc(register __a0 struct Hook *TheHook,
                              register __a2 struct SGWork *Obj,
                              register __a1 APTR Mess);


#define  TEMPLATE "STRING,TEXT/K,TITLE/K,NOGADS/S,WIDTH/N,SAFE/S,PERSIST/S,ENCRYPT/S,COMPARE/K,PUBSCREEN/K"

#define  NArgs 10          /* Number of arguments */

#define  STRING      0
#define  TEXT        1
#define  TITLE       2
#define  NOGADS      3
#define  WIDTH       4
#define  SAFE        5
#define  PERSIST     6
#define  ENCRYPT     7
#define  COMPARE     8
#define  PUBSCREEN   9

#define  MINWIDTH    8     /* Minimum width of gadget in characters */

LONG __saveds RequestString(void)
{
struct DosLibrary       *DOSBase=NULL;
struct Library          *GadToolsBase=NULL;
struct IntuitionBase    *IntuitionBase=NULL;
struct GfxBase          *GfxBase=NULL;

LONG                    ArgList[NArgs];
ULONG                   MsgClass=0;
APTR                    VisInfo=NULL;
char                    CBuffer[12],UName[48];
struct TextExtent       TExtent;
struct Hook             SGHook={0,0,0};
struct RDArgs           *Args=NULL;
struct ExtIntuiMessage  *EIMess=NULL;
struct Gadget           *Gad=NULL,*GList=NULL;
struct Window           *Win=NULL;
struct Screen           *Scr=NULL;
char                    *ReturnText=NULL;

int                     WWidth=0,WHeight=0;
int                     GWidth=32,GHeight=1;
long                    TXPos=0,TYPos=0;

LONG                    Ret=20;

const char *v="$VER: RequestString 39.4 (18.05.97)"; /* Bump this to 4X.1? */

struct TagItem WindowTags[]={
   {WA_PubScreen,0},
   {WA_Gadgets,0},
   {WA_Left,0},
   {WA_Top,0},
   {WA_Width,0},
   {WA_Height,0},
   {WA_DragBar,FALSE},
   {WA_DepthGadget,FALSE},
   {WA_CloseGadget,FALSE},
   {WA_Title,NULL},
   {WA_IDCMP,IDCMP_GADGETUP|IDCMP_INACTIVEWINDOW|IDCMP_ACTIVEWINDOW|IDCMP_REFRESHWINDOW|IDCMP_CLOSEWINDOW},
   {WA_SizeGadget,FALSE},
   {WA_Activate,TRUE}
};

struct NewGadget  StringGad={10,5,80,15,NULL,NULL,0,0L,NULL,NULL};

if((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",39L))!=NULL){
if((GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",39L))!=NULL){
if((IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",39L))!=NULL){
if((GadToolsBase=OpenLibrary("gadtools.library",39L))!=NULL){

memset(ArgList,0,NArgs*sizeof(LONG)); /* Clear the ArgList array */

Args=ReadArgs(TEMPLATE,ArgList,NULL);  /* How do we check for failure? */
if((Scr=LockPubScreen((UBYTE *)ArgList[PUBSCREEN]))!=NULL){
if((VisInfo=GetVisualInfo(Scr,NULL))!=NULL){
if((Gad=CreateContext(&GList))!=NULL){

if(ArgList[WIDTH]!=0)GWidth = *((LONG *)ArgList[WIDTH]);
if(GWidth<8)GWidth=8;

GHeight=GHeight*Scr->RastPort.TxHeight+6;
WHeight=GHeight+8;

do{
   GWidth=GWidth*Scr->RastPort.TxWidth+4;
   WWidth=GWidth+20;
   if(WWidth>Scr->Width)
      GWidth=(Scr->Width-20)/(Scr->RastPort.TxWidth)-1;
}while(WWidth>Scr->Width);

/* Forbid()/Permit() around this following poking in GfxBase? */

FontExtent(GfxBase->DefaultFont,&TExtent);

if(ArgList[TEXT]!=0){
   WHeight+=(TExtent.te_Height);
   if((TExtent.te_Width*strlen((char *)ArgList[TEXT])+20)>WWidth)
      WWidth=(TExtent.te_Width*strlen((char *)ArgList[TEXT])+20);
}

/* If the window has a title, it will also have a dragbar, closegadget
 * and a depthgadget.
 */
if(ArgList[TITLE]!=0){
   WindowTags[6].ti_Data=TRUE;
   if(ArgList[NOGADS]==0){
      WindowTags[7].ti_Data=TRUE;
      WindowTags[8].ti_Data=TRUE;
   }
   WindowTags[9].ti_Data=(ULONG)ArgList[TITLE];
   WHeight+=Scr->RastPort.TxHeight; /* Should be Win->BorderTop, see also note below. */
}

StringGad.ng_VisualInfo=VisInfo;
StringGad.ng_Width=GWidth;
StringGad.ng_Height=GHeight;
StringGad.ng_TopEdge=WHeight-(GHeight+4); /* The '4' should be replaces by Win->BorderTop but how? The window is not yet opened. */
StringGad.ng_LeftEdge=(WWidth-GWidth)>>1;

/* Initialize the Hook if we are using a safe stringgadget.
 * SGHook.d_Data points to storage space for the string which
 * will be returned. Memory sould be cleared.
 */

if(ArgList[SAFE]!=0){
   SGHook.h_Entry=(unsigned long (*)())HookFunc;
/* SGHook.h_SubEntry=NULL; */
   SGHook.h_Data=AllocVec(98,MEMF_ANY|MEMF_CLEAR); /* Should be made dynamic if possible */
   if(ArgList[STRING]!=0){
      int   i = -1;
      strcpy((char *)SGHook.h_Data,(char *)ArgList[STRING]);
      while(((char *)ArgList[STRING])[++i]!=0)((char *)ArgList[STRING])[i]='*';
   }
}

/* Here goes nothing... */

if((Gad=CreateGadget(STRING_KIND,Gad,&StringGad,GTST_EditHook,(ArgList[SAFE]==0)?NULL:&SGHook,GTST_String,ArgList[STRING],TAG_END))!=NULL){

/* Screen Gadget */
WindowTags[0].ti_Data=(ULONG)Scr;
WindowTags[1].ti_Data=(ULONG)GList;
/* Width & Height */
WindowTags[4].ti_Data=(ULONG)WWidth;
WindowTags[5].ti_Data=(ULONG)WHeight;
/* X & Y Pos */
WindowTags[2].ti_Data=(ULONG)(Scr->Width-WWidth)>>1;
WindowTags[3].ti_Data=(ULONG)(Scr->Height-WHeight)>>1;

if((Win=OpenWindowTagList(NULL,WindowTags))!=NULL){

/* Is there some (un)informative text in the window? */

if(ArgList[TEXT]!=0){
   SetAPen(Win->RPort,1);  /* Colour should rather be selected with thougt to the background of the window, fix this. */
   TXPos=(WWidth-TextLength(Win->RPort,(char *)ArgList[TEXT],(unsigned long)strlen((char *)ArgList[TEXT])))>>1;
   TYPos=GfxBase->DefaultFont->tf_Baseline+Win->BorderTop+1;
   Move(Win->RPort,TXPos,TYPos);
   Text(Win->RPort,(char *)ArgList[TEXT],(unsigned long)strlen((char *)ArgList[TEXT]));
}

do{

WaitPort(Win->UserPort);

EIMess=(struct ExtIntuiMessage *)GT_GetIMsg(Win->UserPort);

MsgClass=EIMess->eim_IntuiMessage.Class;

GT_ReplyIMsg((struct IntuiMessage *)EIMess);

switch(MsgClass) {

case  IDCMP_ACTIVEWINDOW   :
   ActivateGadget(Gad,Win,NULL);
break;

case  IDCMP_INACTIVEWINDOW :
   if(ArgList[PERSIST]){
   ScreenToFront(Win->WScreen);
   WindowToFront(Win);
   ActivateWindow(Win);
   }
break;

case  IDCMP_REFRESHWINDOW  :
   GT_BeginRefresh(Win);
   if(ArgList[TEXT]!=0){
      Move(Win->RPort,TXPos,TYPos);
      Text(Win->RPort,(char *)ArgList[TEXT],(unsigned long)strlen((char *)ArgList[TEXT]));
   }
   GT_EndRefresh(Win,TRUE);
break;


}

}while((MsgClass!=IDCMP_GADGETUP) && (MsgClass!=IDCMP_CLOSEWINDOW));

GT_GetGadgetAttrs(Gad,Win,NULL,GTST_String,&ReturnText,TAG_END);

if(ArgList[SAFE]!=0)ReturnText=(char *)SGHook.h_Data;

/* Try to find out who the user is if we are to encrypt the output.   */
/* I really don't know how to acquire the username, but this might    */
/* be a good guess of how to do it.                                   */

if(ArgList[ENCRYPT]!=0){
   if(GetVar("USER",UName,47,0L)==-1)
      if(GetVar("USERNAME",UName,47,0L)==-1)
         if(GetVar("LOGIN",UName,47,0L)==-1)
               UName[0]=0;
   ACrypt(CBuffer,ReturnText,UName);
   ReturnText=CBuffer;
}

Printf("\"%s\"\n",ReturnText);

/* Here follows the COMPARE parameter. If the input string is not equal
 * to the argument of COMPARE we return WARN.
 */

if(ArgList[COMPARE]!=0)
   Ret=(strcmp(ReturnText,(char *)ArgList[COMPARE]))?RETURN_WARN:0;
else
   Ret=0;

}}}}  /* if:s */

/* LockPubScreen failure only warrants a 10 if user defined a screen */
} else if(ArgList[PUBSCREEN]!=0)Ret=10;

/* Clean up. */

if(SGHook.h_Data!=NULL)FreeVec(SGHook.h_Data);
if(Args)FreeArgs(Args);
if(Win)CloseWindow(Win);
if(GList)FreeGadgets(GList);
if(VisInfo)FreeVisualInfo(VisInfo);
if(Scr)UnlockPubScreen(NULL,Scr);

CloseLibrary(GadToolsBase);
}
CloseLibrary((struct Library *)IntuitionBase);
}
CloseLibrary((struct Library *)GfxBase);
}
CloseLibrary((struct Library *)DOSBase);
}

return(Ret);

}


/* EditHook function for safe stringgadgets */


ULONG __saveds __asm HookFunc(register __a0 struct Hook *TheHook,
                              register __a2 struct SGWork *Obj,
                              register __a1 ULONG *Mess)
{
int   i0,i1;
char  * const Tmp=(char *)(TheHook->h_Data);

if(Mess[0]==SGH_KEY)
{
   Obj->Actions|=SGA_USE;

   switch(Obj->EditOp)
   {
   case  EO_DELBACKWARD :
      i0=Obj->StringInfo->BufferPos;
      i1=(Obj->StringInfo->NumChars)-(Obj->NumChars);
      strcpy(&Tmp[i0-i1],&Tmp[i0]);
   break;

   case  EO_DELFORWARD :
      i0=Obj->StringInfo->BufferPos;
      i1=(Obj->StringInfo->NumChars)-(Obj->NumChars);
      strcpy(&Tmp[i0],&Tmp[i0+i1]);
   break;

   case  EO_ENTER :
      /* Exit handling. */
   break;

   case  EO_RESET :
      Tmp[0]=0;
   break;

   case  EO_INSERTCHAR :
      if(strlen(Tmp)>96)Obj->Actions&=~SGA_USE;
      i0=Obj->StringInfo->BufferPos;
      Obj->WorkBuffer[i0]='*';
      i1=strlen(Tmp)+1;
      while(i1--!=i0)Tmp[i1+1]=Tmp[i1];
      Tmp[i0]=Obj->Code;
   break;

   case  EO_CLEAR :
      Tmp[0]=0;
   break;

   case  EO_BIGCHANGE : /* Not Allowed */
   case  EO_UNDO :
   case  EO_SPECIAL : 
      Obj->Actions&=~SGA_USE;
   break;
   }

   return(~0);
} else {
   Obj->Actions&=~SGA_USE;

   return(0);
}

}
