#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/lists.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>

#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/dos.h>
#include <proto/diskfont.h>

#define EMACSXL_VERSION  "EmacsDLG 1.7"
#define EMACSFIG_VERSION "EmacsFIG 1.7"
#define SPECIAL_MAGIC    ";magic"
#define NUM_MAGIC	 10


int curfort;

struct fort_struct {
    char command[120+1];
    char desc[60+1];
};

#define NUM_COOKIE_TOPICS 20
struct {
    struct fort_struct fortune[NUM_COOKIE_TOPICS];
    unsigned char num_fort;
    char filereq_dir[80+1];
    char sp_arexx_port[20+1];
    char sp_start[120+1];
    char sp_check[20+1];
    char sp_exit[20+1];
    char whoports[80+1];
    unsigned char qstyle;
    unsigned char tabs;
    unsigned char readacc;
    unsigned char writeacc;
    unsigned char kool;
    unsigned char current_color[6];
    //   "Message text color"
    //   "Quote text color"
    //   "Help text color"
    //   "Summary text color"
    //   "Window border color"
    //   "Message line color"
    // custom text is not in this struct
} config;

#define EmacsXL_CFG "EmacsDLG.CFG"
#define EmacsXL_OLD "EmacsDLG.CFG.old"

/* function prototypes */
void __stdargs __main(char *);
void go_home(char *);
static void setup(void);
static void closedown(void);
static void create_gadgets(void);
static void handle_input(void);
static void handle_gadgetup(struct Gadget *, UWORD);
static void handle_gadgetdown(struct Gadget *, UWORD);
void show_emacs(void);
static void save_game(void);
static void set_defaults(void);
static int  load_file(void);

/* some constant definitions */
#define MIN_VERSION     37L      /* minimum version number for our libs */

#define MAX_CUSTOM_TEXT	80	/* max length of custom text strings */

/* Gadget ID's */
#define SAVE_ID			0
#define CANCEL_ID		1
#define TABSTOP_ID		2
#define QUOTE_CYCLE_ID		3
#define FORTUNE_ID		5
#define READ_ACCESS_ID		6
#define WRITE_ACCESS_ID		7
#define CUSTOM_TEXT_ID		8
#define CUSTOM_TEXT_DATA_ID	9
#define COLOR_CYCLE_ID		10
#define COLOR_BAR_ID		11
#define FILEREQ_DIR_ID		13
#define SPECIAL_ID		14
#define SPECIAL_INPUT_ID	15
#define KOOL_ID			16
#define SPELL_AREXX_PORT_ID     17
#define SPELL_CHECK_ID          18
#define SPELL_START_ID          19
#define SPELL_EXIT_ID           20
#define FORTDESC_ID             21
#define FORTADD_ID              22
#define FORTDEL_ID              23
#define FORTNEXT_ID             24
#define WHOPORTS_ID             25

// constant -1 is out of range for type unsigned char
#pragma msg 62 ignore

//#define SLIDER_MIN   1
//#define SLIDER_MAX   31

/* libraries we'll need to open */
struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Library *GadToolsBase = NULL;
struct Library *DiskfontBase = NULL;

struct Screen *screen = NULL;
struct Window *window = NULL;
struct Gadget *glist = NULL;     /* Gadget list pointer */
struct List list;                /* we need this for the ListView Gadget */
struct List splist;
struct Remember *rmem = NULL;    /* Intuition memory allocation for List */
void *vi = NULL;                 /* VisualInfo pointer */

struct Gadget *colorgad;
struct Gadget *customgad;
struct Gadget *spgad;
struct Gadget *splistgad;

struct Gadget *fgad_command;
struct Gadget *fgad_desc;
struct Gadget *fgad_add;
struct Gadget *fgad_del;
struct Gadget *fgad_next;

/* It's possible to use the default font with GadTools, but extra
   computations must be made to get the size and positions of the Gadgets
   right. We'll want to use Topaz-80 for simplicity. */

struct TextAttr topaz8 = {
   "topaz.font", 8, 0, 0
};
struct TextFont *amiga8;

struct TextAttr topaz11 = {
   "topaz.font", 11, 0, 0
};
struct TextFont *amiga11;

/* here we declare the information for our new window - notice that GadTools
   provides some constants which describe which IDCMP events the Window will
   need to handle the GadTools Gadgets properly. Just OR them into your
   normal IDCMPFlags field.
*/

struct NewWindow new_window = {
   0, 0, 640, 400,
   -1, -1,              /* DetailPen, BlockPen */
   LISTVIEWIDCMP | SLIDERIDCMP | CHECKBOXIDCMP | MXIDCMP | CLOSEWINDOW |
      REFRESHWINDOW,
   ACTIVATE | WINDOWCLOSE | SIMPLE_REFRESH | WFLG_BORDERLESS | WFLG_BACKDROP,
   NULL,                /* FirstGadget */
   NULL,                /* CheckMark */
   "",
   NULL,                /* Screen */
   NULL,                /* BitMap */
   0, 0, 0, 0,          /* min/max sizes */
   CUSTOMSCREEN
};

static char title[200];

struct NewScreen new_screen = {
   0, 0, /*STDSCREENWIDTH*/ 640, 400, 4,
   8, 6, HIRES | LACE, CUSTOMSCREEN, &topaz11,
   title,
   0, 0
};

#if 0
//OLD COLORS
//UWORD penspec[] = { ~0 };
struct ColorSpec colorspec[] = {
0, 10, 10, 10,	//grey
1, 0, 0, 0,	//black
2, 15, 15, 15,	//white
3, 0, 0, 15,	//lblue

5, 6, 6, 6,	//gray
4, 0, 0, 10,	//blue
6, 0, 10, 10,	//cyan
7, 0, 15, 15,	//lcyan

8, 10, 0, 10,	//magenta
9, 15, 0, 15,	//lmagenta
10, 0, 10, 0,	//green
11, 0, 15, 0,	//lgreen

12, 10, 0, 0,	//red
13, 15, 0, 0,	//lred
14, 10, 6, 0,	//yellow
15, 15, 15, 0,	//lyellow

-1, -1, -1, -1
};

/* EmacsXL color names */
char *cname[] = {		/* names of colors		*/
	"GREY", "BLACK", "WHITE", "LBLUE",
        "BLUE", "GRAY", "CYAN", "LCYAN",
	"MAGENTA", "LMAGENTA", "GREEN", "LGREEN",
	"RED", "LRED", "YELLOW", "LYELLOW"
};
#else

#define PEN_BLACK     7
#define PEN_LIGHTGREY 0

UWORD penspec[] = {
8, //detail
6, //block
7, //text
8, //shiny edge
7, //dark edge
14, //selected gadget
8, //text in selected gadget
0, //background must be zero
7, //other text color
~0
};

struct ColorSpec colorspec[] = {
0, 10, 10, 10,	//grey
1, 10, 0, 10,	//magenta
2, 10, 0, 0,	//red
3, 10, 6, 0,	//yellow

4, 0, 10, 0,	//green
5, 0, 10, 10,	//cyan
6, 0, 0, 10,	//blue
7, 0, 0, 0,	//black

8, 15, 15, 15,	//white
9, 15, 0, 15,	//lmagenta
10, 15, 0, 0,	//lred
11, 15, 15, 0,	//lyellow

12, 0, 15, 0,	//lgreen
13, 0, 15, 15,	//lcyan
14, 0, 0, 15,	//lblue
15, 7, 7, 7,	//gray

-1, -1, -1, -1
};

/* EmacsDLG color names */
char *cname[] = {		/* names of colors		*/
	"GREY", "MAGENTA", "RED", "YELLOW",
        "GREEN", "CYAN", "BLUE", "BLACK",
	"WHITE", "LMAGENTA", "LRED", "LYELLOW",
	"LGREEN", "LCYAN", "LBLUE", "GRAY"
};
#endif

char *custom[] = {
   "Quote Greeting",
   "Input \"yes\"",
   "Input \"no\"",
   "Abort prompt",
   "Exit prompt",
   "More prompt",
   "Help summary",
   "Quote summary",
   "To header",
   "Subject header",
   "Spell in progress",
   "Spell replace prompt",
   "Spell finished OK",
   "Spell finished errors",
   "Fortune query",
   "Command aborted",
   "Mark set (set-mark)",
   "Unknown command",
   "Region copied",
   "No mark set",
   "Search prompt",
   "Search text not found",
   "Reverse search prompt",
   "Replace (search/replace)",
   "with (search/replace)",
   "Key illegal",
   "Saving message",
   NULL
};
char customtext[sizeof(custom)/sizeof(char*)][MAX_CUSTOM_TEXT+1];
char *dflt_customtext[] = {
   "Check out what %F wrote on %d:%N",
   "yes",
   "no",
   "Abort message? (yes/NO) ",
   "Save message and exit? (YES/no) ",
   "More (YES/no) ",
#define IDX_PRESS_ESC 6
   "Press ESC ? for help      ^Z save & exit     ESC ESC abort     ESC Q quote",
#define IDX_QUOTE_LINE 7
   "RETURN quote line, ^XQ quote all, ESC Q back to message, ^C close window",
   "  To: ",
   "Subj: ",
#define IDX_ANY_KEY 10
   "Performing spell check...",
   "Enter new word or RETURN to keep, ^G to stop",
   "No spelling errors found.",
   "Spelling check finished.  %d unrecognized word(s), %d replaced.",
   "Append fortune to your message",
   "Aborted",
   "Mark set",
   "Unknown command",
   "Region copied",
   "No mark set",
   "Search",
   "Not found",
   "Reverse search",
   "Replace",
   "with",
   "Key illegal in this window",
   "Saving...",
};
short current_custom = 0; //index

char *customvar[] = {
"$zhello",
"$zyes",
"$zno",
"$zabort",
"$zbye",
"$zmore",
"$ztop",
"$zqh",
"$zto",
"$zsub",
"$zsp1",
"$zsp2",
"$zsp3",
"$zsp4",
"$zfquery",
"$zz008",
"$zz009",
"$zz019",
"$zz070",
"$zz076",
"$zz078",
"$zz079",
"$zz081",
"$zz084",
"$zz086",
"$zz109",
"$zz148",
};

char *colortype[] = {
   "Message text color",
   "Quote text color",
   "Help text color",
   "Summary text color",
   "Border color",
   "Message line color",
   NULL
};
USHORT color_selection = 0; /* index */
char textbuffer[20];    /* for displaying Gadget event information */

char *quotestyle[] = {
   "AB>",
   "Alan>",
   "ab>",
   ">",
   "Alan_B>",
   NULL
};

#if 0
char *logfile[] = {
   "Logfile OFF",
   "Logfile ON",
   NULL
};
#endif

#define SINGLE_LINE (unsigned char)0xc4
#define DOUBLE_LINE (unsigned char)0xcd
unsigned char kool;
unsigned char kool_array[] = { ' ', SINGLE_LINE, DOUBLE_LINE, '-', '=', '~', '.', ':' };

#if 0
char *koolstyle[] = {
   "Reverse Bar",
   "ANSI Single line",
   "ANSI Double line",
   NULL
};
#endif

char magic[NUM_MAGIC][MAX_CUSTOM_TEXT+1];
short current_magic;

void __regargs _CXBRK(int n) {}
int __regargs __chkabort(void) { return 0; }

#define INTWIDTH sizeof(int) * 3

char *int_asc(int i)
{
	register int digit;		/* current digit being used */
	register char *sp;		/* pointer into result */
	register int sign;		/* sign of resulting number */
	static char result[INTWIDTH+1]; /* resulting string */

	/* record the sign...*/
	sign = 1;
	if (i < 0) {
		sign = -1;
		i = -i;
	}

	/* and build the string (backwards!) */
	sp = result + INTWIDTH;
	*sp = 0;
	do {
		digit = i % 10;
		*(--sp) = '0' + digit;	/* and install the new digit */
		i = i / 10;
	} while (i);

	/* and fix the sign */
	if (sign == -1) {
		*(--sp) = '-';	/* and install the minus sign */
	}

	return(sp);
}

int myatoi(register char *p)
{
	register int n;
	n = 0;
	while (*p) {
		n = (n * 10) + *p - '0';
		++p;
	}
	return n;
}

unsigned char colorval(char *name)
{
	unsigned char i;
	for (i=0; i<16; ++i) {
		if (stricmp(name, cname[i]) == 0) {
			return i;
		}
	}
	return 2; // WHITE
}

void do_fortgads(void)
{
    int n;
    char buf[20];
    
    strcpy(buf, "Command  ");
    if (config.num_fort > 0) {
	char *p =  int_asc(curfort+1);
	if (strlen(p) == 1) {
	    strcpy(buf+9, p);
	} else {
	    strcpy(buf+8, p);
	}
    }
    SetAPen(window->RPort, PEN_BLACK);
    Move(window->RPort, fgad_command->LeftEdge-11*8-6, fgad_command->TopEdge +
	 amiga11->tf_Baseline);
    Text(window->RPort, buf, 10);
    
    n = config.num_fort == 0 ? TRUE : FALSE;
    GT_SetGadgetAttrs(fgad_command, window, NULL,
		      GTST_String, config.fortune[curfort].command,
		      GA_Disabled, n,
		      TAG_DONE);

    n = config.num_fort == 0 ? TRUE : FALSE;
    GT_SetGadgetAttrs(fgad_desc, window, NULL,
		      GTST_String, config.fortune[curfort].desc,
		      GA_Disabled, n,
		      TAG_DONE);

    n = config.num_fort >= NUM_COOKIE_TOPICS ? TRUE : FALSE;
    GT_SetGadgetAttrs(fgad_add, window, NULL, GA_Disabled, n, TAG_DONE);

    n = config.num_fort == 0 ? TRUE : FALSE;
    GT_SetGadgetAttrs(fgad_del, window, NULL, GA_Disabled, n, TAG_DONE);

    n = config.num_fort < 2 ? TRUE : FALSE;
    GT_SetGadgetAttrs(fgad_next, window, NULL, GA_Disabled, n, TAG_DONE);
}

void __stdargs __main(char *s)
{
   setup();
   create_gadgets();
   handle_input();
}

void go_home(txt)
char *txt;
{
   if (txt) {
	FPuts(Output(), txt);
	FPutC(Output(), '\n');
   }
   closedown();
   _XCEXIT(0);
}

static void setup(void)
{
   int newfile;
//   char fullpath[200];
//   char dir[150];

   /* open ze libraries */
   GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", MIN_VERSION);
   if (!GfxBase) {
	strcpy(title, "graphics.library");
	strcat(title, " V37+ required");
	go_home(title);
   }
   IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",
                                                       MIN_VERSION);
   if (!IntuitionBase) {
	strcpy(title, "intuition.library");
	strcat(title, " V37+ required");
	go_home(title);
   }
   GadToolsBase = (struct Library *)OpenLibrary("gadtools.library",
                                                MIN_VERSION);
   if (!GadToolsBase) {
	strcpy(title, "gadtools.library");
	strcat(title, " V37+ required");
	go_home(title);
   }
   DiskfontBase = OpenLibrary("diskfont.library", 33);
   if (!DiskfontBase) {
	strcpy(title, "diskfont.library");
	strcat(title, " V33+ required");
	go_home(title);
   }

   set_defaults();

   newfile = load_file();

   amiga8 = OpenFont(&topaz8);
   amiga11 = OpenDiskFont(&topaz11);
   if (!amiga11) {
       go_home("Can't find topaz 11");
   }


   strcpy(title, EMACSFIG_VERSION);

   
       if (newfile) {
	   strcat(title, " [New file] ");
       } else {
	   strcat(title, " [Old file] ");
       }
       //GetCurrentDirName(dir, 150);
       //strmfp(fullpath, dir, EmacsXL_CFG);
       //strncat(title, fullpath, 80 - strlen(title) - 1);
       GetCurrentDirName(title, 150);
       if (title[strlen(title)-1] != '/') {
	   strcat(title, "/");
       }
       strcat(title, EmacsXL_CFG);

   
   /* create an ANSI-colored screen */
   screen = OpenScreenTags(&new_screen,
			SA_Pens, penspec,
			SA_Colors, colorspec,
			TAG_DONE);
   if (!screen)
      go_home("scr?");

   /* get the screen's visual information data */
   vi = GetVisualInfo(screen, TAG_DONE);
   if (!vi)
      go_home("vis?");

   /* now open up a Window on our ANSI screen */
   new_window.Screen = screen;
   window = OpenWindowTags(&new_window,
			   WA_Width, screen->Width,
			   WA_Height, screen->Height,
                           TAG_DONE);
   if (!window)
      go_home("win?");

   SetFont(window->RPort, amiga11);
}

static void closedown(void)
{
    if (amiga8) CloseFont(amiga8);
    if (amiga11) CloseFont(amiga11);
    
    if (window) CloseWindow(window);

    if (glist) FreeGadgets(glist);
    if (vi) FreeVisualInfo(vi);

    if (screen) CloseScreen(screen);
    if (rmem) FreeRemember(&rmem, TRUE);

    if (GadToolsBase) CloseLibrary(GadToolsBase);
    if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
    if (GfxBase) CloseLibrary((struct Library *)GfxBase);
    if (DiskfontBase) CloseLibrary(DiskfontBase);
}

static void create_gadgets()
{
   UWORD top;              /* offset into Window under titlebar */
   struct NewGadget ng;    /* for Gadget positioning */
   struct Gadget *gad;     /* our running Gadget pointer */
   struct Node *node;      /* for our LISTVIEW list allocation */
   int index;              /* ditto */
   UWORD rbase, cbase, lbase, width;

   /* let's determine the top Window border height (taking into account
      whatever system font has been used to render the titlebar) so we can
      place the Gadgets properly within the Window. Overwriting of the
      titlebar is possible if you're not careful. */
   top = window->BorderTop + 1;

   /* this initial call is required when using the GadTool toolkit. It
      gives the toolkit a place to keep track of Gadget context information,
      and also forms a starting point to begin the Gadget creation with.
      Each Gadget creation call requires a pointer to the previous Gadget
      as one of its arguments, and this pointer is used for the first one.
      The Gadgets are automatically linked with this facility. Also, there
      is no need to check the returned Gadget pointer until it is actually
      used, such as if the Gadget data need be referenced, and of course
      before the Gadget list is added to a Window. */

   /* we pass the ADDRESS of our Gadget list pointer, so the toolkit can
      allocate some memory there */
   gad = CreateContext(&glist);

   /* now we can fill out the NewGadget structure to describe where we want
      the Gadget to be placed */
   ng.ng_TextAttr = &topaz11;                      /* this is required!! */
   ng.ng_VisualInfo = vi;                          /* this is required!! */


   rbase = 146;
   lbase = 0;
	//
	// CUSTOM TEXT INPUT
	//
   ng.ng_TopEdge = rbase;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 402;
   ng.ng_Height = 16;
   ng.ng_GadgetText = NULL;
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = CUSTOM_TEXT_DATA_ID;
   customgad = gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, MAX_CUSTOM_TEXT,
			GTST_String, customtext[current_custom],
                        TAG_DONE);

   /* prepare a List for the LISTVIEW Gadget below */
   NewList(&list);
   index = 0;
   while (custom[index])
   {
      node = (struct Node *)AllocRemember(&rmem, sizeof(struct Node),
                                          MEMF_CLEAR);
      if (!node)
         go_home("mem?");
      node->ln_Name = custom[index++];
      AddTail(&list, node);
   }

	//
	// CUSTOM TEXT OUTPUT
	//
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 220;
   ng.ng_Height = 112;
   ng.ng_TopEdge = rbase-ng.ng_Height;
   ng.ng_GadgetText = "Custom Text";
   ng.ng_GadgetID = CUSTOM_TEXT_ID;
   ng.ng_Flags = NG_HIGHLABEL | PLACETEXT_ABOVE;
   gad = CreateGadget(LISTVIEW_KIND, gad, &ng,
                        GTLV_Labels, &list,
                        GTLV_ShowSelected, NULL,
                        GTLV_Selected, 0,
                        //GTLV_ScrollWidth, 18,
                        TAG_DONE);

//   // Box around save/cancel gadgets
//   DrawBevelBox(window->RPort, 640-152, 370+top, 152, 20,
//		GT_VisualInfo, vi,
//		TAG_DONE);

	//
	// SAVE
	//
   ng.ng_LeftEdge = 640-152+10;
   ng.ng_TopEdge = 370+top;
   ng.ng_Width = 60;
   ng.ng_Height = 14;
   ng.ng_GadgetText = "Save";
   ng.ng_GadgetID = SAVE_ID;
   ng.ng_Flags = NULL;
   gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);

	//
	// CANCEL
	//
   ng.ng_LeftEdge = 640-152+82;
   ng.ng_TopEdge = 370+top;
   ng.ng_Width = 60;
   ng.ng_Height = 14;
   ng.ng_GadgetText = "Cancel";
   ng.ng_GadgetID = CANCEL_ID;
   ng.ng_Flags = NULL;
   gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);

//   /* here we'll create a SLIDER with an automatic value display */
//   ng.ng_LeftEdge = 300;
//   ng.ng_TopEdge = 16+120+top;
//   ng.ng_Width = 180;
//   ng.ng_Height = 12;
//   ng.ng_GadgetText = "DAY:  ";
//   ng.ng_GadgetID = SLIDER_ID;
//   ng.ng_Flags = PLACETEXT_LEFT;
//   gad = CreateGadget(SLIDER_KIND, gad, &ng,
//                        GTSL_Min, SLIDER_MIN,
//                        GTSL_Max, SLIDER_MAX,
//                        GTSL_Level, 1,
//                        GTSL_LevelFormat, "%2ld",  /* don't forget the 'l'*/
//                        GTSL_MaxLevelLen, 2,
//                        GA_RELVERIFY, TRUE,
//                        TAG_DONE);

//   /* a CHECKBOX */
//   ng.ng_LeftEdge = window->Width - 40;
//   ng.ng_TopEdge = 10+top;
//   ng.ng_GadgetID = CHECK_ID;
//   ng.ng_GadgetText = "Check Me:";
//   ng.ng_Flags = PLACETEXT_LEFT;
//   gad = CreateGadget(CHECKBOX_KIND, gad, &ng, TAG_DONE);


	//
	// READ ACCESS
	//
   rbase = 4+top;
   lbase = 354;

   ng.ng_TopEdge = rbase;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 44;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "File read";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL;
   ng.ng_GadgetID = READ_ACCESS_ID;
   gad = CreateGadget(INTEGER_KIND, gad, &ng,
                        GTIN_Number, config.readacc,
                        GTIN_MaxChars, 3,
                        TAG_DONE);


	//
	// WRITE ACCESS
	//
   ng.ng_TopEdge = rbase+16*1;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 44;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "File write";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL;
   ng.ng_GadgetID = WRITE_ACCESS_ID;
   gad = CreateGadget(INTEGER_KIND, gad, &ng,
                        GTIN_Number, config.writeacc,
                        GTIN_MaxChars, 3,
                        TAG_DONE);



	//
	// TABSTOP
	//
   ng.ng_TopEdge = rbase+16*2;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 44;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "Tab width";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = TABSTOP_ID;
   gad = CreateGadget(INTEGER_KIND, gad, &ng,
			GTIN_Number, config.tabs,
                        GTIN_MaxChars, 2,
                        TAG_DONE);


   // FORTUNE COOKIE AREA

   rbase = 188;
   lbase = 100;
   width = 188;
   DrawBevelBox(window->RPort, lbase-100, rbase-8, width+100+4, 16*5,
		GT_VisualInfo, vi,
		GTBB_Recessed, 1,
		TAG_DONE);

   SetAPen(window->RPort, PEN_BLACK);
   Move(window->RPort, lbase-100+(width+100+4 - 15*8)/2, rbase+5);
   Text(window->RPort, "Fortune Cookies", 15);
	     
	//
	// Fortune Command
	//
   ng.ng_TopEdge = rbase+16;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = width;
   ng.ng_Height = 16;
   //ng.ng_GadgetText = "Command  ";
   ng.ng_GadgetText = "";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = FORTUNE_ID;
   fgad_command = gad = CreateGadget(STRING_KIND, gad, &ng,
				     GTST_MaxChars, 120,
				     GTST_String, config.fortune[0].command,
				     TAG_DONE);
	//
	// Fortune Description
	//
   ng.ng_TopEdge = rbase+32;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = width;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "Description";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = FORTDESC_ID;
   fgad_desc = gad = CreateGadget(STRING_KIND, gad, &ng,
				  GTST_MaxChars, 60,
				  GTST_String, config.fortune[0].desc,
				  TAG_DONE);
	//
	// FORTUNE ADD
	//
   ng.ng_TopEdge = rbase+54;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 32;
   ng.ng_Height = 14;
   ng.ng_GadgetText = "New";
   ng.ng_GadgetID = FORTADD_ID;
   ng.ng_Flags = NULL;
   fgad_add = gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);
	//
	// FORTUNE DELETE
	//
   ng.ng_LeftEdge = ng.ng_LeftEdge+ng.ng_Width+4;
   ng.ng_Width = 56;
   ng.ng_GadgetText = "Delete";
   ng.ng_GadgetID = FORTDEL_ID;
   ng.ng_Flags = NULL;
   fgad_del = gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);
	//
	// FORTUNE NEXT
	//
   ng.ng_LeftEdge = ng.ng_LeftEdge+ng.ng_Width+4 + 8;
   ng.ng_Width = 40;
   ng.ng_GadgetText = "Next";
   ng.ng_GadgetID = FORTNEXT_ID;
   ng.ng_Flags = NULL;
   fgad_next = gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);

	//
	// File Requester Directory
	//
   rbase = 288;
   lbase = 154;

   ng.ng_TopEdge = rbase;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 108-10+10*8-40;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "File Requester dir";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = FILEREQ_DIR_ID;
   gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, 80,
			GTST_String, config.filereq_dir,
                        TAG_DONE);

	//
	// Ports for Who's on-line listing
	//
   ng.ng_TopEdge = rbase+16*1;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 108-10+10*8-40;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "Broadcast ports";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = WHOPORTS_ID;
   gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, 80,
			GTST_String, config.whoports,
                        TAG_DONE);

   rbase = 76;
   lbase = 304;
	//
	// QUOTE STYLE
	//
   ng.ng_TopEdge = rbase;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = 96;
   ng.ng_Height = 14;
   ng.ng_GadgetText = "Quote";
   ng.ng_GadgetID = QUOTE_CYCLE_ID;
   ng.ng_Flags = NG_HIGHLABEL | PLACETEXT_LEFT;
   gad = CreateGadget(CYCLE_KIND, gad, &ng,
                        GTCY_Labels, quotestyle,
			GTCY_Active, config.qstyle,
                        TAG_DONE);

	//
	// KOOL WINDOW SEPARATOR
	//
   ng.ng_TopEdge = rbase+18;
   ng.ng_LeftEdge = lbase-10+48;
   ng.ng_Width = 96+10-48;
   ng.ng_Height = 14;
   ng.ng_GadgetText = "Border";
   ng.ng_GadgetID = KOOL_ID;
   ng.ng_Flags = NULL;
   gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);


   // SPELLING CHECKER AREA
   rbase = 188;
   lbase = 408;
   width = 228;
   DrawBevelBox(window->RPort, lbase-100, rbase-8, width+100+4, 16*5,
		GT_VisualInfo, vi,
		GTBB_Recessed, 1,
		TAG_DONE);
	//
	// Spell Start
	//
   ng.ng_TopEdge = rbase;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = width;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "Spell run";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = SPELL_START_ID;
   gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, 120,
			GTST_String, config.sp_start,
                        TAG_DONE);
	//
	// Spell AREXX Port
	//
   ng.ng_TopEdge = rbase+16*1;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = width;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "AREXX port";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = SPELL_AREXX_PORT_ID;
   gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, 20,
			GTST_String, config.sp_arexx_port,
                        TAG_DONE);
	//
	// Spell Check Command
	//
   ng.ng_TopEdge = rbase+16*2;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = width;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "Spell check";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = SPELL_CHECK_ID;
   gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, 20,
			GTST_String, config.sp_check,
                        TAG_DONE);
	//
	// Spell Exit
	//
   ng.ng_TopEdge = rbase+16*3;
   ng.ng_LeftEdge = lbase;
   ng.ng_Width = width;
   ng.ng_Height = 16;
   ng.ng_GadgetText = "Spell exit";
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = SPELL_EXIT_ID;
   gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, 20,
			GTST_String, config.sp_exit,
                        TAG_DONE);

   
        //
	// SPECIAL
	//
   ng.ng_TopEdge = 296;
   ng.ng_LeftEdge = 464;
   ng.ng_Width = 176;
   ng.ng_Height = 16;
   ng.ng_GadgetText = NULL;
   ng.ng_Flags = PLACETEXT_LEFT | NG_HIGHLABEL; 
   ng.ng_GadgetID = SPECIAL_INPUT_ID;
   spgad = gad = CreateGadget(STRING_KIND, gad, &ng,
                        GTST_MaxChars, MAX_CUSTOM_TEXT,
			GTST_String, magic[current_magic],
                        TAG_DONE);
   NewList(&splist);
   for (index = 0; index < NUM_MAGIC; ++index) {

      node = (struct Node *)AllocRemember(&rmem, sizeof(struct Node),
                                          MEMF_CLEAR);
      if (!node)
         go_home("mem?");
      node->ln_Name = magic[index];
      AddTail(&splist, node);
   }
   ng.ng_LeftEdge = 464;
   ng.ng_Width = 176;
   ng.ng_TopEdge = 296;
   ng.ng_Height = 48;
   ng.ng_GadgetText = "Special Options";
   ng.ng_GadgetID = SPECIAL_ID;
   ng.ng_Flags = NG_HIGHLABEL | PLACETEXT_ABOVE;
   splistgad = gad = CreateGadget(LISTVIEW_KIND, gad, &ng,
                        GTLV_Labels, &splist,
                        GTLV_ShowSelected, spgad,
                        GTLV_Selected, 0,
                        //GTLV_ScrollWidth, 18,
                        TAG_DONE);

//   /* and make a button to play with the integer Gadget with */
//   ng.ng_LeftEdge = window->Width - 110;
//   ng.ng_TopEdge = 16+50+top;
//   ng.ng_Width = 100;
//   ng.ng_Height = 14;
//   ng.ng_GadgetText = "INCREMENT";
//   ng.ng_GadgetID = INCREMENT_ID;
//   ng.ng_Flags = NULL;
//   gad = CreateGadget(BUTTON_KIND, gad, &ng, TAG_DONE);


	//
	// COLOR SELECTION
	//
   cbase = 4+top;
   ng.ng_LeftEdge = 415;
   ng.ng_TopEdge = cbase;
   ng.ng_Width = 224;
   ng.ng_Height = 14;
   ng.ng_GadgetText = "";
   ng.ng_GadgetID = COLOR_CYCLE_ID;
   ng.ng_Flags = NG_HIGHLABEL | PLACETEXT_LEFT;
   gad = CreateGadget(CYCLE_KIND, gad, &ng,
                        GTCY_Labels, colortype,
                        TAG_DONE);

//   /* a set of mutually-exclusive Gadgets which performs the same function
//      as the CYCLE Gadget above -- if you have 5 or less items to select
//      from, the smaller CYCLE Gadget would be a better choice -- the user
//      doesn't want to have to click through too many choices -- if you
//      have room go with a MX Gadget -- it's more visual as to the choices */
//   ng.ng_LeftEdge = 260;
//   ng.ng_TopEdge = 25+top;
//   ng.ng_GadgetID = MX_ID;
//   ng.ng_Flags = PLACETEXT_LEFT;
//   gad = CreateGadget(MX_KIND, gad, &ng,
//                        GTMX_Labels, color_labels,
//                        GTMX_Active, 5,
//                        GTMX_Spacing, 3,
//                        TAG_DONE);


	//
	// COLOR PALETTE
	//
   ng.ng_LeftEdge = 415;
   ng.ng_TopEdge = cbase+17;
   ng.ng_Width = 230;
   ng.ng_Height = 28;
   ng.ng_GadgetText = "";
   ng.ng_GadgetID = COLOR_BAR_ID;
   ng.ng_Flags = PLACETEXT_ABOVE | NG_HIGHLABEL;
   colorgad = gad = CreateGadget(PALETTE_KIND, gad, &ng,
                        GTPA_Depth, screen->BitMap.Depth,
                        GTPA_Color, config.current_color[color_selection],
                        GTPA_IndicatorWidth, 40,
                        TAG_DONE);

   /* now we're ready to add the Gadget list. Quit if the final Gadget
      pointer is NULL - this means that one of the Gadget allocations failed.
      If it exists, add it to the window and refresh, then add the new
      GT_RefreshWindow() call. */

   if (!gad)
      go_home("mem?");

   AddGList(window, glist, (UWORD)-1, (UWORD)-1, NULL);
   RefreshGList(glist, window, NULL, (UWORD)-1);
   GT_RefreshWindow(window, NULL);

#define BOX_LEFT 415
#define BOX_TOP 52+window->BorderTop+1
#define BOX_WIDTH 224
#define BOX_HEIGHT 101

   DrawBevelBox(window->RPort, BOX_LEFT, BOX_TOP, BOX_WIDTH, BOX_HEIGHT,
		GTBB_Recessed, 1,
		GT_VisualInfo, vi,
		TAG_DONE);

   SetAPen(window->RPort, PEN_BLACK); /* black */
   RectFill(window->RPort, BOX_LEFT+4, BOX_TOP+2, BOX_LEFT+4+27*8, BOX_TOP+2+12*8);

   show_emacs();

   do_fortgads();
}

void show_emacs(void)
{
	short left = BOX_LEFT+4;
	short top = BOX_TOP+8;
	struct RastPort *r = window->RPort;
	unsigned char kool_text[27];
	char *c;
	unsigned char temp;
	
	SetFont(r, amiga8);
	
	temp = kool_array[config.kool];
	if (temp == SINGLE_LINE || temp == DOUBLE_LINE) temp = ' ';
	memset(kool_text, temp, 27);

	if (config.kool == 0) {
		c = cname[config.current_color[4]];
		if (strcmp(c, "WHITE") == 0 || strcmp(c, "GRAY") == 0) {
			SetAPen(r, colorval("LBLUE"));
		} else {
			SetAPen(r, colorval("WHITE"));
		}
		SetBPen(r, config.current_color[4]);
	} else {
		SetAPen(r, config.current_color[4]);
		SetBPen(r, PEN_BLACK);
		Move(r, left, top+2*8);
		Text(r, kool_text, 27);
		Move(r, left, top+6*8);
		Text(r, kool_text, 27);
		Move(r, left, top+10*8);
		Text(r, kool_text, 27);
	}
	if (kool_array[config.kool] == SINGLE_LINE) {
		Move(r, left, top+2*8-3);
		Draw(r, left+8*27-1, top+2*8-3);
		Move(r, left, top+6*8-3);
		Draw(r, left+8*27-1, top+6*8-3);
		Move(r, left, top+10*8-3);
		Draw(r, left+8*27-1, top+10*8-3);
	} else if (kool_array[config.kool] == DOUBLE_LINE) {
		Move(r, left, top+2*8-2);
		Draw(r, left+8*27-1, top+2*8-2);
		Move(r, left, top+6*8-2);
		Draw(r, left+8*27-1, top+6*8-2);
		Move(r, left, top+10*8-2);
		Draw(r, left+8*27-1, top+10*8-2);

		Move(r, left, top+2*8-4);
		Draw(r, left+8*27-1, top+2*8-4);
		Move(r, left, top+6*8-4);
		Draw(r, left+8*27-1, top+6*8-4);
		Move(r, left, top+10*8-4);
		Draw(r, left+8*27-1, top+10*8-4);
	}

	if (config.kool) {
		Move(r, left+8, top+2*8);
	} else {
		Move(r, left, top+2*8);
		Text(r, kool_text, 1);
	}
//	Text(r, "Press ESC ? for help", 20);
	Text(r, dflt_customtext[IDX_PRESS_ESC], 20);
	if (!config.kool) {
		Text(r, kool_text, 6);
	}

	if (config.kool) {
		Move(r, left+8, top+6*8);
	} else {
		Move(r, left, top+6*8);
		Text(r, kool_text, 1);
	}
	Text(r, EMACSXL_VERSION, sizeof(EMACSXL_VERSION)-1);
	if (!config.kool) {
		Text(r, kool_text, 27-sizeof(EMACSXL_VERSION));
	}

	if (config.kool) {
		Move(r, left+8, top+10*8);
	} else {
		Move(r, left, top+10*8);
		Text(r, kool_text, 1);
	}
//	Text(r, "Press RETURN to quote, ESC", 26);
	Text(r, dflt_customtext[IDX_QUOTE_LINE], 26);
	SetBPen(r, PEN_BLACK);

	Move(r, left+2*8, top);
	SetAPen(r, config.current_color[3]);
	Text(r, "To: The Cyborg", 14);
	Move(r, left, top+1*8);
	Text(r, "Subj: Al's Casino", 17);

	Move(r, left, top+3*8);
	SetAPen(r, config.current_color[0]);
	Text(r, "Hey dude,", 9);
	Move(r, left+3*8, top+4*8);
	Text(r, "Yes, the best blackjack", 23);

	SetAPen(r, config.current_color[2]);
	Move(r, left, top+5*8);
	Text(r, "Help screen is this color", 25);

	Move(r, left, top+7*8);
	SetAPen(r, config.current_color[1]);
	Text(r, "Check out what The Cyborg s", 27);
	Move(r, left+8, top+8*8);
	Text(r, "TC> Try Al's Blackjack Cas", 26);
	Move(r, left+8, top+9*8);
	Text(r, "TC> better than Hack & Sla", 26);
	SetAPen(r, config.current_color[5]);
	Move(r, left, top+11*8);
//	Text(r, "Saving...", 9);
	Text(r, dflt_customtext[IDX_ANY_KEY], strlen(dflt_customtext[IDX_ANY_KEY]));

	SetBPen(r, PEN_LIGHTGREY);
	SetFont(r, amiga11);
}

static void handle_input(void)
{
   struct IntuiMessage *message;
   struct Gadget *gadget;
   ULONG class;
   UWORD code;

   while (1)
   {
      WaitPort(window->UserPort);

      /* use the new GadTools GT_GetIMsg() function to get input events */
      while (message = GT_GetIMsg(window->UserPort))
      {
         class = message->Class;
         code = message->Code;

         /* we'll assume it's a Gadget, but no harm is done if it isn't */
         gadget = (struct Gadget *)message->IAddress;

         /* use the GT_ReplyIMsg() function to reply to the message */
         GT_ReplyIMsg(message);

         switch (class)
         {
            case CLOSEWINDOW:
               go_home(NULL);
            case GADGETUP:
               handle_gadgetup(gadget, code);
               break;
            case GADGETDOWN:
               handle_gadgetdown(gadget, code);
               break;
            case REFRESHWINDOW:
               /* when using a window of type SIMPLE_REFRESH, use this
                  input event and GadTools-compatible refreshing code. */
               GT_BeginRefresh(window);
               GT_EndRefresh(window, TRUE);
	       show_emacs();
               break;
         }
      }
   }
}



static void handle_gadgetdown(gadget, code)
struct Gadget *gadget;
UWORD code;
{
//   switch (gadget->GadgetID)
//   {
//      case MX_ID:
//         /* seems this requires a GADGETDOWN event */
//         printf("MX (MUTUAL EXCLUDE) GADGET: item=%s\n", color_labels[code]);
//         break;
//   }
}


static void handle_gadgetup(gadget, code)
struct Gadget *gadget;
UWORD code;
{
    int i;
   //char *p;
   //char *q;

   switch (gadget->GadgetID)
   {
      case SAVE_ID:
	 save_game();
         break;
      case CANCEL_ID:
         go_home(NULL);
         break;
      case QUOTE_CYCLE_ID:
	 config.qstyle = code;
	 break;
#if 0
      case LOGFILE_ID:
	 config.xlog = code;
	 break;
#endif
      case KOOL_ID:
	 config.kool++;
         if (config.kool >= sizeof(kool_array)) config.kool = 0;
	 show_emacs();
	 break;
      case SPECIAL_ID:
	 current_magic = code;
	 break;
      case SPECIAL_INPUT_ID:
	 strcpy(magic[current_magic],
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
	 GT_SetGadgetAttrs(splistgad, window, NULL,
			   TAG_DONE);
	 break;
      case COLOR_CYCLE_ID:
	 color_selection = code;
	 GT_SetGadgetAttrs(colorgad, window, NULL,
			   GTPA_Color, config.current_color[color_selection],
			   TAG_DONE);
         break;
      case FILEREQ_DIR_ID:
#if 0
	 OLD PRIVATE STUFF
	 p = ((struct StringInfo *)gadget->SpecialInfo)->Buffer;
	 q = config.private;
	 while (*p) {
		if (*p == ',' || (*p >= '0' && *p <= '9')) *q++ = *p;
		p++;
	 }
	 *q = 0;
#endif
	 strcpy(config.filereq_dir,
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
	 GT_SetGadgetAttrs(gadget, window, NULL,
			   GTST_String, config.filereq_dir,
			   TAG_DONE);
         break;
   case WHOPORTS_ID:
	 strcpy(config.whoports,
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
	 GT_SetGadgetAttrs(gadget, window, NULL,
			   GTST_String, config.whoports,
			   TAG_DONE);
         break;
   case FORTUNE_ID:
       strcpy(config.fortune[curfort].command,
	      ((struct StringInfo *)gadget->SpecialInfo)->Buffer);
       GT_SetGadgetAttrs(fgad_command, window, NULL,
			 GTST_String, config.fortune[curfort].command,
			 TAG_DONE);
       break;
   case FORTDESC_ID:
       strcpy(config.fortune[curfort].desc,
	      ((struct StringInfo *)gadget->SpecialInfo)->Buffer);
       GT_SetGadgetAttrs(fgad_desc, window, NULL,
			 GTST_String, config.fortune[curfort].desc,
			 TAG_DONE);
       break;
   case FORTADD_ID:
       if (config.num_fort < NUM_COOKIE_TOPICS) {
	   ++config.num_fort;
	   if (curfort < config.num_fort-1) {
	       ++curfort;
	   }
	   for (i=config.num_fort-1; i>curfort; --i) {
	       strcpy(config.fortune[i].command, config.fortune[i-1].command);
	       strcpy(config.fortune[i].desc, config.fortune[i-1].desc);
	       
	   }
	   config.fortune[curfort].command[0] = 0;
	   config.fortune[curfort].desc[0] = 0;
	   do_fortgads();
       }
       break;
   case FORTDEL_ID:
       if (config.num_fort > 0) {
	   for (i=curfort; i<config.num_fort-1; ++i) {
	       strcpy(config.fortune[i].command, config.fortune[i+1].command);
	       strcpy(config.fortune[i].desc, config.fortune[i+1].desc);
	       
	   }
	   --config.num_fort;
	   if (curfort >= config.num_fort && curfort > 0) {
	       --curfort;
	   }
	   if (config.num_fort == 0) {
	       config.fortune[0].command[0] = 0;
	       config.fortune[0].desc[0] = 0;
	   }
	   do_fortgads();
       }
       break;
   case FORTNEXT_ID:
       if (++curfort >= config.num_fort) {
	   curfort = 0;
       }
       do_fortgads();
       break;

#if 0
     case FORTUNE_AREAS_ID:
       // do minimal syntax-check
       p = ((struct StringInfo *)gadget->SpecialInfo)->Buffer;
       q = config.fort_areas;
       while (*p) {
	   if ((*p >= '0' && *p <= '9') || *p == '-' || *p == ',') {
	       *q++ = *p++;
	   } else {
	       p++;
	   }
       }
       *q = 0;
       
	 GT_SetGadgetAttrs(gadget, window, NULL,
			   GTST_String, config.fort_areas,
			   TAG_DONE);
         break;
#endif
     case SPELL_AREXX_PORT_ID:
	 strcpy(config.sp_arexx_port,
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
	 GT_SetGadgetAttrs(gadget, window, NULL,
			   GTST_String, config.sp_arexx_port,
			   TAG_DONE);
         break;
     case SPELL_CHECK_ID:
	 strcpy(config.sp_check,
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
	 GT_SetGadgetAttrs(gadget, window, NULL,
			   GTST_String, config.sp_check,
			   TAG_DONE);
         break;
     case SPELL_START_ID:
	 strcpy(config.sp_start,
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
	 GT_SetGadgetAttrs(gadget, window, NULL,
			   GTST_String, config.sp_start,
			   TAG_DONE);
         break;
     case SPELL_EXIT_ID:
	 strcpy(config.sp_exit,
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
	 GT_SetGadgetAttrs(gadget, window, NULL,
			   GTST_String, config.sp_exit,
			   TAG_DONE);
         break;
      case CUSTOM_TEXT_ID:
	 current_custom = code;
	 GT_SetGadgetAttrs(customgad, window, NULL,
			   GTST_String, customtext[current_custom],
			   TAG_DONE);
         break;
      case CUSTOM_TEXT_DATA_ID:
	 strcpy(customtext[current_custom],
		((struct StringInfo *)gadget->SpecialInfo)->Buffer);
         break;
//      case SLIDER_ID:
//         printf("SLIDER GADGET: %d\n", code);
//         break;
//      case CHECK_ID:
//         printf("CHECKBOX GADGET: state=");
//         if (gadget->Flags & SELECTED)
//            printf("ON\n");
//         else
//            printf("OFF\n");
//         break;
#if 0
      case FILL_COLUMN_ID:
	 config.fillcol = ((struct StringInfo *)gadget->SpecialInfo)->LongInt;
	 if (config.fillcol < 40) config.fillcol = 40;
	 if (config.fillcol > 80) config.fillcol = 80;
	 GT_SetGadgetAttrs(gadget, window, NULL, GTIN_Number, config.fillcol, TAG_DONE);
         break;
#endif
#if 0
      case PAGE_LENGTH_ID:
	 config.pagelen = ((struct StringInfo *)gadget->SpecialInfo)->LongInt;
	 if (config.pagelen < 20) config.pagelen = 20;
	 GT_SetGadgetAttrs(gadget, window, NULL, GTIN_Number, config.pagelen, TAG_DONE);
         break;
#endif
      case TABSTOP_ID:
	 config.tabs = ((struct StringInfo *)gadget->SpecialInfo)->LongInt;
	 if (config.tabs < 1) config.tabs = 1;
	 if (config.tabs > 40) config.tabs = 40;
	 GT_SetGadgetAttrs(gadget, window, NULL, GTIN_Number, config.tabs, TAG_DONE);
         break;
      case READ_ACCESS_ID:
	 config.readacc = ((struct StringInfo *)gadget->SpecialInfo)->LongInt;
	 // ubyte wraps around
	 GT_SetGadgetAttrs(gadget, window, NULL, GTIN_Number, config.readacc, TAG_DONE);
         break;
      case WRITE_ACCESS_ID:
	 config.writeacc = ((struct StringInfo *)gadget->SpecialInfo)->LongInt;
	 // ubyte wraps around
	 GT_SetGadgetAttrs(gadget, window, NULL, GTIN_Number, config.writeacc, TAG_DONE);
         break;
#if 0
      case FORTUNE_ACCESS_ID:
	 config.fortune_access = ((struct StringInfo *)gadget->SpecialInfo)->LongInt;
	 // ubyte wraps around
	 GT_SetGadgetAttrs(gadget, window, NULL, GTIN_Number, config.fortune_access, TAG_DONE);
         break;
#endif
//      case INCREMENT_ID:
//         /* here we use a Tag-based Gadget attributes modifying function --
//            just pass the appropriate Tags you want modified */
//         GT_SetGadgetAttrs(intgad, window, NULL,
//                           GTIN_Number, ++value,
//                           TAG_DONE);
//
//         printf("INCREMENT INTEGER: value=%ld\n",
//               ((struct StringInfo *)intgad->SpecialInfo)->LongInt);
//         break;
      case COLOR_BAR_ID:
	 config.current_color[color_selection] = code;
	 show_emacs();
         break;
   }
}

void parse_in(char *to, char *from)
{
	while (*from) {
		// convert ~" to "
		if (*from == '~' && *(from+1) == '"') {
			from++;
		}
		*to++ = *from++;
	}
	*to = 0;
}

void parse_out(char *to, char *from)
{
	while (*from) {
		if (*from == '"') {
			*to++ = '~';
		}
		*to++ = *from++;
	}
	*to = 0;
}

static BPTR fh;

void nset(char *name, int val)
{
	char buf[50];
	strcpy(buf, "set ");
	strcat(buf, name);
	strcat(buf, " ");
	stci_d(buf+strlen(buf), val);
	FPuts(fh, buf);
	FPutC(fh, '\n');
}

void sset(char *name, char *val, char *dflt)
{
	char buf[300];
	char *p;

	// do not save if unchanged from default
	if (strcmp(val, dflt) != 0) {
	    strcpy(buf, "set ");
	    strcat(buf, name);
	    strcat(buf, " \"");
	    p = buf + strlen(buf);
	    parse_out(p, val);
	    strcat(buf, "\"");
	    FPuts(fh, buf);
	    FPutC(fh, '\n');
	}
}

static void write_fort(void)
{
    int i;
    for (i=0; i<config.num_fort; ++i) {
	FPuts(fh, ";=");
	FPuts(fh, config.fortune[i].command);
	FPuts(fh, "\n;!");
	FPuts(fh, config.fortune[i].desc);
	FPutC(fh, '\n');
    }
}

static void save_game(void)
{
	int i;
	int shazam = 0;
	
	DeleteFile(EmacsXL_OLD);
	Rename(EmacsXL_CFG, EmacsXL_OLD);

	fh = Open(EmacsXL_CFG, MODE_NEWFILE);
	if (fh == 0) {
		go_home("Can't write?");
	}

	write_fort();
	
	nset("$qstyle", config.qstyle);
	nset("$softtab", config.tabs);
	nset("$aread", config.readacc);
	nset("$awrite", config.writeacc);
	nset("$kool", config.kool);
	sset("$zdir", config.filereq_dir, "RAM:");
	sset("$zwho", config.whoports, "TL0TR0TR1");
	sset("$zsrexx", config.sp_arexx_port, "");
	sset("$zscheck", config.sp_check, "");
	sset("$zsrun", config.sp_start, "");
	sset("$zsexit", config.sp_exit, "");
	sset("$zmsgc", cname[config.current_color[0]], "");
	sset("$zqc", cname[config.current_color[1]], "");
	sset("$zhelpc", cname[config.current_color[2]], "");
	sset("$zhbc", cname[config.current_color[3]], "");
	sset("$zwbc", cname[config.current_color[4]], "");
	sset("$zmlc", cname[config.current_color[5]], "");
	for (i = 0; custom[i]; ++i) {
		sset(customvar[i], customtext[i], dflt_customtext[i]);
	}
	for (i = 0; i < NUM_MAGIC; ++i) {
		if (*magic[i]) {
			if (!shazam) {
				shazam = 1;
				FPuts(fh, SPECIAL_MAGIC);
				FPutC(fh, '\n');
			}
			FPuts(fh, magic[i]);
			FPutC(fh, '\n');
		}
	}
	Close(fh);
	go_home(NULL);
}

static void set_defaults(void)
{
	int i;
	strcpy(config.filereq_dir, "RAM:");
	config.qstyle = 0;
	// spell check defaults for ISPELL speller
	strcpy(config.sp_arexx_port, "IRexxSpell");
	strcpy(config.sp_check, "quickcheck");
	strcpy(config.sp_exit, "exit");
	strcpy(config.sp_start, "Run >nil: c:ISpell -r");
	strcpy(config.whoports, "TL0TR0TR1");
	config.tabs = 8;
	config.readacc = 255;
	config.writeacc = 255;
	config.fortune[0].command[0] = 0;
	config.fortune[0].desc[0] = 0;
	config.num_fort = 0;
	config.kool = 0;
	config.current_color[0] = colorval("CYAN");
	config.current_color[1] = colorval("RED");
	config.current_color[2] = colorval("YELLOW");
	config.current_color[3] = colorval("LGREEN");
	config.current_color[4] = colorval("BLUE");
	config.current_color[5] = colorval("WHITE");
	for (i = 0; custom[i]; ++i) {
		strcpy(customtext[i], dflt_customtext[i]);
	}
	for (i=0; i<NUM_MAGIC; ++i) {
		*magic[i] = 0;
	}
}

static int load_file()
{
    char buf[300];
    char *p;
    char *name;
    char *val;
    int i;
    int shazam = 0;

    fh = Open(EmacsXL_CFG, MODE_OLDFILE);
    if (fh == 0) {
	return 1;
    }
    while (FGets(fh, buf, 300) != NULL) {
	p = buf;
	while (*p == ' ' || *p == '\t') ++p;
	if (strncmp(p, SPECIAL_MAGIC, strlen(SPECIAL_MAGIC)) == 0) {
	    // all following lines are special
	    shazam = 1;
	}
	if (*p == ';' && *(p+1) == '=') {
	    p[strlen(p)-1] = 0;
	    strcpy(config.fortune[config.num_fort].command, p+2);
	}
	if (*p == ';' && *(p+1) == '!') {
	    p[strlen(p)-1] = 0;
	    strcpy(config.fortune[config.num_fort].desc, p+2);
	    config.num_fort++;
	}
	if (*p == '\n' || *p == ';' || *p == 0) continue;
	if (shazam && shazam <= NUM_MAGIC) {
	    name = p;
	    p = name + strlen(name) - 1;
	    while (*p == '\n' || *p == ' ' || *p == '\t') *p-- = 0;
	    strcpy(magic[shazam-1], name);
	    ++shazam;
	    continue;
	}
	if (strncmp(p, "set", 3) == 0) {
	    p += 3;
	    while (*p == ' ' || *p == '\t') ++p;
	    if (*p == '\n' || *p == 0) continue;
	    name = p;
	    while (*p && *p != ' ' && *p != '\t') ++p;
	    if (!*p) continue;
	    *p++ = 0;
	    while (*p == ' ' || *p == '\t') ++p;
	    if (!*p) continue;
	    val = p;
	    if (*val == '"') ++val;
	    p = val + strlen(val) - 1;
	    while (*p == '\n' || *p == ' ' || *p == '\t') *p-- = 0;
	    if (*p == '"') *p-- = 0;

if      (strcmp(name,"$qstyle") == 0)	  config.qstyle = myatoi(val);
//else if (strcmp(name,"$fillcol") == 0)	  config.fillcol = myatoi(val);
else if (strcmp(name,"$softtab") == 0)	  config.tabs = myatoi(val);
else if (strcmp(name,"$aread") == 0)	  config.readacc = myatoi(val);
else if (strcmp(name,"$awrite") == 0)	  config.writeacc = myatoi(val);
//else if (strcmp(name,"$zfacc") == 0)	  config.fortune_access = myatoi(val);
else if (strcmp(name,"$kool") == 0)	  config.kool = myatoi(val);
else if (strcmp(name,"$zdir") == 0)	  strcpy(config.filereq_dir, val);
else if (strcmp(name,"$zfortune") == 0)   {
    parse_in(config.fortune[0].command, val);
    config.num_fort = 1;  // backwards compatibility
}
//else if (strcmp(name,"$zfareas") == 0)  strcpy(config.fort_areas, val);
else if (strcmp(name,"$zsrexx") == 0)	  strcpy(config.sp_arexx_port, val);
else if (strcmp(name,"$zscheck") == 0)	  strcpy(config.sp_check, val);
else if (strcmp(name,"$zsrun") == 0)	  strcpy(config.sp_start, val);
else if (strcmp(name,"$zsexit") == 0)	  strcpy(config.sp_exit, val);
else if (strcmp(name,"$zwho") == 0)	  strcpy(config.whoports, val);
else if (strcmp(name,"$zmsgc") == 0)  config.current_color[0] = colorval(val);
else if (strcmp(name,"$zqc") == 0)    config.current_color[1] = colorval(val);
else if (strcmp(name,"$zhelpc") == 0) config.current_color[2] = colorval(val);
else if (strcmp(name,"$zhbc") == 0)   config.current_color[3] = colorval(val);
else if (strcmp(name,"$zwbc") == 0)   config.current_color[4] = colorval(val);
else if (strcmp(name,"$zmlc") == 0)   config.current_color[5] = colorval(val);
else {
		for (i = 0; custom[i]; ++i) {
			if (strcmp(name, customvar[i]) == 0) {
				parse_in(customtext[i], val);
			}
		}
	}

      }
}
    Close(fh);
    return 0;
}
