/* Side color support for the Mac interface to Xconq.
   Copyright (C) 1997, 1998 Hans Ronne.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

#include "conq.h"
#include "macconq.h"

#ifdef NEW_HEADERS
#include <ColorPicker.h>
#else
#include <Picker.h>
#endif

/* --- FUNCTIONS PROTOTYPES -------------------------------------------------*/

static int append_image_names(MenuHandle menu);
static int append_color_names(MenuHandle menu);
static int append_terrain_names(MenuHandle menu);
static int append_imf_name(Obj *form, MenuHandle menu);
static RGBColor get_color(char *name);					/* Get new color by name */
static RGBColor pick_new_color(RGBColor inColor, char *name); 	/* Use color picker to get a new color */

/* --- EXTERN DEFINED IN MACDRAW.C  -------------------------------------------*/

extern Image **best_terrain_images;

/* --- DEFAULTS FOR NEW MAP PREFERENCES ---------------------------------------*/

int default_sidecolors = FALSE;		/* Use side colors in maps & lists */
int default_iconmasks = FALSE;		/* Use masks for icons  */
int default_boxmasks = TRUE;		/* Use masks for grouping boxes */
int default_textmasks = FALSE;		/* Use masks for text */
int default_featureborders = TRUE;	/* Draw feature (province) borders */
int default_featurenames = TRUE;		/* Draw feature (province) names */
int default_simple_borders = TRUE;	/* Do not draw borders to water or to empty lands */
int default_shorelines = TRUE;		/* Draw shorelines in 3D relief */
int default_draw_ai = FALSE;		/* Draw AI regions (if present). */
int alert_256_colors = FALSE;		/* Suggest switch to 256 colors at start. */
int default_draw_scrollbars = FALSE;	/* Draw scrollbars in maps. */
int default_draw_cpanel = TRUE;		/* Draw control panel in maps. */
int default_draw_emblems = TRUE;	/* Draw unit emblems in maps and lists. */
int default_draw_topline = TRUE;		/* Draw top line boxes in maps */
int default_draw_topunit = TRUE;		/* Draw unit info boxes in maps */
int default_drawothermaps = TRUE;	/* Draw other maps as boxes */
int default_erase_names = TRUE;		/* Draw extra rows to erase name of moving unit */
int default_max_bufx = 600;		/* Horizontal scrolling buffers in offscreen graphics */
int default_max_bufy = 400;		/* Vertical scrolling buffers in offscreen graphics */
int default_optimize_fonts = TRUE;	/* Use optimized font sizes in map content */
int default_blinking_curunit = TRUE;	/* Use blinking current unit instead of animated box */
int default_selectionmasks = TRUE;	/* Use selection masks instead of selection boxes */
int default_power = 5;				/* Default power of new map */

/* --- VARIOUS PREFERENCES -----------------------------------------------------*/

int use_colornames = TRUE;			/* Save and read side colors as colorschemes */
int use_RGB_values = FALSE;		/* Save and read RGB colors in XCol resource */
int show_instructions = TRUE;		/* Show instructions window at start */
int fullsize_map = TRUE;			/* Show full size map window at start */
int unified_cicns = FALSE;			/* Use BitMap as mask for the PixMap */

/* --- COLOR SCHEME DEFAULTS ---------------------------------------------------*/

int default_main_icon_color = 1;		/* Default number (1-3) of sideColor used for icons */ 
int default_half_icon_color = 2;		/* Default number (1-3) of sideColor used for right half of icons */
int default_icon_mask_color = 3;		/* Default number (1-3) of sideColor used for icon masks */
int default_split_icon = TRUE;		/* Default use different colors for right and left halves of icons */
		
/* --- COLORS, COLOR SCHEMES AND COLOR NAMES --------------------------------------*/

RGBColor blackcolor = {0, 0, 0};
RGBColor dkgraycolor = {24000, 24000, 24000};
RGBColor graycolor = {32768, 32768, 32768};
RGBColor ltgraycolor = {49000, 49000, 49000};
RGBColor whitecolor = {0xFFFF, 0xFFFF, 0xFFFF};

RGBColor *featColor;				/* Feature colors by RGB value */
RGBColor *sideColor;				/* sideColor[0-15][1-3] are defined by default color schemes */
							/* sideColor[][0] is not defined, and available for future use */

char **current_colorscheme;		/* Current color schemes by names (red,pink,blue) */
							/* Unlike side->colorscheme defined also for null sides */	

char **default_featColorName;		/* Default feature colors by name (black etc) */
char **default_colorscheme;		/* Default color schemes (red,pink,brown) etc */

char **featColorItemName;			/* Name of item for which featColor is set (Shorelines etc) */
char **featColorName;			/* Name of feature color values (red, green etc) */
char **sideColorName;			/* Name of side color values (red, green etc) */

int *main_icon_color;				/* Number (1-3) of sideColor used for icons */ 
int *half_icon_color;				/* Number (1-3) of sideColor used for right half of icons */
int *icon_mask_color;				/* Number (1-3) of sideColor used for icon masks */
int *split_icon;					/* Use two colors for left and right halves of icons if TRUE */

/* --- FUNCTIONS FOR BUILDING AND HANDLING COLORS AND COLOR SCHEMES ---------------------*/

void 
set_default_colors(void)			/* Default colors used in absence of saved preferences */
{
	int i;

	featColor = (RGBColor *) xmalloc((FEATURES+1) * sizeof(RGBColor));
	featColorItemName = (char **) xmalloc((FEATURES+1) * sizeof(char *));
	featColorName = (char **) xmalloc((FEATURES+1) * sizeof(char *));
	default_featColorName = (char **) xmalloc((FEATURES+1) * sizeof(char *));
	sideColor = (RGBColor *) xmalloc((MAXSIDES+1) * 4 * sizeof(RGBColor));
	sideColorName = (char **) xmalloc((MAXSIDES+1) * 4 * sizeof(char *));
	current_colorscheme = (char **) xmalloc((MAXSIDES+1) * sizeof(char *)); 
	default_colorscheme = (char **) xmalloc((MAXSIDES+1) * sizeof(char *));
	main_icon_color = (int *) xmalloc((MAXSIDES+1) * sizeof(int));
	half_icon_color = (int *) xmalloc((MAXSIDES+1) * sizeof(int));
	icon_mask_color = (int *) xmalloc((MAXSIDES+1) * sizeof(int));
	split_icon = (int *) xmalloc((MAXSIDES+1) * sizeof(int));

	default_colorscheme[0] = "black,black,white";
	default_colorscheme[1] = "blue,blue,pale-turquoise";
	default_colorscheme[2] = "red,red,yellow";
	default_colorscheme[3] = "deep-pink,deep-pink,spring-green";
	default_colorscheme[4] = "straw,straw,saddle-brown";
	default_colorscheme[5] = "red,blue,cyan";
	default_colorscheme[6] = "white,violet,black";
	default_colorscheme[7] = "cyan,cyan,blue";
	default_colorscheme[8] = "yellow,yellow,red";
	default_colorscheme[9] = "green,magenta,medium-blue";
	default_colorscheme[10] = "purple,purple,cyan";
	default_colorscheme[11] = "navy-blue,deep-pink,spring-green";
	default_colorscheme[12] = "lawn-green,lawn-green,deep-pink";
	default_colorscheme[13] = "light-pink,spring-green,midnight-blue";
	default_colorscheme[14] = "dark-green,purple,aquamarine";
	default_colorscheme[15] = "cyan,green-yellow,navy-blue";
	default_featColorName[0] = "dark-orange";		/* Window background outside map. */
	default_featColorName[1] = "black";			/* Fore color */	
	default_featColorName[2] = "pale-turquoise";	/* Mask color */
	default_featColorName[3] = "midnight-blue";	/* Text color */
	default_featColorName[4] = g_grid_color();
	default_featColorName[5] = g_unseen_color();
	default_featColorName[6] = g_shoreline_color();
	default_featColorName[7] = "deep-pink";		/* Feature text and borders */
	default_featColorName[8] = g_frontline_color();
	default_featColorName[9] = g_meridian_color();
	default_featColorName[10] = g_contour_color();
	featColorItemName[0] = "Background";
	featColorItemName[1] = "Fore color";	/* These names are used in the Colors menu */
	featColorItemName[2] = "Mask color";
	featColorItemName[3] = "Text color";
	featColorItemName[4] = "Grid color";
	featColorItemName[5] = "Unseen color";
	featColorItemName[6] = "Shorelines";
	featColorItemName[7] = "Features";
	featColorItemName[8] = "Frontlines";
	featColorItemName[9] = "Meridians";
	featColorItemName[10] = "Contours";
}

void
set_default_side_colors(int e)
{
	/* First reset current colors to default colors */
	side_n(e)->colorscheme = default_colorscheme[e];
	/* Change the current colors */
	set_side_colors(e);
	/* Copy the result back to default colors */
	default_colorscheme[e] = side_n(e)->colorscheme;
	force_overall_update();
	save_preferences();
}

void
set_default_feature_color(int e)
{
	/* First reset current colors to default colors */
	featColorName[e] = default_featColorName[e];
	/* Change the current colors */
	set_feature_color(e);
	/* Copy the result back to default colors */
	default_featColorName[e] = featColorName[e];
	force_overall_update();
	save_preferences();
}

/* Load side colors and feature colors from defaults */

void
init_side_colors(void)
{
	int i;
	char cstring[96];

	for (i = 0; i <= MAXSIDES; i++) {

		/* Use of XCol colors overrides any colorschemes */
		if (!use_RGB_values) {

			/* Set side color names initially to "undefined" */
			set_sideColorName(i, 1, "undefined");
			set_sideColorName(i, 2, "undefined");
			set_sideColorName(i, 3, "undefined");
			/* Use schemes defined in game file if present or else default schemes */
			if (side_n(i) && side_n(i)->colorscheme) {
				current_colorscheme[i] = copy_string(side_n(i)->colorscheme);
			} else {
				current_colorscheme[i] = copy_string(default_colorscheme[i]);
			}
			net_set_side_colorscheme(dside, side_n(i), current_colorscheme[i]);
			dissect_colorscheme(i);
		}

		/* Load color use parameters from their defaults values */
		main_icon_color[i] 	= default_main_icon_color;
		half_icon_color[i] 	= default_half_icon_color;
		icon_mask_color[i] 	= default_icon_mask_color;
		split_icon[i]		= default_split_icon;

		/* Then zero color use parameters if the corresponding color is undefined */
		if (strcmp(get_sideColorName(i, main_icon_color[i]), "undefined") == 0) {
			main_icon_color[i] = 0;
		}
		if (strcmp(get_sideColorName(i, half_icon_color[i]), "undefined") == 0) {
			half_icon_color[i] = 0;
			split_icon[i] = 0;
		}
		if (strcmp(get_sideColorName(i, icon_mask_color[i]), "undefined") == 0) {
			icon_mask_color[i] = 0;
		}
	}
	for (i = 0; i <= FEATURES; i++) {
		/* Load feature colors from their defaults  */
		featColorName[i] = default_featColorName[i];
		featColor[i] = get_color(featColorName[i]);
	}
	/* This is now checked once and for all, instead of every time an area background is drawn. */
	if (strcmp(gridcolor_name, unseencolor_name) == 0
		|| g_terrain_seen() || (dside)->see_all)
			grid_matches_unseen = TRUE;
}

/* This function is based on init_emblem from the X11 interface. */
/* It also tests each name for validity and stores it in an array. */

void
dissect_colorscheme(int sidenum)
{
	char		*colorscheme = current_colorscheme[sidenum];
	char	 	*s, *c;
	char		cbuf[BUFSIZE];
	int 		i, button;
	Str255	pname;

	if (!empty_string(colorscheme)) {
		/* Take spec apart and iterate for multiple colors */
		for (s = colorscheme, c = cbuf, i = 1; i <= 3; ++s) {
			/* Test for end of name or end of scheme */
			if (*s == ',' || *s == '\0') {
				/* Terminate the name string */
				*c = '\0';
				c = cbuf;
				/* First check that the color name is valid. If not, substitute "undefined" */
				if (!valid_imf_name(cbuf)) {
					c2p(cbuf, pname);
					ParamText("\pSorry, Ò", pname, 
							 "\pÓ is not a valid color name. Substituting ÒundefinedÓ.", "\p");
					button = Alert(aOneButtonAlert, NULL);
					strcpy(cbuf, "undefined");
				}
				/* Also replace empty strings with "undefined" but do it silently */
				if (strcmp(cbuf, "") == 0)
				  strcpy(cbuf, "undefined");

				/* Get the color and store the color name in the name array */
				set_sideColorName(sidenum, i, copy_string(cbuf));
				set_sideColor(sidenum, i++, get_color(cbuf)); 

			/* Add one more character */
			} else *c++ = *s;
			/* Test for end of colorscheme */
			if (*s == '\0')
			  break;
		}
	}
}

/* Get a ppat color by its name. Function that call get_color should first check that the name is valid */ 

RGBColor
get_color(char* name)
{
	ImageFamily	*imf;
	Image 		*img;
	MacImage		*macimg;
	PixMapHandle 	pmhandle;
	CTabHandle 	ctabhandle;
	ColorSpec 		*ctdata;

	/* Return black if name is "undefined" */	
	if (strcmp(name, "undefined") == 0)
	     return blackcolor;

	/* Find (or create) the image family. */
	imf = get_generic_images(name);

	/* Needed only in the presence of mac-lib. */ 
    	record_imf_get(imf);			

	/* Get the 1x1 image (color). */
	img = get_img(imf, 1, 1);

	/* Needed only in the presence of mac-lib. */ 
	mac_interp_imf(imf, img, 0);	

	/* Get the resulting mac image. */
	macimg = (MacImage *) img->hook;

	/* Return black if it has no colrpat. */
	if (!macimg->colrpat) 
	      return blackcolor;

	/* Copy colrpat data into macimg->color. */
	pmhandle = (*macimg->colrpat)->patMap;
	ctabhandle = (*pmhandle)->pmTable;
	ctdata = (ColorSpec *) &((*ctabhandle)->ctTable);
	macimg->color.red   	= ctdata[0].rgb.red;
	macimg->color.green 	= ctdata[0].rgb.green;
	macimg->color.blue  	= ctdata[0].rgb.blue;
	macimg->colordefined 	= TRUE;
	
	/* Return the resulting color. */
	return macimg->color;
}	

/* Given an initial color and a name, allow the user to pick a color
   for that name. */

RGBColor  
pick_new_color(RGBColor inColor, char* name) 
{
	RGBColor outColor = inColor;
	Point where = {0};
	char prompt[64];
	Str255 pprompt;
	short button;

	/* Return if the color name is "undefined". */
	if (strcmp(name, "undefined") == 0)
	  return;

	/* Build the prompt. */
	strcpy(prompt, "Define Ò");
	strcat(prompt, name);
	strcat(prompt, "Ó:");
	c2p(prompt, pprompt);

	/* Open the color picker. */
	button = GetColor(where, pprompt, &inColor, &outColor);
	if (button == 1)
	  return outColor;
	else
	  return inColor;
}

/* --- FUNCTIONS FOR READING AND WRITING XCOL RGB DATA ------------------------------*/

/* (should generify all this) */

void
read_xcol(void)
{
	Handle XCol;	/* Special resource used to save RGB colors in prefs file */
	int i, j;		/* Index for loading colors from XCol resource */
	int n = 0;		/* XCol resource counter */

	/* First load the side colors */
	for (i = 0; i <= MAXSIDES; i++) {
		for (j = 1; j <= 3; j++) {

			/* Convert XCol resource data into RGB colors */
			XCol = GetResource('XCol', 128 + n);
			if (XCol != nil) {
			
				sideColor[4 * i + j].red	= *(*XCol + 1); 
				sideColor[4 * i + j].green	= *(*XCol + 3);
				sideColor[4 * i + j].blue	= *(*XCol + 5) ;
				
				sideColor[4 * i + j].red	&= 0x00FF;	/* Necessary to avoid color drift */
				sideColor[4 * i + j].green	&= 0x00FF;	/* Dont ask me why */
				sideColor[4 * i + j].blue	&= 0x00FF;

				sideColor[4 * i + j].red	+= (*(*XCol + 0) << 8);
				sideColor[4 * i + j].green	+= (*(*XCol + 2) << 8);
				sideColor[4 * i + j].blue	+= (*(*XCol + 4) << 8);
				
				n += 1;
			}
		}
	}			

	/* Then load the feature colors */
	for (i = 1; i <= FEATURES; i++) {

		/* Convert XCol resource data into RGB colors */
		XCol = GetResource('XCol', 128 + n);
		if (XCol != nil) {
		
			featColor[i].red   = *(*XCol + 1); 
			featColor[i].green = *(*XCol + 3);
			featColor[i].blue  = *(*XCol + 5) ;
			
			featColor[i].red   &= 0x00FF;	/* Necessary to avoid color drift */
			featColor[i].green &= 0x00FF;	/* Dont ask me why */
			featColor[i].blue  &= 0x00FF;

			featColor[i].red   += (*(*XCol + 0) << 8);
			featColor[i].green += (*(*XCol + 2) << 8);
			featColor[i].blue  += (*(*XCol + 4) << 8);

			n += 1;
		}
	}			
}

void
write_xcol(void)
{
	char cnumber[2], cstring[36];
	Str255 pnumber, pstring;			/* Used to write XCol names in pref file */
	Handle XCol, oldXCol;				/* Special resource used to store RGB colors */
	int i, j;							/* Index for loading XCols into sideColor[] */
	int n = 0;							/* Resource counter */

	/* First write the side colors */
	for (i = 0; i <= MAXSIDES; i++) {
		for (j = 1; j <= 3; j++) {

			/* Convert RGB colors into XCol resource data */
			oldXCol = GetResource('XCol', 128 + n);
			if (oldXCol != nil) RmveResource(oldXCol);
			XCol = NewHandle(6);

			*(*XCol + 1) = sideColor[4 * i + j].red;	
			*(*XCol + 3) = sideColor[4 * i + j].green;	
			*(*XCol + 5) = sideColor[4 * i + j].blue;

			*(*XCol + 0) = sideColor[4 * i + j].red	>> 8;
			*(*XCol + 2) = sideColor[4 * i + j].green	>> 8;
			*(*XCol + 4) = sideColor[4 * i + j].blue	>> 8;

			strcpy(cstring, "Side ");
			NumToString(i, pnumber);
			p2c(pnumber, cnumber);
			strcat(cstring, cnumber);
			strcat(cstring, " color ");
			NumToString(j, pnumber);
			p2c(pnumber, cnumber);
			strcat(cstring, cnumber);
			c2p(cstring, pstring);
			AddResource(XCol, 'XCol', 128 + n, pstring);
			
			n += 1;					
		}
	}

	/* Then write the feature colors */
	for (i = 1; i <= FEATURES; i++) {

		/* Convert RGB colors into XCol resource data */
		oldXCol = GetResource('XCol', 128 + n);
		if (oldXCol != nil) RmveResource(oldXCol);
		XCol = NewHandle(6);

		*(*XCol + 1) = featColor[i].red;	
		*(*XCol + 3) = featColor[i].green;	
		*(*XCol + 5) = featColor[i].blue;

		*(*XCol + 0) = featColor[i].red >> 8;
		*(*XCol + 2) = featColor[i].green >> 8;
		*(*XCol + 4) = featColor[i].blue >> 8;

		c2p(featColorItemName[i], pstring);
		AddResource(XCol, 'XCol', 128 + n, pstring);

		n += 1;					
	}
}

/* --- COLOR MANAGING DIALOGS --------------------------------------------------*/

/* The following three dialogs are used by the Colors menu and also by Preferences : Colorschemes */

/* This monster dialog does just about anything that has to do with side colors. A checkbox at the top enables or 
   disables the use of split icons (two different colors for the left and right half). Three popup menus makes it
   possible to chose three different colors from the available ppat colors in the Images file. The name of the new
   color can also be entered in a text field, followed by a Return. The editflag parameter is used to ensure that
   such a Return is not interpreted as a hit of the OK button. The actual RGB values associated with a given color 
   name can be changed by clicking the color field, where the color itself is displayed. A second row of popup
   menus makes it possible to chose how each color should be used. The half icon color alternative is disabled if 
   the split icon checkbox is unchecked, and the whole menu is disabled if the color is undefined. If a color use is
   picked which already was assigned to another color, that color is switched to "Not in use". At the bottom of the
   dialog box, five sample icons are plotted in real time to illustrate the given choice of colors. */

void
set_side_colors(int e)
{
	char cname[32], tmpName1[32], tmpName2[32], tmpName3[32], tmpScheme[96];
	short tmpUse[4], tmpSplit, editflag, ditem, mitem, button, found, done, i, n;  
	RGBColor tmpColor[4], tmpMain, tmpHalf, tmpMask, tmpBack;
	Handle itemhandle, ppathandle;  
	MenuHandle popMenu, useMenu;
	Rect srcrect, itemrect;
	CIconHandle cicnhandle;
	MacImage *macuimg;
	BitMap bm, mm;		
	GrafPtr oldport;
	Image *uimg;
	Str255 pname;
	DialogPtr win;

	/* Then check for color support */
	if (!hasColorQD) {
		ParamText("\pSorry, your mac does not support colors.", "\p", "\p", "\p");
		button = Alert(aOneButtonAlert, NULL);
		return;
	} 

	/* Get dialog window and popup menus */
	win = GetNewDialog(dSideColor, NULL, (DialogPtr) -1);
	popMenu = GetMenu(mSideColorPopup);
	useMenu = GetMenu(mSideColorUse);

	/* Save the Dialog background color if not white */
	tmpBack = ((CGrafPtr) win)->rgbBkColor;

	/* Load current side color names into temp names */
	strcpy(tmpName1, get_sideColorName(e, 1));
	strcpy(tmpName2, get_sideColorName(e, 2));
	strcpy(tmpName3, get_sideColorName(e, 3));
	
	/* Load current side colors into temp colors */
	tmpColor[1] = get_sideColor(e, 1);
	tmpColor[2] = get_sideColor(e, 2);
	tmpColor[3] = get_sideColor(e, 3);

	/* Also load side colors into tmpMain, tmpHalf & tmpMask */
	tmpMain = get_sideColor(e, main_icon_color[e]);
	tmpHalf = get_sideColor(e, half_icon_color[e]);
	tmpMask = get_sideColor(e, icon_mask_color[e]);

	/* Load color use info into tmpUse[i] */
	for (i = 1; i <= 3; i++) {
		if (main_icon_color[e] == i)
		  tmpUse[i] = miMainIconColor;
		else if (half_icon_color[e] == i)
		  tmpUse[i] = miHalfIconColor;
		else if (icon_mask_color[e] == i)
		  tmpUse[i] = miIconMaskColor;
		else
		  tmpUse[i] = miNotInUse;
	}

	/* Load use of split icons */
	tmpSplit = split_icon[e];

	/* Load temp names into the text fields */
	GetDItem(win, diSideColorName1, NULL, &itemhandle, NULL);
	c2p(tmpName1, pname);
	SetIText(itemhandle, pname);
	GetDItem(win, diSideColorName2, NULL, &itemhandle, NULL);
	c2p(tmpName2, pname);
	SetIText(itemhandle, pname);
	GetDItem(win, diSideColorName3, NULL, &itemhandle, NULL);
	c2p(tmpName3, pname);
	SetIText(itemhandle, pname);

	/* Add "undefined" at the top of the popup menu. */
	AppendMenu(popMenu, "\pundefined");
	/* Add an item separator. */
	AppendMenu(popMenu, "\p(-");
	/* Then append available color and terrain names. */
	append_image_names(popMenu);

	/* Change port and start the main loop */
	GetPort(&oldport);
	SetPort(win);
	done = FALSE;
	editflag = FALSE;
	while (!done) {

		/* Check length of popMenu */
		n = CountMItems(popMenu);

		/* Set popup1 to tmpName1 if found in menu */
		GetDItem(win, diSideColorPopup1, NULL, &itemhandle, NULL);
		SetCtlMax((ControlHandle) itemhandle, n);		/* Important! */
		found = FALSE;
		for (i = 1; i <= n; i++) {
			GetItem(popMenu, i, pname);
			p2c(pname, cname);
			if (strcmp(tmpName1, cname) != 0)
			  continue;
			SetCtlValue((ControlHandle) itemhandle, i);
			found = TRUE;
			break;
		}
		if (!found) {			/* Add tmpName1 to menu if necessary */
			n += 1;
			c2p(tmpName1, pname);
			AppendMenu(popMenu, pname);
			SetCtlMax((ControlHandle) itemhandle, n);	/* Important! */
			SetCtlValue((ControlHandle) itemhandle, n);
		}
		/* Set popup2 to tmpName2 if found in menu */
		GetDItem(win, diSideColorPopup2, NULL, &itemhandle, NULL);
		SetCtlMax((ControlHandle) itemhandle, n);		/* Important! */
		found = FALSE;
		for (i = 1; i <= n; i++) {
			GetItem(popMenu, i, pname);
			p2c(pname, cname);
			if (strcmp(tmpName2, cname) != 0)
			  continue;
			SetCtlValue((ControlHandle) itemhandle, i);
			found = TRUE;
			break;
		}
		if (!found) {			/* Add tmpName2 to menu if necessary */
			n += 1;
			c2p(tmpName2, pname);
			AppendMenu(popMenu, pname);
			SetCtlMax((ControlHandle) itemhandle, n);	/* Important! */
			SetCtlValue((ControlHandle) itemhandle, n);
		}
		/* Set popup3 to tmpName3 if found in menu */
		GetDItem(win, diSideColorPopup3, NULL, &itemhandle, NULL);
		SetCtlMax((ControlHandle) itemhandle, n);		/* Important! */
		found = FALSE;
		for (i = 1; i <= n; i++) {
			GetItem(popMenu, i, pname);
			p2c(pname, cname);
			if (strcmp(tmpName3, cname) != 0)
			  continue;
			SetCtlValue((ControlHandle) itemhandle, i);
			found = TRUE;
			break;
		}
		if (!found) {			/* Add tmpName3 to menu if necessary */
			n += 1;
			c2p(tmpName3, pname);
			AppendMenu(popMenu, pname);
			SetCtlMax((ControlHandle) itemhandle, n);	/* Important! */
			SetCtlValue((ControlHandle) itemhandle, n);
		}

		/* Set undefined colors to default forecolor & maskcolor */
		if (!strcmp(tmpName1, "undefined"))
		  tmpColor[1] = forecolor;
		if (!strcmp(tmpName2, "undefined"))
		  tmpColor[2] = forecolor;
		if (!strcmp(tmpName3, "undefined"))
		  tmpColor[3] = maskcolor;

		/* Set split icons checkbox to correct value */
		GetDItem(win, diSideColorSplit, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, tmpSplit);
		/* Enable or disable the half icon color item in the popup menu */
		if (tmpSplit)
		  EnableItem(useMenu, miHalfIconColor);
		else
		  DisableItem(useMenu, miHalfIconColor);

		/* Set color use popup menus to correct initial values */
		GetDItem(win, diSideColorUse1, NULL, &itemhandle, NULL);
		/* Disable the whole menu if the color is undefined */
		if (!strcmp(tmpName1, "undefined")) {
			tmpUse[1] = miNotInUse;
			HiliteControl((ControlHandle) itemhandle, 255);
		} else
		  HiliteControl((ControlHandle) itemhandle, 0); 
		/* Dont permit half icon color if split icons is unchecked */
		if (!tmpSplit && (tmpUse[1] == miHalfIconColor))
		  tmpUse[1] = miNotInUse;
		SetCtlValue((ControlHandle) itemhandle, tmpUse[1]);

		GetDItem(win, diSideColorUse2, NULL, &itemhandle, NULL);
		/* Disable the whole menu if the color is undefined */
		if (!strcmp(tmpName2, "undefined")) {
			tmpUse[2] = miNotInUse;
			HiliteControl((ControlHandle) itemhandle, 255);
		} else
		  HiliteControl((ControlHandle) itemhandle, 0); 
		/* Dont permit half icon color if split icons is unchecked */
		if (!tmpSplit && (tmpUse[2] == miHalfIconColor))
		  tmpUse[2] = miNotInUse;
		SetCtlValue((ControlHandle) itemhandle, tmpUse[2]);

		GetDItem(win, diSideColorUse3, NULL, &itemhandle, NULL);
		/* Disable the whole menu if the color is undefined */
		if (!strcmp(tmpName3, "undefined")) {
			tmpUse[3] = miNotInUse;
			HiliteControl((ControlHandle) itemhandle, 255);
		} else
		  HiliteControl((ControlHandle) itemhandle, 0); 
		/* Dont permit half icon color if split icons is unchecked */
		if (!tmpSplit && (tmpUse[3] == miHalfIconColor))
		  tmpUse[3] = miNotInUse;
		SetCtlValue((ControlHandle) itemhandle, tmpUse[3]);

		/* Set icon plotting colors */
		tmpMain = forecolor;
		tmpHalf = forecolor;
		tmpMask = maskcolor;
		for (i = 1; i <= 3; i++) {
			if (tmpUse[i] == miMainIconColor)
			  tmpMain = tmpColor[i];
			if (tmpUse[i] == miHalfIconColor)
			  tmpHalf = tmpColor[i];
			if (tmpUse[i] == miIconMaskColor)
			  tmpMask = tmpColor[i];
		}

		/* Open the window now (as late as possible) */
		ShowWindow(win);
		SetCursor(&QD(arrow));
		draw_default_button(win, diSideColorOkButton);
	
		/* Draw background for sample icons */
		GetDItem(win, diSideColorCyan, NULL, NULL, &itemrect);
		ForeColor(cyanColor);
		FillRect(&itemrect, QDPat(black));
		GetDItem(win, diSideColorGreen, NULL, NULL, &itemrect);
		ForeColor(greenColor);
		FillRect(&itemrect, QDPat(black));
		ForeColor(blackColor);
		GetDItem(win, diSideColorFrame, NULL, NULL, &itemrect);
		FrameRect(&itemrect);

		/* Plot 5 sample icons (if available) */
		for (i = 0; i <= 4; i++) {
			/* Skip now if we ran out of unit types */			
			if (i > numutypes - 1)
			     continue;
			/* Get the best unit image from family */
			uimg = best_image(uimages[i], 32, 32);
			macuimg = (MacImage *) uimg->hook;
			/* mac_init_cicn now always creates colricon. */
			if (!macuimg->colricon)
			     mac_init_cicn(uimg);
			cicnhandle = (CIconHandle) macuimg->colricon;
			/* Get pixmap, bitmap & maskmap from cicn and set srcrect */
			bm = (*(cicnhandle))->iconBMap;
			mm = (*(cicnhandle))->iconMask;
			SetRect(&srcrect, 0, 0, bm.rowBytes * 8, bm.rowBytes * 8);
			/* Set destination and colors */
			GetDItem(win, diSideColorIcon1 + i, NULL, NULL, &itemrect);
			RGBForeColor(&tmpMain);
			RGBBackColor(&tmpMask);

			/* First plot the MaskMap srcBic using the icon mask color */
			CopyBits(&mm, &win->portBits, &srcrect, &itemrect, srcBic, NULL);
			/* Then plot the BitMap using the main icon color */ 
			CopyBits(&bm, &win->portBits, &srcrect, &itemrect, srcOr, NULL);
			/* Plot right half in half icon color, but only if the latter is defined */
			if (tmpUse[1] == 3 || tmpUse[2] == 3 || tmpUse[3] == 3) {
				RGBForeColor(&tmpHalf);
				srcrect.left = srcrect.right / 2;
				itemrect.left += (itemrect.right - itemrect.left) / 2;
				CopyBits(&bm, &win->portBits, &srcrect, &itemrect, srcOr, NULL);
			}
			/* Restore colors */
			ForeColor(blackColor);
			RGBBackColor(&tmpBack);
		}

		/* Paint temp colors into the color panes */
		PenSize(2, 2);

		GetDItem(win, diSideColorPane1, NULL, NULL, &itemrect);
		if (strcmp(tmpName1, "undefined") == 0) {
			FillRect(&itemrect, QDPat(ltGray));
		} else {
			RGBForeColor(&tmpColor[1]);
			FillRect(&itemrect, QDPat(black));
			ForeColor(blackColor);
		}
		FrameRect(&itemrect);

		GetDItem(win, diSideColorPane2, NULL, NULL, &itemrect);
		if (strcmp(tmpName2, "undefined") == 0) {
			FillRect(&itemrect, QDPat(ltGray));
		} else {
			RGBForeColor(&tmpColor[2]);
			FillRect(&itemrect, QDPat(black));
			ForeColor(blackColor);
		}
		FrameRect(&itemrect);

		GetDItem(win, diSideColorPane3, NULL, NULL, &itemrect);
		if (strcmp(tmpName3, "undefined") == 0) {
			FillRect(&itemrect, QDPat(ltGray));
		} else {
			RGBForeColor(&tmpColor[3]);
			FillRect(&itemrect, QDPat(black));
			ForeColor(blackColor);
		}
		FrameRect(&itemrect);

		PenNormal();

		/* Handle clicks on buttons, popup menus, color panes, check boxes & edit fields */
		ModalDialog(NULL, &ditem);
		switch (ditem) {

			/* Get name from popup menu, load the color and copy name into text field  */
		case 	diSideColorPopup1:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			mitem = GetCtlValue((ControlHandle) itemhandle);
			GetItem(popMenu, mitem, pname);
			p2c(pname, tmpName1);
			tmpColor[1] = get_color(tmpName1);	/* May change tmpName1 to undefined! */
			c2p(tmpName1, pname);
			GetDItem(win, diSideColorName1, NULL, &itemhandle, NULL);
			SetIText(itemhandle, pname);
	    	break;
		case 	diSideColorPopup2:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			mitem = GetCtlValue((ControlHandle) itemhandle);
			GetItem(popMenu, mitem, pname);
			p2c(pname, tmpName2);
			tmpColor[2] = get_color(tmpName2);	/* May change tmpName2 to undefined! */
			c2p(tmpName2, pname);
			GetDItem(win, diSideColorName2, NULL, &itemhandle, NULL);
			SetIText(itemhandle, pname);
	    	break;
		case 	diSideColorPopup3:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			mitem = GetCtlValue((ControlHandle) itemhandle);
			GetItem(popMenu, mitem, pname);
			p2c(pname, tmpName3);
			tmpColor[3] = get_color(tmpName3);	/* May change tmpName3 to undefined! */
			c2p(tmpName3, pname);
			GetDItem(win, diSideColorName3, NULL, &itemhandle, NULL);
			SetIText(itemhandle, pname);
	    	break;

			/* Get color use from popup menus and copy into tmpUse[i] */
		case 	diSideColorUse1:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			tmpUse[1] = GetCtlValue((ControlHandle) itemhandle);
			if (strcmp(tmpName1, "") == 0) tmpUse[1] = miNotInUse;
			if (tmpUse[2] == tmpUse[1]) tmpUse[2] = miNotInUse;
			if (tmpUse[3] == tmpUse[1]) tmpUse[3] = miNotInUse;
	    	break;
		case 	diSideColorUse2:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			tmpUse[2] = GetCtlValue((ControlHandle) itemhandle);
			if (strcmp(tmpName2, "") == 0) tmpUse[2] = miNotInUse;
			if (tmpUse[1] == tmpUse[2]) tmpUse[1] = miNotInUse;
			if (tmpUse[3] == tmpUse[2]) tmpUse[3] = miNotInUse;
	    	break;
		case 	diSideColorUse3:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			tmpUse[3] = GetCtlValue((ControlHandle) itemhandle);
			if (strcmp(tmpName3, "") == 0) tmpUse[3] = miNotInUse;
			if (tmpUse[1] == tmpUse[3]) tmpUse[1] = miNotInUse;
			if (tmpUse[2] == tmpUse[3]) tmpUse[2] = miNotInUse;
	    	break;

			/* Pick new color by clicking in a color pane */
		case 	diSideColorPane1:
			tmpColor[1] = pick_new_color(tmpColor[1], tmpName1);
			break;
		case 	diSideColorPane2:
			tmpColor[2] = pick_new_color(tmpColor[2], tmpName2);
			break;
		case 	diSideColorPane3:
			tmpColor[3] = pick_new_color(tmpColor[3], tmpName3);
			break;

			/* Toggle split icons checkbox */
		case	diSideColorSplit:
			GetDItem(win, diSideColorSplit, NULL, &itemhandle, NULL);
			tmpSplit = !GetCtlValue((ControlHandle) itemhandle);
			SetCtlValue((ControlHandle) itemhandle, tmpSplit);
			break;

			/* Edit the text fields */
		case	diSideColorName1:
		case	diSideColorName2:
		case	diSideColorName3:
			editflag = TRUE;		/* Set flag to save text instead of exit next time Return is hit */
			continue;	 		/* Do NOT break if text is being edited! Continue instead */

		case 	diSideColorOkButton:
			if (editflag) {		 	/* Save text fields instead of exit when flag is set */ 
				editflag = FALSE;		/* Clear flag to permit exit next time Return is hit */

				/* Check text fields for validity and then update temp colors */
				GetDItem(win, diSideColorName1, NULL, &itemhandle, NULL);
		    	GetIText(itemhandle, pname);
				p2c(pname, cname);
				if (valid_imf_name(cname)) {
					tmpColor[1] = get_color(cname);		/* May change cname to undefined! */
					strcpy(tmpName1, cname);
				} else {	
					ParamText("\pSorry, Ò", pname, "\pÓ is not a valid color name. ", 
						 	 "\pUse hyphens: Òdark-redÓ is OK but Òdark redÓ is not.");
					button = Alert(aOneButtonAlert, NULL);
				}
				c2p(tmpName1, pname);
				SetIText(itemhandle, pname);		/* Update or restore the text field */

				GetDItem(win, diSideColorName2, NULL, &itemhandle, NULL);
		    	GetIText(itemhandle, pname);
				p2c(pname, cname);
				if (valid_imf_name(cname)) {
					tmpColor[2] = get_color(cname);		/* May change cname to undefined! */
					strcpy(tmpName2, cname);
				} else {
					ParamText("\pSorry, Ò", pname, "\pÓ is not a valid color name. ", 
						 	 "\pUse hyphens: Òdark-redÓ is OK but Òdark redÓ is not.");
					button = Alert(aOneButtonAlert, NULL);
				}
				c2p(tmpName2, pname);
				SetIText(itemhandle, pname);		/* Update or restore the text field */

				GetDItem(win, diSideColorName3, NULL, &itemhandle, NULL);
		    	GetIText(itemhandle, pname);
				p2c(pname, cname);
				if (valid_imf_name(cname)) {
					tmpColor[3] = get_color(cname);		/* May change cname to undefined! */
					strcpy(tmpName3, cname);
				} else {	
					ParamText("\pSorry, Ò", pname, "\pÓ is not a valid color name. ", 
						 	 "\pUse hyphens: Òdark-redÓ is OK but Òdark redÓ is not.");
					button = Alert(aOneButtonAlert, NULL);
				}
				c2p(tmpName3, pname);
				SetIText(itemhandle, pname);		/* Update or restore the text field */

				break;

			} else {	/* Update everything and exit the dialog */

				/* Update side colors */
				set_sideColor(e, 1, tmpColor[1]);
				set_sideColor(e, 2, tmpColor[2]);
				set_sideColor(e, 3, tmpColor[3]);

				/* Update side color names */
				set_sideColorName(e, 1, copy_string(tmpName1));
				set_sideColorName(e, 2, copy_string(tmpName2));
				set_sideColorName(e, 3, copy_string(tmpName3));

				/* Change "undefined" to empty string before proceeding */
				/* This is done to conform with the color scheme format */
				if (strcmp(tmpName1, "undefined") == 0)
				  strcpy(tmpName1, "");
				if (strcmp(tmpName2, "undefined") == 0)
				  strcpy(tmpName2, "");
				if (strcmp(tmpName3, "undefined") == 0)
				  strcpy(tmpName3, "");

				/* Build a new colorscheme */
				strcpy(tmpScheme,tmpName1);
				strcat(tmpScheme, ",");
				strcat(tmpScheme, tmpName2);
				strcat(tmpScheme, ",");
				strcat(tmpScheme, tmpName3);
				side_n(e)->colorscheme = copy_string(tmpScheme);

				/* Update color use parameters */
				main_icon_color[e] = 0;
				half_icon_color[e] = 0;
				icon_mask_color[e] = 0;
				for (i = 1; i <=3; i++) {
					if (tmpUse[i] == miMainIconColor)
					  main_icon_color[e] = i;
					if (tmpUse[i] == miHalfIconColor)
					  half_icon_color[e] = i;
					if (tmpUse[i] == miIconMaskColor)
					  icon_mask_color[e] = i;
				}

				/* Set use of split icons */
				split_icon[e] = tmpSplit;

				done = TRUE;
				break;
			}

		case 	diSideColorCancelButton:
			done = TRUE;
			break;
		
		default:   /* Important to break here in order to reenter the while loop! */
			break;

 		}
	}
	SetPort(oldport);
	DisposeDialog(win);
	update_all_map_windows();
}

/* Set the colors of special features */

void
set_feature_color(int e)
{
	short editflag, ditem, mitem, button, found, done, i, n;  
	char cname[32], tmpName[32];
	Handle itemhandle, ppathandle;  
	RGBColor tmpColor;
	MenuHandle popMenu;
	Rect itemrect;
	GrafPtr oldport;
	Str255 pname;
	DialogPtr win;

	/* Then check for color support */
	if (!hasColorQD) {
		ParamText("\pSorry, your mac does not support colors.", "\p", "\p", "\p");
		button = Alert(aOneButtonAlert, NULL);
		return;
	} 

	/* Get dialog window and popup menu */
	win = GetNewDialog(dFeatureColor, NULL, (DialogPtr) -1);
	popMenu = GetMenu(mSideColorPopup);

	/* Load current color & name into temps */
	strcpy(tmpName, featColorName[e]);
	tmpColor = featColor[e];

	/* Load temp name into the text field */
	GetDItem(win, diFeatureColorName, NULL, &itemhandle, NULL);
	c2p(tmpName, pname);
	SetIText(itemhandle, pname);

	/* Append available color and terrain names to the popup menu. */
	append_image_names(popMenu);
	
	/* Change port and start the main loop */
	GetPort(&oldport);
	SetPort(win);
	done = FALSE;
	editflag = FALSE;
	while (!done) {

		/* Check length of popMenu */
		n = CountMItems(popMenu);

		/* Set popup to tmpName if found in menu */
		GetDItem(win, diFeatureColorPopup, NULL, &itemhandle, NULL);
		SetCtlMax((ControlHandle) itemhandle, n);			/* Important! */
		found = FALSE;
		for (i = 1; i <= n; i++) {
			GetItem(popMenu, i, pname);
			p2c(pname, cname);
			if (strcmp(tmpName, cname) != 0)
			  continue;
			SetCtlValue((ControlHandle) itemhandle, i);
			found = TRUE;
			  break;
		}
		if (!found) {				/* Add tmpName to menu if necessary */
			c2p(tmpName, pname);
			AppendMenu(popMenu, pname);
			SetCtlMax((ControlHandle) itemhandle, n + 1);	/* Important! */
			SetCtlValue((ControlHandle) itemhandle, n + 1);
		}

		/* Open the window now (as late as possible) */
		ShowWindow(win);
		SetCursor(&QD(arrow));
		draw_default_button(win, diFeatureColorOkButton);
	
		/* Paint temp color into the color pane */
		PenSize(2, 2);
		GetDItem(win, diFeatureColorPane, NULL, NULL, &itemrect);
		RGBForeColor(&tmpColor);
		FillRect(&itemrect, QDPat(black));
		ForeColor(blackColor);
		FrameRect(&itemrect);
		PenNormal();

		/* Handle clicks on buttons, popup menus, and color panes */
		ModalDialog(NULL, &ditem);
		switch (ditem) {

			/* Get name from popup menu, copy into text field, and load the color */
		case 	diFeatureColorPopup:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			mitem = GetCtlValue((ControlHandle) itemhandle);
			GetItem(popMenu, mitem, pname);
			GetDItem(win, diFeatureColorName, NULL, &itemhandle, NULL);
			SetIText(itemhandle, pname);
			p2c(pname, tmpName);
			tmpColor = get_color(tmpName);
	    		break;

			/* Pick new color by clicking in a color pane */
		case 	diFeatureColorPane:
			tmpColor = pick_new_color(tmpColor, tmpName);
			break;

			/* The text field is being edited */
		case	diFeatureColorName:
			editflag = TRUE;		/* Set flag to save text instead of exit next time OK is hit */
			continue;	 		/* Do NOT break if text is being edited! Continue instead */

		case 	diFeatureColorOkButton:
			if (editflag) {		 	/* Save text fields instead of exit when flag is set */ 
				editflag = FALSE;		/* Clear flag to permit exit next time OK is hit */

				/* Check text field for validity and then update temp color */
				GetDItem(win, diFeatureColorName, NULL, &itemhandle, NULL);
		    		GetIText(itemhandle, pname);
				p2c(pname, cname);
				if (valid_imf_name(cname)) {
					strcpy(tmpName, cname);
					tmpColor = get_color(tmpName);
				} else {
					ParamText("\pSorry, Ò", pname, "\pÓ is not a valid color name. ", 
						 	 "\pUse hyphens: Òdark-redÓ is OK, Òdark redÓ is not.");
					button = Alert(aOneButtonAlert, NULL);
					c2p(tmpName, pname);
					SetIText(itemhandle, pname);		/* Restore old text */
				} 
				break;

			} else {	/* Update everything and exit the dialog */

				featColor[e] = tmpColor;
				featColorName[e] = copy_string(tmpName);

				/* This is now checked only once, instead of every time a map is redrawn. */
				if (strcmp(gridcolor_name, unseencolor_name) == 0
				    || g_terrain_seen() || (dside)->see_all) {
		 	 		 grid_matches_unseen = TRUE;
				} else grid_matches_unseen = FALSE;

				ui_update_state();
				done = TRUE;
				break;
			}

		case 	diFeatureColorCancelButton:
			done = TRUE;
			break;
		
		default:   /* Important to break here in order to reenter the while loop! */
			break;

 		}
	}
	SetPort(oldport);
	DisposeDialog(win);
	update_all_map_windows();
}

/* Set color (or pattern) used to display the terrain */

void
set_terrain_color( Str255 tname)
{
	short editflag, ditem, mitem, button, found, done, i, n, e = -1;  
	char cname[32], tmpName[32];
	Handle itemhandle, ppathandle;  
	RGBColor tmpColor;
	MenuHandle popMenu;
	MacImage *macimg;
	Rect itemrect;
	GrafPtr oldport;
	Str255 pname;
	ImageFamily *imf;
	Image *img;
	DialogPtr win;

	/* Then check for color support */
	if (!hasColorQD) {
		ParamText("\pSorry, your mac does not support colors.", "\p", "\p", "\p");
		button = Alert(aOneButtonAlert, NULL);
		return;
	} 

	/* Get dialog window and popup menu */
	win = GetNewDialog(dFeatureColor, NULL, (DialogPtr) -1);
	popMenu = GetMenu(mSideColorPopup);

	/* Load tname into the text field and get the corresponding color */
	GetDItem(win, diFeatureColorName, NULL, &itemhandle, NULL);
	SetIText(itemhandle, tname);
	p2c(tname, tmpName);
	tmpColor = get_color(tmpName);

	/* Get the current terrain number from tmpName */
	for_all_terrain_types(i) {
		/* Use image names if they exist */
		if (!empty_string(t_image_name(i))) {
			sanitize_for_menu(t_image_name(i), pname);
			/* Else use terrain names (may differ from image names) */
		} else
		  sanitize_for_menu(t_type_name(i), pname);
		p2c(pname, cname);
		if (strcmp(cname, tmpName) != 0)
		  continue;
		e = i; 
		break;
	}

	/* Append available color and terrain names to the popup menu. */
	append_image_names(popMenu);
	
	/* Change port and start the main loop */
	GetPort(&oldport);
	SetPort(win);
	done = FALSE;
	editflag = FALSE;
	while (!done) {

		/* Check length of popMenu and load the image family */
		n = CountMItems(popMenu);
		imf = get_imf(tmpName);
		mac_load_imf(imf);

		/* Set popup to tmpName if found in menu */
		GetDItem(win, diFeatureColorPopup, NULL, &itemhandle, NULL);
		SetCtlMax((ControlHandle) itemhandle, n);			/* Important! */
		found = FALSE;
		for (i = 1; i <= n; i++) {
			GetItem(popMenu, i, pname);
			p2c(pname, cname);
			if (strcmp(tmpName, cname) != 0)
			  continue;
			SetCtlValue((ControlHandle) itemhandle, i);
			found = TRUE; break;
		}
		if (!found) {				/* Add tmpName to menu if necessary */
			c2p(tmpName, pname);
			AppendMenu(popMenu, pname);
			SetCtlMax((ControlHandle) itemhandle, n + 1);	/* Important! */
			SetCtlValue((ControlHandle) itemhandle, n + 1);
		}

		/* Open the window now (as late as possible) */
		ShowWindow(win);
		SetCursor(&QD(arrow));
		draw_default_button(win, diFeatureColorOkButton);
	
		/* Paint terrain pattern into the color pane */
		GetDItem(win, diFeatureColorPane, NULL, NULL, &itemrect);
		img = best_image(imf, hws[5], hcs[5]);
		macimg = (MacImage *) img->hook;
		if (macimg->colrpat != NULL) {
			FillCRect(&itemrect, macimg->colrpat);
		} else if (macimg->colordefined) {
			RGBForeColor(&macimg->color);
			PaintRect(&itemrect);
			ForeColor(blackColor);
		}

		/* Draw the frame */
		PenSize(2, 2);
		FrameRect(&itemrect);
		PenNormal();

		/* Handle clicks on buttons, popup menus, and color panes */
		ModalDialog(NULL, &ditem);
		switch (ditem) {

			/* Get name from popup menu, copy into text field, and load the color */
		case 	diFeatureColorPopup:
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			mitem = GetCtlValue((ControlHandle) itemhandle);
			GetItem(popMenu, mitem, pname);
			GetDItem(win, diFeatureColorName, NULL, &itemhandle, NULL);
			SetIText(itemhandle, pname);
			p2c(pname, tmpName);
			tmpColor = get_color(tmpName);
	    	break;

			/* The text field is being edited */
		case	diFeatureColorName:
			editflag = TRUE;		/* Set flag to save text instead of exit next time OK is hit */
			continue;	 		/* Do NOT break if text is being edited! Continue instead */

		case 	diFeatureColorOkButton:
			if (editflag) {		 	/* Save text fields instead of exit when flag is set */ 
				editflag = FALSE;		/* Clear flag to permit exit next time OK is hit */

				/* Check text field for validity and then update temp color */
				GetDItem(win, diFeatureColorName, NULL, &itemhandle, NULL);
		    	GetIText(itemhandle, pname);
				p2c(pname, cname);
				if (valid_imf_name(cname)) {
					strcpy(tmpName, cname);
					tmpColor = get_color(tmpName);
				} else {	/* Restore previous text */
					ParamText("\pSorry, Ò", pname, "\pÓ is not a valid color name. ", 
						 	 "\pUse hyphens: Òdark-redÓ is OK but Òdark redÓ is not.");
					button = Alert(aOneButtonAlert, NULL);
					c2p(tmpName, pname);
					SetIText(itemhandle, pname);
				} 
				break;

			} else {	/* Update everything and exit the dialog */

				/* Replace timages[e] with the new image family. */
				timages[e] = imf;
				/* Recompute its best images. */
				for (i = 0; i < NUMPOWERS; ++i) {
					img = best_image(timages[e], hws[i], hhs[i]);
					if (img && e != -1) {
						best_terrain_images[i * numttypes + e] = img;
					}
				}
				/* Replace terrain colors used for solid color drawing */
				img = get_img(imf, 1, 1);
				if (img->hook && e != -1) {
					macimg = (MacImage *) img->hook;
					tcolors[e] = macimg;
				}
				done = TRUE;
				break;
			}

		case 	diFeatureColorCancelButton:
			done = TRUE;
			break;
		
		default:   /* Important to break here in order to reenter the while loop! */
			break;

 		}
	}
	SetPort(oldport);
	DisposeDialog(win);
	update_all_map_windows();
}

/* --- FUNCTIONS USED TO BUILD AND MAINTAIN MENUS -----------------------------------*/

/* Insert side and feature names into the colors menu */

void
build_colors_menu(void)
{
	MenuHandle menu;
	Str255 pname;
	int i, t;

	menu = GetMenu(mColors);
	/* First load side names at the top of the menu */
	/* Side 0 (Independents) is already in the menu */
	for (i = 1; i <= numsides; i++) {
		sanitize_for_menu(shortest_side_title(side_n(i), tmpbuf), pname);
		InsMenuItem(menu, pname, i);
	}
	/* Then load feature names below first separator */
	for (i = 0; i <= FEATURES; i++) {
		sanitize_for_menu(featColorItemName[i], pname);
		InsMenuItem(menu, pname, numsides + i + 2);
	}
	/* Then load terrain types below second separator */
	for_all_terrain_types(t) {
		sanitize_for_menu(t_type_name(t), pname);
		InsMenuItem(menu, pname, numsides + FEATURES + t + 4);
	}
}

void
build_sides_menu(void)
{
	MenuHandle menu;
	Str255 pname;
	int i;

	menu = GetMenu(mSidesPopup);
	/* First load real side names for sides in play */
	/* Side 0 (Independents) is already in the menu */
	for (i = 1; i <= numsides; i++) {
		sanitize_for_menu(shortest_side_title(side_n(i), tmpbuf), pname);
		AppendMenu(menu, pname);
	}
	/* Then load side numbers for remaining sides */
	for (i = numsides + 1; i <= MAXSIDES; i++) {
		sprintf(tmpbuf, "Side %d", i);
		sanitize_for_menu(tmpbuf, pname);
		AppendMenu(menu, pname);
	}
}

void
build_features_menu(void)
{
	MenuHandle menu;
	Str255 pname;
	int i;

	menu = GetMenu(mFeatPopup);
	/* Load available feature names */
	for (i = 0; i <= FEATURES; i++) {
		sanitize_for_menu(featColorItemName[i], pname);
		AppendMenu(menu, pname);
	}
}

int
append_image_names(MenuHandle menu)
{
	Handle	ppathandle;  
	char 		cname[32];
	Str255	pname;
	int 		i, n, num = 0;

	/* Use lib-mac Images file if available. */
	n = GetResFileAttrs(images_refnum);
	if (!ResError()) {
		/* Load all available ppat colors into the popup menu. */
		n = CountResources('ppat');
		for (i = 1; i <= n; ++i ) {
			ppathandle = GetIndResource('ppat', i);
			GetResInfo(ppathandle, NULL, NULL, pname);
			/* Only use ppats that live in the Images file. */
			if (HomeResFile(ppathandle) != images_refnum)
				continue;
			p2c(pname, cname);
			/* Skip invalid names. */
			if (!valid_imf_name(cname))
				continue;
			AppendMenu(menu, pname);
			++num;
		}

	/* Else use colors.imf and terrain.imf. */
	} else {
		/* First load all real colors. */
		num += append_color_names(menu);
		/* Then add an item separator. */
		if (num)
		    AppendMenu(menu, "\p(-");
		/* Then load the terrain colors. */
		num += append_terrain_names(menu);
	}
	return num;
}

int
append_color_names(MenuHandle menu)
{
	int	num = 0, startlineno = 1, endlineno = 1;
	LibraryPath *p;
	Obj	*form;
	FILE	*fp;

	for_all_library_paths(p) {
    		make_pathname(p->path, "colors.imf", "", spbuf);
		fp = open_file(spbuf, "r");
		if (fp != NULL) {
			while ((form = read_form(fp, &startlineno, &endlineno)) != lispeof) {
				if (append_imf_name(form, menu))
				    ++num;
			}
			fclose(fp);
			return TRUE;
		}
	}
		run_warning("Could not find your colors.imf file");
		return FALSE;		
	}

int
append_terrain_names(MenuHandle menu)
{
	int	num = 0, startlineno = 1, endlineno = 1;
	LibraryPath *p;
	Obj	*form;
	FILE	*fp;

	for_all_library_paths(p) {
    		make_pathname(p->path, "terrain.imf", "", spbuf);
		fp = open_file(spbuf, "r");
		if (fp != NULL) {
			while ((form = read_form(fp, &startlineno, &endlineno)) != lispeof) {
				if (append_imf_name(form, menu))
				    ++num;
			}
			fclose(fp);
			return TRUE;
		}
	}
		run_warning("Could not find your terrain.imf file");
	return FALSE;		
}	

int
append_imf_name(Obj *form, MenuHandle menu)
{
	Str255 	lastname, pname;
	char 		*name, cname[32];
	Obj		*head;
	
	/* Ignore any non-lists. */
	if (!consp(form))
	    return FALSE;
	head = car(form);
	/* Ignore any non-imf forms. */
	if (!match_keyword(head, K_IMF))
	    return FALSE;
	/* Ignore imf without a name. */
	if (!stringp(cadr(form)))
	    return FALSE;
	name = c_string(cadr(form));
	/* Ignore invalid imf names. */
	if (!valid_imf_name(name))
	    return FALSE;
	/* Get the last item name in the menu. */
	GetItem(menu, CountMItems(menu), lastname);
	p2c(lastname, cname);
	/* Skip if imf has the same name as last item. */
	if (strcmp(name, cname) == NULL)
	    return FALSE;
	c2p(name, pname);
	AppendMenu(menu, pname);
	return TRUE;
}
