/* Edit by Frank on 20 Jun 5:25pm */
/* In commdialog(), don't set parity on sio chip. */
/* Edit by Bill on Apr 30 00:11 */
/* use mygetitem instead of getitem, it doesn't convert from pascal to c */
/* Redo handapple */
/* Edit by Bill on Apr 21 17:27 */
/* Try to fix meaning of TURN and TURNCH in dialog box */
/* Remove version from dofiledialog dialog box */


/*
 * file ckmutl.c
 *
 * Module of mackermit containing code for the menus and other MacIntosh
 * things.
 *
 */

/*
 Copyright (C) 1985, Trustees of Columbia University in the City of New York.
 Permission is granted to any individual or institution to use, copy, or
 redistribute this software so long as it is not sold for profit, provided this
 copyright notice is retained. 
*/

#include "ckcker.h"
#include "ckcdeb.h"
#include "ckmdef.h"			/* General Mac defs */
#include "ckmres.h"			/* Resource file defs */
#include <ctype.h>			/* For isdigit */
#include "mac/quickdraw.h"		/* Macintosh C interface */
#include "mac/osintf.h"
#include "mac/toolintf.h"
#include "mac/packintf.h"

extern MenuHandle menus[];


/*  B L D L E N  --  Make length-encoded copy of string  */

char *
bldlen(str,dest)
char *str, *dest;
{
    int len;

    len = strlen(str);
    *dest = tochar(len);
    strcpy(dest+1,str);
    return(dest+len+1);
}

/*  S E T G E N  --  Construct a generic command  */

setgen(stor,type,arg1,arg2,arg3)
char *stor, type, *arg1, *arg2, *arg3;
{
    char *upstr, *cp;

    cp = stor;
    *cp++ = type;
    *cp = '\0';
    if (*arg1 != '\0') {
	upstr = bldlen(arg1,cp);
	if (*arg2 != '\0') {
	    upstr = bldlen(arg2,upstr);
	    if (*arg3 != '\0') bldlen(arg3,upstr);
	}
    }
    debug(F110,"setgen",stor,0);
}


/*
 * printerr - display error message and number in standard error box.
 *
 */

printerr(str,err)
char *str;
int err; 
{
    int i;
    char error[10];

    if (err) 				/* Err=0 signals message only */
    {
	for(i=0; i<10; error[i++]='\0');    /* Make string null terminated */

	NumToString(err,error);  	/* Convert err number */
	ParamText(str,error,"",""); 	/* Insert strings into error box */
    }
    else ParamText(str,"","","");

    StopAlert(ALERT_ERROR,NILPROC);
}


/*
 * fatal - close file, and exit to shell.
 */

fatal(str,err)
char *str;
int err;
{
    printerr(str,err);
    doexit();				/* quit */
}

/* 
 * aboutKermit - Display the about kermit dialog box, wait for user to
 *     	      	 hit the OK button (maybe this should be a mouse click).
 *
 * The kermit version string is stored in the resource fork as type 
 * "KERM" ID 0 -- load this resource into memory, and lock it.  The
 * resource is locked so that we won't go off the deep end if the
 * heap gets compacted during the dialog display.  This would cause
 * problems because we are using the de-referenced handle, the actual
 * location of the string and not a pointer (handle) to it.
 *
 */

aboutKermit()
{
  Handle kversion;
  static char ktype[] = "KERM";
  DialogPtr kDialog;			/* our dialog */
  int itemhit;				/* item clicked */

  kversion = GetResource(ktype,0);	/* get version information */
  LoadResource(kversion);		/* make sure loaded */
  HLock(kversion);			/* prevent movement */
  ParamText(isapstr(*kversion),"","",""); /* set it up for display via ^0 */
					/*  de-reference the handle */
  kDialog = GetNewDialog(ABOUTID,NILPTR,(WindowPtr) -1);
  ModalDialog(NILPROC,&itemhit);	/* wait for OK */
  DisposDialog(kDialog);		/* dispose dialog box */
  HUnlock(kversion);			/* undo previous HLock */
  ReleaseResource(kversion);		/* no longer needed */
}


/* handapple - Handle the apple menu, either running a desk accessory
 *    	       or calling a routine to display information about our
 *    	       program.  For desk accessories use "mygetitem" instead
 *    	       of standard GetItem.  mygetitem does not convert the name
 *    	       into C form thus averting the problems of DA names with
 *    	       imbedded nulls (and those without).  Use the practice of
 *    	       checking for available memory, and saving the GrafPort
 *    	       described in the DA Manager's Guide.
 */

handapple(accitem)
int accitem;
{
    GrafPtr saveport;			/* Where to save current port */
    Handle acchdl;			/* holds ptr to accessory resource */
    char accname[255];			/* string holds accessory name */
    int accsize;			/* holds size of the acc + stack */

    if (accitem == 1)			/* tell about Kermit? */
    {
      aboutKermit();			/* yes, "about kermit" please */
      return;				/* and return */
    }

    mygetitem(menus[APPL_MENU],accitem,accname); /* get the pascal name */
    SetResLoad(FALSE);			/* don't load into memory */
    accsize = SizeResource(		/* figure out acc size + heap */
      	      	GetNamedResource('DRVR',isapstr(accname)+3072));
    acchdl = NewHandle(accsize);	/* try for a block this size */
    SetResLoad(TRUE);			/* reset flag for rsrc mgr */
    if (acchdl == NIL)			/* able to get a chunk? */
    {
      	printerr("Not enough memory for accessory. Requires: ",accsize);
	return;				/* failed */
    }
    DisposHandle(acchdl);		/* get rid of this handle */
    GetPort(&saveport);			/* save the current port */
    OpenDeskAcc(isapstr(accname));	/* run desk accessory */
    SetPort(saveport);			/* and put back our port */
}

/*
 * setserial - set the baud and parity for the serial port.
 *
 */

typedef struct {
  int kerval;
  int macval;
} KMTBL;

KMTBL kerbaudtbl[] = {
    {300,baud300},    {600,baud600},     {1200,baud1200},   {1800,baud1800},
    {2400,baud2400},  {4800,baud4800},   {7200,baud7200},   {9600,baud9600},
    {19200,baud19200},{57600,baud57600}
  };

#define kerbaudtblln ((sizeof (kerbaudtbl))/(sizeof (KMTBL)))

KMTBL kerparitytbl[] = {
    {KPARITY_ODD,MPARITY_ODD},    {KPARITY_EVEN,MPARITY_EVEN},
    {KPARITY_MARK,MPARITY_MARK},  {KPARITY_SPACE,MPARITY_SPACE},
    {KPARITY_NONE,MPARITY_NONE}
  };

#define kerparitytblln ((sizeof (kerparitytbl))/(sizeof (KMTBL)))

setserial(irefnum,orefnum,b,p)
{
  int err,i,mb,mp = 0;

  speed = b;				/* set global kermit value */

  for (i=0; i <= kerbaudtblln; i++)
    if (kerbaudtbl[i].kerval == b)
     mb = kerbaudtbl[i].macval;		/* set mac value for baud */

  for (i=0; i <= kerparitytblln; i++)
    if (kerparitytbl[i].kerval == p)
     mp = kerparitytbl[i].macval;	/* set mac value for parity */
     
  err = SerReset(irefnum,mb+mp);	/* reset serial input port */
  if (err != noErr)
   fatal("setserial couldn't set port: ",err);
   
  err = SerReset(orefnum,mb+mp);	/* reset serial output port */
  if (err != noErr)
   fatal("setserial couldn't set port: ",err);
}  


typedef struct {
  short resval;
  int serval;
 } RESSERTBL;

RESSERTBL commduplextbl[] = {		/* "echo" for duplex */
    {CRES_ECHOLCL,1},			/* local should do echo */
    {CRES_ECHOREM,0},			/* remote is doing echo */
    {0,0}};

RESSERTBL commbaudtbl[] = {
    {CRES_BAUD300,300},
    {CRES_BAUD600,600},
    {CRES_BAUD1200,1200},
    {CRES_BAUD1800,1800},
    {CRES_BAUD2400,2400},
    {CRES_BAUD4800,4800},
    {CRES_BAUD7200,7200},
    {CRES_BAUD9600,9600},
    {CRES_BAUD19200,19200},
    {CRES_BAUD57600,57600},
    {0,0}};


RESSERTBL commparitytbl[] = {
    {CRES_PARODD,KPARITY_ODD},
    {CRES_PAREVEN,KPARITY_EVEN},
    {CRES_PARMARK,KPARITY_MARK},
    {CRES_PARSPACE,KPARITY_SPACE},
    {CRES_PARNONE,KPARITY_NONE},
    {0,0}};

/*
 * rshilite - hilite the radio control item matching the given
 *    	  serial value, and disable all other control items in the
 *    	  resource-serial table.
 *
 */

rshilite(servalue,rstbl,dlg)
int servalue;
RESSERTBL rstbl[];
DialogPtr dlg;
{
 int i,itemtype;
 Rect itembox; 
 Handle itemhdl;
 
 for (i=0; rstbl[i].resval != 0; i++)
 {
   GetDItem(dlg,rstbl[i].resval,&itemtype,&itemhdl,&itembox);
   if (itemtype != ctrlItem + radCtrl)
    printerr("rshilite called with non radio control: ",rstbl[i].resval);
   if (rstbl[i].serval == servalue)
    SetCtlValue((ControlHandle) itemhdl,btnOn);
   else
    SetCtlValue((ControlHandle) itemhdl,btnOff);
 }
}
    
rsreference(rstbl,value,dlg)
RESSERTBL rstbl[];
DialogPtr dlg;
int value;
{
  int i,itemtype;
  Rect itembox;
  Handle itemhdl;

  for (i=0; rstbl[i].resval != 0; i++)
  {
   GetDItem(dlg,rstbl[i].resval,&itemtype,&itemhdl,&itembox);
   SetCRefCon((ControlHandle) itemhdl,value);
  }
}

/*
 * rsserval - given a resource ID for a control and a resource-serial
 *    	      table, return the serial value.
 */

int rsserval(resvalue,rstbl)
int resvalue;
RESSERTBL rstbl[];
{
 int i;
 for (i=0; rstbl[i].resval != 0; i++)
   if (rstbl[i].resval == resvalue)
    return(rstbl[i].serval);
 fatal("rsserval didn't find: ",resvalue);
 return(0);				/* never get here */
}


/*
 * commdialog - enter communications setup dialog.
 *
 */

#define CREF_BAUD 1
#define CREF_PARITY 2
#define CREF_ECHO 3

commdialog() 
{
  DialogPtr commdlg;
  int i,itemhit,itemtype,dlgspeed,dlgparity,dlgduplex;
  
  Handle itemhdl;
  Rect itembox;

  commdlg = GetNewDialog(COMMBOXID,NILPTR,(WindowPtr) -1);
  ShowWindow(commdlg);

  dlgspeed = speed;			/* initialize to current global */
  dlgparity = parity;			/*  values of baud and parity */
  dlgduplex = duplex;			/*  and tty emulator's duplex val */

  rshilite(dlgspeed,commbaudtbl,commdlg); /* hilite our baud */
  rshilite(dlgparity,commparitytbl,commdlg);	/* hilite our parity */
  rshilite(dlgduplex,commduplextbl,commdlg); /* hilite our echo mode */
  
/* for all baud and parity controls set the reference value for each
 * control to the resource-serial table address so we can manipulate
 * these controls easily during the dialog processing.
 */

  rsreference(commbaudtbl,CREF_BAUD,commdlg); /* setup control ref's */
  rsreference(commparitytbl,CREF_PARITY,commdlg);
  rsreference(commduplextbl,CREF_ECHO,commdlg);

  for (;;)
  {
    ModalDialog(NILPROC,&itemhit);
    switch (itemhit) {
      case OKBtn: 
	duplex = dlgduplex;		/* set for tty emulator */
	 				/* set fork kermit and mac */
	parity = dlgparity;
      	setserial(innum,outnum,dlgspeed,KPARITY_NONE);

      case QuitBtn:
      	DisposDialog(commdlg);	/* finished with the dialog */
	return;				/* so return */
	
      default:				/* default is radio button */
      	GetDItem(commdlg,itemhit,&itemtype,&itemhdl,&itembox);      
        i = GetCRefCon((ControlHandle) itemhdl);
	switch (i) {
	  case CREF_BAUD:
	    dlgspeed = rsserval(itemhit,commbaudtbl);
	    rshilite(dlgspeed,commbaudtbl,commdlg);
	    break;
	    
	  case CREF_PARITY:
	    dlgparity = rsserval(itemhit,commparitytbl);
	    rshilite(dlgparity,commparitytbl,commdlg);
	    break;			  
	    
      	  case CREF_ECHO:
	    dlgduplex = rsserval(itemhit,commduplextbl);
	    rshilite(dlgduplex,commduplextbl,commdlg);
	    break;
	    
          default:			  
	    printerr("Item has no refcon: ",itemhit);
	}
     }  
  }
}

RESSERTBL protoblktbl[] = {		/* block check:  */
  {PRES_BLK1,1},			/*  type 1 */
  {PRES_BLK2,2},			/*  type 2 */
  {PRES_BLK3,3},			/*  type 3 */
  {0,0}};

#define KTURN_NONE 0128			/* indicate no handshake */

RESSERTBL protohstbl[] = {		/* hand shake: */
  {PRES_HSBELL,7},			/*  bell = 7 */
  {PRES_HSCR,15},			/*  cr = 15 */
  {PRES_HSESC,33},			/*  esc = 33  */
  {PRES_HSLF,12},			/*  lf = 12 */
  {PRES_HSNONE,KTURN_NONE},		/*  none = 128 */
  {PRES_HSXON,XON},			/*  xon = 21 */
  {PRES_HSXOFF,23},			/*  xoff = 23 */
  {0,0}};

RESSERTBL protointtbl[] = {		/* edit text integer items: */
  {PRES_INPADN,0},			/* inbound pad count edit text */
  {PRES_OUTPADN,1},			/* outbound pad count edit text */
  {PRES_INTIMEO,2},			/* inbound secs timeout edit text */
  {PRES_OUTTIMEO,3},			/* outbound secs timeout edit text */
  {PRES_INPKTLEN,4},			/* inbound packet length edit text */
  {PRES_OUTPKTLEN,5},			/* outbound packet length edit text */
  {0,0}};


int *protointcells[] = {		/* parallel to above table! */
  &mypadn,				/* inbound pad count */
  &npad,				/* outbound pad count */
  &timint,				/* inbound timeout in secs */
  &rtimo,				/* outbound timeout in secs */
  &rpsiz,				/* inbound pkt length */
  &spsiz				/* inbound pkt length */
};
  
RESSERTBL protochrtbl[] = {		/* edit text character items: */
  {PRES_INPADC,0},			/* inbound pad chr edit text */  
  {PRES_OUTPADC,1},			/* outbound pad chr */
  {PRES_INEOP,2},			/* inbound end of packet edit text */
  {PRES_OUTEOP,3},			/* outbound end of packet edit text */
  {PRES_INSOP,4},			/* inbound start of pkt edit text */  
  {PRES_OUTSOP,5},			/* outbound start of pkt edit text */  
  {0,0}};

  
char *protochrcells[] = {		/* parallel to above table! */
  &mypadc,				/* inbound pad char */  
  &padch,				/* outbound pad char */
  &eol,					/* inbound end of packet char */
  &seol,				/* outbound end of pkt char */
  &stchr,				/* inbound start of pkt char */
  &mystch				/* outbound start of packet char */
};


/*
 * etsetval - Set the numeric value in the dialog's edit text item.
 *
 */
 
etsetval(item,val,dlg)
DialogPtr dlg;
int item,val;
{
 int itemtype;
 Rect itemrect;
 Handle itemhdl;
 char itembuf[10];
 
 NumToString(val,itembuf); 
 GetDItem(dlg,item,&itemtype,&itemhdl,&itemrect);
 if (itemtype != editText)
  printerr("Bad item in etsetvalue: ",item);
 else
  SetIText(itemhdl,itembuf);
}

/*
 * etgetcc - Set edit text paramter from dialog window to an ASCII
 *    	     control character value.  Returns FALSE if value is
 *    	     illegal, warning has been displayed.
 */

int etgetcc(item,dlg,chrcell)
DialogPtr dlg;
int item;
char *chrcell;
{
 int itemtype,i;
 int rslt;
 Rect itemrect;
 Handle itemhdl;
 char itembuf[256],c;

 GetDItem(dlg,item,&itemtype,&itemhdl,&itemrect);
 if (itemtype != editText)
 {
  printerr("etsetcc item not editText: ",item);
  return(FALSE);
 }
 GetIText(itemhdl,itembuf);
/* p2cstr(itembuf);			/* convert to c string */
 for (i=0; (c = itembuf[i]) != 0; i++)	/* check for digits */
  if (!isdigit(c))
  {
    printerr("Field contains a non numeric, code ",c);
    return(FALSE);
  }
 StringToNum(itembuf,&rslt);
 if ((rslt > 037) && (rslt != 0177))
 {
  printerr("Not in ASCII control range: ",rslt);
  return(FALSE);
 }
 *chrcell = (char) rslt;
 return(TRUE);
}
 
/*
 * etgetnum - Set edit text paramet from dialog to numeric cell.
 *    	      Returns FALSE if value is non numeric or not in range
 *    	      in which case an error message is printed.
 */

int etgetnum(item,dlg,intcell)
DialogPtr dlg;
int item;
int *intcell;
{
 int itemtype,i;
 int rslt;
 Rect itemrect;
 Handle itemhdl;
 char itembuf[256],c;

 GetDItem(dlg,item,&itemtype,&itemhdl,&itemrect);
 if (itemtype != editText)
 {
  printerr("etsetnum item not editText: ",item);
  return(FALSE);
 }
 GetIText(itemhdl,itembuf);
/* p2cstr(itembuf);			/* convert to c string */
 for (i=0; (c = itembuf[i]) != 0; i++)	/* check for digits */
  if (!isdigit(c))
  {
    printerr("Field contains a non numeric, code ",c);
    return(FALSE);
  }
 StringToNum(itembuf,&rslt);
 if (rslt > 94)
 {
  printerr("Sorry, 94 is the maximum: ",rslt);
  return(FALSE);
 }
 *intcell = (int) rslt;
 return(TRUE);
}

 
/*
 * protodialog - enter protocol setup dialog.
 *
 */

#define PREF_BLKCHK 1
#define PREF_HNDSHK 2

protodialog() {
  
  DialogPtr protoDialog;
  int i,itemhit,itemtype,dlgval,dlgint,dlgbctr,dlgturnch;
  Handle itemhdl;
  Rect itembox;
  char dlgchr;

  protoDialog = GetNewDialog(PROTOBOXID,NILPTR,(WindowPtr) -1);
  ShowWindow(protoDialog);

  dlgbctr = bctr;			/* init local block check */
  dlgturnch = turnch;			/* handshake */
  if (!turn)				/* no turn character? */
   dlgturnch = KTURN_NONE;		/* indicate none */

/* for all button controls set the reference value */

  rsreference(protohstbl,PREF_HNDSHK,protoDialog);
  rsreference(protoblktbl,PREF_BLKCHK,protoDialog);

/* for each button controls, hilite the current setting */

  rshilite(dlgturnch,protohstbl,protoDialog); /* hilite our hand shake */
  rshilite(dlgbctr,protoblktbl,protoDialog);  /* hilite our block check */

/* for each edit text item, set current value in edit text */
  
  for (i=0; protochrtbl[i].resval != 0; i++)
    etsetval(protochrtbl[i].resval,
      	      (int) *protochrcells[i],protoDialog);
	      
  for (i=0; protointtbl[i].resval != 0; i++)
    etsetval(protointtbl[i].resval,
      	       *protointcells[i],protoDialog);

  for (;;) {

    ModalDialog(NILPROC,&itemhit);
    
    switch (itemhit) {
      case OKBtn:			/* finish up */

      	turnch = dlgturnch;		/* set global handshake */
	turn = (turnch == KTURN_NONE) ? FALSE : TRUE;	/* set turn */
	bctr = dlgbctr;			/* set block type check */
	
	for (i=0; protochrtbl[i].resval != 0; i++)  /* set chr vals */
          if (!etgetcc(protochrtbl[i].resval,protoDialog,protochrcells[i]))
            fatal("During OKBtn etgetcc failed!",i);     

	for (i=0; protointtbl[i].resval != 0; i++)  /* set int vals */
	  if (!etgetnum(protointtbl[i].resval,protoDialog,protointcells[i]))
          fatal("During OKBtn etgetnum failed!",i);
	  
      case QuitBtn:			/* fall in from above */
      	DisposDialog(protoDialog);	/* finished with the dialog */
	return;				/* return */

      case PRES_INPADC:
      case PRES_OUTPADC:
      case PRES_INEOP:
      case PRES_OUTEOP:
      case PRES_INSOP:
      case PRES_OUTSOP:
      	if (!etgetcc(itemhit,protoDialog,&dlgchr))
	{
	  dlgval = rsserval(itemhit,protochrtbl); /* get back tbl idx */
	  dlgval = (int) *protochrcells[dlgval];  /* now cell value */
	  etsetval(itemhit,dlgval,protoDialog);   /* and reset it */
      	}
	break;

      case PRES_INPADN:
      case PRES_OUTPADN:
      case PRES_INTIMEO:
      case PRES_OUTTIMEO:
      case PRES_INPKTLEN:
      case PRES_OUTPKTLEN:
      	if (!etgetnum(itemhit,protoDialog,&dlgint))	  
	{
	  dlgval = rsserval(itemhit,protointtbl); /* get back tbl idx */
	  dlgval = *protointcells[dlgval]; /* now cell value */
	  etsetval(itemhit,dlgval,protoDialog); /* and reset it */
      	}
	break;

      default:
      	GetDItem(protoDialog,itemhit,&itemtype,&itemhdl,&itembox);          
	if (itemtype != ctrlItem+radCtrl)	/* must be radio button */
	 fatal("Not radio button",itemhit);
	i = GetCRefCon((ControlHandle) itemhdl);
	if (i == PREF_BLKCHK)
	{
	  dlgbctr = rsserval(itemhit,protoblktbl);
	  rshilite(dlgbctr,protoblktbl,protoDialog);
      	}
	else if (i == PREF_HNDSHK)
	{
	  dlgturnch = rsserval(itemhit,protohstbl);
	  rshilite(dlgturnch,protohstbl,protoDialog);
      	}
	else printerr("raido item has bad refcon: ",itemhit);
	break;				/* all done with radio buttons */
      }
   }
}
  
    
/*
 * handlelaunch - Handle transfer to another application.  Called when
 *    	  "launch" selected from menu bar.
 *
 */

struct launchparams {
	char	*lnamep;
	long	lunknown;
	short	lsound;
  } lparams;
char lfile[32];
 
handlelaunch(item)
{
 SFReply sfr;
 static char appltype[] = "APPL";	/* wan't to be long aligned */
 Point where;

 if (item == TOAP_LAU)			/* they want to select application */
 {
  SetPt(&where,75,115);
  SFGetFile(&where,"",NILPROC,1,appltype,NILPROC,&sfr);
  if (!sfr.good)			/* hit cancel, return now */
   return;
   
  mac_cleanup();			/* about to leave, leave clean! */
   
  p2cstr(sfr.fName);
  strcpy(lfile,sfr.fName);
  c2pstr(lfile);
  }
  lparams.lnamep = lfile;  
  lparams.lunknown = 0;
  lparams.lsound = 0;
  SetVol((char *)0,sfr.vRefNum);	/* allow any drive */
  asm("lea lparams,a0");
  asm(".word 0xa9f2");
}
