/* Unit specific windows and dialogs for the Mac interface to Xconq.
   Copyright (C) 1998-2000 Hans Ronne and
  1992-1999  Stanley T. Shebs (closeup code).

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.  */

/* This file now contains all windows (closeups) and dialogs that refer to individual units.
Much of the stuff is from the old macadv.c file, some from macwins.c. global_advance_dialog 
has been renamed to side_research_dialog and is now in macwins.c. */ 

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

/* Base numbers of resource cicns. */
#define LARGE_CICNS 2000
#define SMALL_CICNS 2100

/* Max number of facilities and occs displayed in city dialog. Should always agree with the DITL!  */
#define MAX_DISPLAYED_FACS 12
#define MAX_DISPLAYED_OCCS 24
#define CLEAR_AGENDA 99

int default_drawsizes = TRUE;		/* Draw unit size in maps, closeups and lists. */

extern void unit_research_dialog(Unit *unit);			

static UnitCloseup *create_unit_closeup(Unit *unit);
static void draw_simple_closeup(UnitCloseup *closeup);
static void set_simple_closeup_size(UnitCloseup *closeup);
static void draw_advanced_closeup(UnitCloseup *closeup);
static void copy_rect_from_gworld(WindowPtr win, Map *map, Rect cityrect, Rect itemrect);
static void draw_advanced_content(DialogPtr win, Unit *unit);
static void plot_resource_cicns(Map *map, Rect plotrect, int x, int y);
static void draw_landuse_near_unit(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int near);
static void toggle_landuse_one_cell(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int x, int y);
static void hit_closeup_dialog(WindowPtr win, int itemhit, Point mouse, int mods);

static void hit_unit_build_dialog(int ditem, Point mouse, int mods);

static MenuHandle build_construction_menu(Unit *unit);

/* Unit that is shown in the build window. */

Unit	*buildwin_unit = NULL;

/* Globals for unit closeup windows. */

int lastunitcloseuph = -1;
int lastunitcloseupv = -1;

static int m_per_row = 3;
static char *nummrows;

int closeup_spacing = 16;
int closeupwinwid = 240;

/* Unit closeups. */

/* This is the top-level access to bring up a unit closeup. */

void
show_unit_closeup(Unit *unit)
{
	UnitCloseup *unitcloseup;
	Map *map;

	if (frontmap) {
		/* Select this unit on the frontmap. */
		select_exactly_one_unit(frontmap, unit);
		/* Also make it the current unit if possible. */
		if (unit->plan
			&& !unit->plan->asleep
			&& !unit->plan->reserve
			&& !unit->plan->delayed
			&& unit->plan->waitingfortasks
				) {
				frontmap->curunit = unit;
		}
		update_cell(frontmap, unit->x, unit->y);
	}

	/* Then find the closeup if it exists. */
	unitcloseup = find_unit_closeup(unit);
	/* Or create it. */
	if (!unitcloseup)
	     unitcloseup = create_unit_closeup(unit);
	/* Check that we have one before proceeding. */
	if (unitcloseup && unitcloseup->window) {
		SelectTheWindow(unitcloseup->window);
		update_window(unitcloseup->window);	
	}
}

UnitCloseup *
find_unit_closeup(Unit *unit)
{
	UnitCloseup *unitcloseup;

	for_all_unit_closeups(unitcloseup) {
		if (unitcloseup->unit == unit && unitcloseup->window)
		    return unitcloseup;
	}
	return NULL;
}

UnitCloseup *
unit_closeup_from_window(WindowPtr win)
{
	UnitCloseup *unitcloseup;
	
	for_all_unit_closeups(unitcloseup) {
		if (unitcloseup->window == win)
		  return unitcloseup;
	}
	return NULL;
}

UnitCloseup *
create_unit_closeup(Unit *unit)
{
	UnitCloseup	*unitcloseup;
	int 			mainwidth, mainheight;
	Rect			winRect;		
	Point			winUL;
	Point			winDR;
	DialogPtr 		win;
	int 			u, w, h;
	GrafPtr 		oldport;
		
	/* Stop if we lack a display. */
	if (!active_display(dside) || unit == NULL)
	    return NULL;
	/* Stop if we may not examine this unit. */
	if (!side_sees_unit(dside, unit)
	    && (!frontmap || !frontmap->see_all))
		return NULL;
	DGprintf("Creating a closeup of %s\n", unit_desig(unit));
	unitcloseup = (UnitCloseup *) xmalloc(sizeof(UnitCloseup));
	unitcloseup->unit = unit;
	u = unit->type;

	if (u_advanced(unit->type))
		win = GetNewDialog(dCity, NULL, NULL);
	else	win = GetNewDialog(dCloseup, NULL, NULL);
	unitcloseup->window = win;
	
	/* Size and position code for non-advanced closeups. */
	if (u_advanced(unit->type) != TRUE) {
		/* Put it adjacent to clicked point if opened from within another closeup. */
		if (unit->transport && find_unit_closeup(unit->transport)) {
			Point mouse;

			GetMouse(&mouse);
			LocalToGlobal(&mouse);
			MoveWindow(win, mouse.h - 2 , mouse.v + 8, TRUE);
		/* else put it adjacent to the clicked or list-selected unit. */
		} else if (frontmap) {
			Point upoint;
			int sx, sy;
							
			GetPort(&oldport);
			SetPort(frontmap->window);
			xform(frontmap, unit->x + 2, unit->y - 2, &sx, &sy);
			upoint.h = sx; 
			upoint.v = sy;
			LocalToGlobal(&upoint);
			MoveWindow(win, upoint.h, upoint.v, true);
			SetPort(oldport);
		} else {
			/* This only happens if no maps are open and a closeup 
			is chosen from a list. */
			stagger_window(unitcloseup->window, 
						&lastunitcloseuph, &lastunitcloseupv);
		}
		set_simple_closeup_size(unitcloseup);

		/* Find the closeups global coordinates. */
		GetPort(&oldport);
		SetPort(win);
		winRect = win->portRect;			
		winUL.h = winRect.left;
		winUL.v = winRect.top;
		winDR.h = winRect.right;
		winDR.v = winRect.bottom;
		LocalToGlobal(&winUL);			
		LocalToGlobal(&winDR);			

		/* Make sure the closeup stays within the main screen. */
		get_main_screen_size(&mainwidth, &mainheight);
		if (winDR.h + 3 > mainwidth
		     || winUL.h < 3
		     || winDR.v + 3 > mainheight
		     || winUL.v < GetMBarHeight() + 3) {
			MoveWindow(win, 
				max(3, min(winUL.h, winUL.h + mainwidth - winDR.h - 3)),
				max(GetMBarHeight() + 3, min(winUL.v, winUL.v + mainheight - winDR.v - 3)),
				false);
		}
		SetPort(oldport);
	}

	/* Add window and menu titles. */
	sprintf(spbuf, "%s", medium_long_unit_handle(unit));
	capitalize(spbuf);
	add_window_menu_item(spbuf, win);
	/* Add it to the closeup list. */
	unitcloseup->next = unitcloseuplist;
	unitcloseuplist = unitcloseup;
	MakeDialogFloat(win);
	return unitcloseup;
}

/* Compute the right size for a simple closeup. */

void
set_simple_closeup_size(UnitCloseup *closeup)
{
	Unit 		*unit = closeup->unit, *unit2;
	DialogPtr 	win = closeup->window;
	int 		hgt, m, u, count, occs = 0;
	Task 		*task;
			
	closeup_spacing = small_line_spacing + 1;
	/* This is the first line below the icon. */
	hgt = 60;
	/* Add row for location. */
	hgt += closeup_spacing;
	if (nummrows == NULL) {
		/* Compute and cache the space needed to display each unit's supply. */
		nummrows = xmalloc(numutypes);
		for_all_unit_types(u) {
			nummrows[u] = count = 0;
			for_all_material_types(m) {
				if (um_storage_x(u, m) > 0)
		  		  ++count;
		  	}
		  	if (count > 0) {
		  		nummrows[u] = count / m_per_row;
		  		nummrows[u] += (count % m_per_row > 0 ? 1 : 0);
		  	}
		}
	}
	hgt += nummrows[unit->type] * closeup_spacing;

	/* Compute space needed for plans and tasks. */
	if (unit->plan) {
		hgt += closeup_spacing;
		if (unit->plan->maingoal) {
			hgt += closeup_spacing;
		}
		if (unit->plan->formation) {
			hgt += closeup_spacing;
		}
		if (unit->plan->tasks) {
			for_all_tasks(unit->plan, task) {
		  		hgt += closeup_spacing;
			}
		}
	}
	/* Add space for occupants. */
	if (unit->occupant) {
		for_all_occupants(unit, unit2) {
			occs += 1;
		}
		hgt += (occs / 2 + occs % 2) * 22;
	}
	/* Resize the window if the size has changed. */
	if (win->portRect.bottom - win->portRect.top != hgt)
		SizeWindow(win, closeupwinwid, hgt, 1);
}

/* Draw all the fields and displays in a unit closeup. */

void
draw_unit_closeup(UnitCloseup *closeup)
{
	DialogPtr 	win = closeup->window;
	Unit 		*unit = closeup->unit;
	GrafPtr 	oldport;

	/* Skip if we lack a display. */
	if (!active_display(dside))
		return;
	/* Blast the closeup if the unit is dead or has defected. */
	if (!in_play(unit)
	      || (!side_sees_unit(dside, unit)
	    	&& (!frontmap || !frontmap->see_all))) { 
		close_window(win);
		return;
	}

	/* This seemed like a good idea, but it interferes with the
	current unit display code. */
#if 0
	/* Make sure the unit's cell is updated before proceeding. */
	update_cell_display(dside, unit->x, unit->y, UPDATE_ALWAYS);
#endif

	GetPort(&oldport);
	SetPort(win);

	/* Draw the appropriate type of closeup. */
	if (u_advanced(unit->type))
		draw_advanced_closeup(closeup);
	else	draw_simple_closeup(closeup);

	SetPort(oldport);
}	

void
draw_simple_closeup(UnitCloseup *closeup)
{
	int u, m, sx = 2, sy = 2, occs = 0, count = 0, mrow, sx2, sy2, w, h;
	char infobuf[BUFSIZE];
	Rect tmprect;
	DialogPtr win = closeup->window;
	Unit *unit = closeup->unit;
	Unit *unit2;

	/* First adjust closeup size to current need. */
	set_simple_closeup_size(closeup);
	/* Use the small font. */
	TextFont(small_font_id);
	TextSize(small_font_size);
	EraseRect(&win->portRect);
	u = unit->type;

	/* Draw the unit's image. */
	SetRect(&tmprect, sx, sy, sx + 44, sy + 44); 
	EraseRect(&tmprect);
	draw_unit_image(win, tmprect.left, tmprect.top, 
				tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
				unit->type, side_number(unit->side), 
				(frontmap ? frontmap->sidecolors : default_sidecolors), 
				0, !completed(unit),
				(frontmap ? frontmap->draw_emblems : default_draw_emblems));
	/* Draw unit size on top of advanced unit. */
	if (u_advanced(unit->type) && default_drawsizes) 
		draw_unit_size(unit, win, tmprect.left, tmprect.top,
						tmprect.right - tmprect.left, tmprect.bottom - tmprect.top);

	/* Draw the unit's id next to the icon if debugging. */
	if (Debug || DebugG || DebugM) {
		sprintf(infobuf, "# %d", unit->id);
		MoveTo(52, sy + closeup_spacing);
		DrawText(infobuf, 0, strlen(infobuf));
	}
	/* Draw the unit's hit points and acp's next to the icon. */
	hp_desc(infobuf, unit, TRUE);
	MoveTo(52, sy + 2 * closeup_spacing);
	DrawText(infobuf, 0, strlen(infobuf));
	acp_desc(infobuf, unit, TRUE);
	MoveTo(52, sy + 3 * closeup_spacing);
	DrawText(infobuf, 0, strlen(infobuf));
	/* This is the first line below the icon. */
	sx = 6;
	sy += 60;
	/* Draw the transport if it exists. */
	if (unit->transport) {
		sprintf(infobuf, "Inside:");
		MoveTo(sx, sy);
		DrawText(infobuf, 0, strlen(infobuf));
		SetRect(&tmprect, 40, sy - 16, 62, sy + 6); 
		EraseRect(&tmprect);
		draw_unit_image(win, tmprect.left, tmprect.top,
					tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
					unit->transport->type, side_number(unit->transport->side), 
					(frontmap ? frontmap->sidecolors : default_sidecolors), 
					0, !completed(unit->transport),
					(frontmap ? frontmap->draw_emblems : default_draw_emblems));
		sprintf(infobuf, "%s", short_unit_handle(unit->transport));
    		tprintf(infobuf, " at %d,%d", unit->x, unit->y);
		MoveTo(66, sy);
		DrawText(infobuf, 0, strlen(infobuf));
	/* Else draw the unit's location. */
	} else {
		location_desc(infobuf, dside, unit, unit->type, unit->x, unit->y);
		MoveTo(sx, sy);
		DrawText(infobuf, 0, strlen(infobuf));
	}
	/* Draw the unit's supplies. */
	mrow = 0;
	while (supply_desc(infobuf, unit, mrow)) {
		sy += closeup_spacing;
		MoveTo(sx, sy);
		DrawText(infobuf, 0, strlen(infobuf));
		++mrow;
	}
	/* Draw the unit's plan, if it has one. */
	if (unit->plan) {

		Task *task;
		Plan *plan = unit->plan;
		
		/* plan_desc now also includes goal_desc on the same line. To avoid 
		repetition only the first half of plan_desc is therefore included below. */ 
		strcpy(infobuf, "Plan: ");
		strcat(infobuf, plantypenames[plan->type]);
		if (plan->waitingfortasks)
		  strcat(infobuf, " Waiting");
		if (plan->asleep)
		  strcat(infobuf, " Asleep");
		if (plan->reserve)
		  strcat(infobuf, " Reserve");
		if (plan->delayed)
		  strcat(infobuf, " Delay");
		if (!plan->aicontrol)
		  strcat(infobuf, " NoAI");
		if (plan->supply_is_low)
		  strcat(infobuf, " SupplyLow");
		if (plan->tasks) {
			int i = 0;
			for_all_tasks(plan, task)
				++i;
			tprintf(infobuf, " (%d task%s)", i, (i == 1 ? "" : "s"));
		} 
		sy += closeup_spacing;
		MoveTo(sx, sy);
		DrawText(infobuf, 0, strlen(infobuf));
	    	if (plan->maingoal) {
				strcpy(infobuf, "Goal: ");
		    		goal_desc(tmpbuf, plan->maingoal);
				strncat(infobuf, tmpbuf, strlen(tmpbuf));
				sy += closeup_spacing;			
				MoveTo(sx, sy);
				DrawText(infobuf, 0, strlen(infobuf));
	    	}
	    	if (plan->formation) {
				strcpy(infobuf, "Formation: ");
	    			goal_desc(infobuf+strlen(infobuf), plan->formation);
				sy += closeup_spacing;			
				MoveTo(sx, sy);
				DrawText(infobuf, 0, strlen(infobuf));
	    	}
		if (plan->tasks) {
			int i = 0;
	    		for_all_tasks(plan, task) {
				++i;
				sprintf(infobuf, "Task %d: ", i);
				task_desc(tmpbuf, unit->side, unit, task);			
				strcat(infobuf, tmpbuf);
				sy += closeup_spacing;			
				MoveTo(sx, sy);
				DrawText(infobuf, 0, strlen(infobuf));
			}
		}
	}
	if (unit->occupant) {
		sy += 6;
		MoveTo(0, sy);
		LineTo(closeupwinwid, sy);
		sy += 4;
		/* Count the occupants. */
		for_all_occupants(unit, unit2) {
			occs += 1;
		}
		/* Draw each occupant in the correct position. */
		for_all_occupants(unit, unit2) {
			count += 1;
			SetRect(&tmprect, sx, sy, sx + 22, sy + 22); 
			EraseRect(&tmprect);
			draw_unit_image(win, tmprect.left, tmprect.top,
						tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
						unit2->type, side_number(unit2->side), 
						(frontmap ? frontmap->sidecolors : default_sidecolors), 
						0, !completed(unit2),
						(frontmap ? frontmap->draw_emblems : default_draw_emblems));
			sprintf(infobuf, "%s", short_unit_handle(unit2));
			MoveTo(sx + 22, sy + 16);
			DrawText(infobuf, 0, strlen(infobuf));
			sy += 22;
			/* Check if we should switch to second column. */
			if (2 * count == occs || 2 * count == occs + 1) {
				sx += closeupwinwid / 2;
				sy -= count * 22;	
			}
		}
	}			
	SetCursor(&QD(arrow));
	draw_default_button(win, CancelButton);
}

/* First half of the old modal city_dialog. */

void
draw_advanced_closeup(UnitCloseup *closeup)
{
	int			run =0, width, height, downsx, downsy, downx, downy, sx, sy, m, d;
	short 		ditem, mitem, done = FALSE, disabled = TRUE, i, u, u2, a, x, y;
	char			buf[32], cname[32], uname[32], sname[32];
	MenuHandle	buildMenu, advanceMenu, planMenu;
	Rect 			itemrect, cityrect;
	Str255 		pname, pnumber;
	Handle 		itemhandle;  
	RGBColor		oldBack;
	Point			mouse;
	Unit 			*unit;
	Side			*side;
 	DialogPtr		win;
 	Map			*map;	

	win = closeup->window;
	unit = closeup->unit;
	side = unit->side;
	map = frontmap;
	
	/* Give controlling side (including debuggers 
	and designers) full control. */
	if (side_controls_unit(dside, unit))
		disabled = FALSE;

	/* Make sure visibility is up to date. */
	if (side) {
		for_all_cells_within_reach(unit, x, y) {
			if (terrain_visible(side, x, y)) {
				see_exact(side, x, y);
			}
		}
	}

	planMenu = GetMenu(mPlanTypes);
	buildMenu = build_construction_menu(unit);
	advanceMenu = build_research_menu(side);
	
	/* Get the type name of unit that is being constructed, if any. */
	if (unit->plan->tasks 
	      && unit->plan->tasks->type == TASK_BUILD
	      && is_unit_type(unit->plan->tasks->args[0])) {
	   	strcpy(uname, u_type_name(unit->plan->tasks->args[0]));
	} else if (unit->plan->asleep)
		strcpy(uname, "Asleep");
	else if (unit->plan->reserve)
		strcpy(uname, "Skip Turn");
	else	strcpy(uname, "Idle");

	/* Set build popup to uname. */
	m = CountMItems(buildMenu);
	GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
	SetCtlMax((ControlHandle) itemhandle, m);
	for (i = 1; i <= m; i++) {
		GetItem(buildMenu, i, pname);
		p2c(pname, cname);
		if (strcmp(uname, cname) != 0)
			continue;
		SetCtlValue((ControlHandle) itemhandle, i);
	}

	/* Get the advance that is being researched by this unit, if any. */
	if (unit->curadvance != NOADVANCE) { 
	   	 strcpy(sname, a_type_name(unit->curadvance));
	} else strcpy(sname, "Idle"); 

	/* Set advance popup to sname. */
	m = CountMItems(advanceMenu);
	GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
	SetCtlMax((ControlHandle) itemhandle, m);
	for (i = 1; i <= m; i++) {
		GetItem(advanceMenu, i, pname);
		p2c(pname, cname);
		if (strcmp(sname, cname) != 0)
			continue;
		SetCtlValue((ControlHandle) itemhandle, i);
	}

	GetDItem(win, diCityMap, NULL, NULL, &itemrect);
	width = itemrect.right - itemrect.left;
	height = itemrect.bottom - itemrect.top;

	/* Save the Dialog background color. */
	oldBack = ((CGrafPtr) win)->rgbBkColor;
	BackColor(whiteColor);

	/* Draw background and frame. */
	InsetRect(&itemrect, -2, -2);
	RGBForeColor(&backcolor);
	PaintRect(&itemrect);
	PenSize(2, 2);
	ForeColor(blackColor);
	FrameRect(&itemrect);
	PenNormal();
	InsetRect(&itemrect, 2, 2);

	if (map) {
		/* Find the city position. */
		xform(map, unit->x, unit->y, &sx, &sy);

		/* Center cityrect around the city. */
		cityrect.left 	= sx + map->vp->hw/2 - width/2;
		cityrect.right 	= sx + map->vp->hw/2 + width/2;
		cityrect.top 	= sy + map->vp->hh/2 - height/2;
		cityrect.bottom 	= sy + map->vp->hh/2 + height/2;
		
		/* Find cityrect position in the offscreen gworld. */
		OffsetRect(&cityrect, map->offsetx + map->bufx - map->conw, 
					        map->offsety + map->bufy - map->toph);

		/* Copy cityrect from the offscreen gworld. */
		copy_rect_from_gworld(win, map, cityrect, itemrect);

		/* Handle cityrect part that extends beyond left end of gworld. */
		if (area.xwrap && cityrect.left < map->gworldPortPtr->portRect.left) {
			OffsetRect(&cityrect, map->vp->sxmax, 0);
			copy_rect_from_gworld(win, map, cityrect, itemrect);
		}
		/* Handle cityrect part that extends beyond right end of gworld. */
		if (area.xwrap && cityrect.right > map->gworldPortPtr->portRect.right) {
			OffsetRect(&cityrect, -map->vp->sxmax, 0);
			copy_rect_from_gworld(win, map, cityrect, itemrect);
		}
				
		/* Draw landuse within the cityrect. */
		d = max(width/map->vp->hw, height/map->vp->hh);	/* d could be smaller? */
		draw_landuse_near_unit(map, win, itemrect, unit, d);
	}

	/* Restore dialog background. */
	RGBBackColor(&oldBack);

	/*Hide click map text from disabled sides. */
	if (disabled)
		HideDItem(win, diCityClickText);

	/* Set the AI control checkbox. */
	GetDItem(win, diCityAICheck, NULL, &itemhandle, NULL);
	SetCtlValue((ControlHandle) itemhandle, unit->plan->aicontrol);
	/* Don't allow disabled sides to use the AI box. */
	if (disabled)
		HiliteControl((ControlHandle) itemhandle, 255); 

	/* Set the plan checkbox. */
	GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
	SetCtlValue((ControlHandle) itemhandle, unit->autoplan);

	/* Set the advance checkbox. */
	GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
	SetCtlValue((ControlHandle) itemhandle, unit->autoresearch);

	/* Set the autobuild radio button. */
	GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
	SetCtlValue((ControlHandle) itemhandle, unit->autobuild);

	/* Set the manual build radio button. */
	GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
	SetCtlValue((ControlHandle) itemhandle, !unit->autobuild);

	/* Draw the run in its textfield. */
	GetDItem(win, diCityBuildEdit, NULL, &itemhandle, NULL);
	if (unit->plan->tasks
	    && unit->plan->tasks->type == TASK_BUILD
	    && unit->plan->tasks->args[3] > 0)
		NumToString(unit->plan->tasks->args[3], pname);
	else	NumToString(construction_run_doctrine(unit, 0), pname);
	SetIText(itemhandle, pname);
	SelIText(win, diCityBuildEdit, 0, 32767);

	/* Set plan type popup menu to current plan. */
	GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
	SetCtlValue((ControlHandle) itemhandle, unit->plan->type + 1);

	/* Also update the checkmarks, since this menu is used elsewhere. */
	CheckItem(planMenu, miPlanTypeNone, (unit->plan->type == PLAN_NONE));
	CheckItem(planMenu, miPlanTypePassive, (unit->plan->type == PLAN_PASSIVE));
	CheckItem(planMenu, miPlanTypeDefensive, (unit->plan->type == PLAN_DEFENSIVE));
	CheckItem(planMenu, miPlanTypeExploratory, (unit->plan->type == PLAN_EXPLORATORY));
	CheckItem(planMenu, miPlanTypeOffensive, (unit->plan->type == PLAN_OFFENSIVE));
	CheckItem(planMenu, miPlanTypeColonizing, (unit->plan->type == PLAN_COLONIZING));
	CheckItem(planMenu, miPlanTypeImproving, (unit->plan->type == PLAN_IMPROVING));
	CheckItem(planMenu, miPlanTypeRandom, (unit->plan->type == PLAN_RANDOM));

	/* Unhilite controls for disabled sides and when under active AI control. */
	if (disabled || ai_controlled(unit)) {
		GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		/* Hide the runlength Edit field. */
		HideDItem(win, diCityBuildEdit);
		HideDItem(win, diCityBuildTimes);
		/* Update AI control info. */
		GetDItem(win, diCityStats, NULL, &itemhandle, NULL);
		strcpy(buf, "Under ");
		strcat(buf, side->player->aitypename);
		strcat(buf, " control");
		c2p(buf, pname);
		SetIText(itemhandle, pname);
	} else {
		/* Hilite controls. */
		GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 0); 
		GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 0); 
		GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 0); 
		GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 0); 
		GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 0); 
		GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 0); 
		GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 0); 
		/* Show the runlength Edit field. */
		ShowDItem(win, diCityBuildEdit);
		ShowDItem(win, diCityBuildTimes);
		/* Update AI control info. */
		GetDItem(win, diCityStats, NULL, &itemhandle, NULL);
		strcpy(buf, "Under manual control");
		c2p(buf, pname);
		SetIText(itemhandle, pname);
	}
	
	/* Always unhilite the autobuild radio button for normal units. */
	GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
	if (!acp_indep(unit)) {
		HiliteControl((ControlHandle) itemhandle, 255); 
		SetCtlValue((ControlHandle) itemhandle, FALSE);
	}	
	/* Always select the manual radio button for normal units. */
	GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
	if (!acp_indep(unit)) {
		SetCtlValue((ControlHandle) itemhandle, TRUE);
	}	

	/* Draw all the other stuff. */
	draw_advanced_content(win, unit);
	DrawDialog(win);

	SetCursor(&QD(arrow));
	draw_default_button(win, OkButton);
}

static void
copy_rect_from_gworld(WindowPtr win, Map *map, Rect cityrect, Rect itemrect)
{
	Rect	portrect = map->gworldPortPtr->portRect;
	Rect	destrect = itemrect;
	Rect	sourcerect;
	
	/* Clip sourcerect to portrect. */
	SectRect(&cityrect, &portrect, &sourcerect);

	/* Clip destrect so that it will match sourcerect. */ 
	destrect.top 	+= max(0, portrect.top - cityrect.top);
	destrect.bottom 	-= max(0, cityrect.bottom - portrect.bottom);
	destrect.left 	+= max(0, portrect.left - cityrect.left);
	destrect.right 	-= max(0, cityrect.right - portrect.right);

	/* Copy from the map gworld. */
	LockPixels(GetGWorldPixMap(map->gworldPortPtr));
	CopyBits(  &((GrafPtr) map->gworldPortPtr)->portBits,
			&((GrafPtr) win)->portBits,
			&sourcerect, &destrect, srcCopy, NULL  );
	UnlockPixels(GetGWorldPixMap(map->gworldPortPtr));
}		

void
draw_advanced_content(DialogPtr win, Unit *unit)
{
	int			occupants[MAX_DISPLAYED_OCCS] = {0};
	char			cname[32], sname[32], buf[BUFSIZE];
	RGBColor 		tmpcolor, oldBack;
	Str255 		pname, pnumber;
	Handle 		itemhandle;  
	CIconHandle 	cicnhandle;
	Rect			itemrect, tmprect;
	Unit			*unit2;
	int			i, m; 

	/* Draw the unit icon. */
	GetDItem(win, diCityIcon, NULL, NULL, &itemrect);
	draw_unit_image(win, itemrect.left, itemrect.top, 
				itemrect.right - itemrect.left, itemrect.bottom - itemrect.top,
				unit->type, side_number(unit->side), 
				(frontmap ? frontmap->sidecolors : default_sidecolors), 
				0, !completed(unit),
				(frontmap ? frontmap->draw_emblems : default_draw_emblems));
	/* Draw unit size on top of the icon. */
	if (default_drawsizes) 
		draw_unit_size(unit, win, itemrect.left, itemrect.top,
				itemrect.right - itemrect.left, itemrect.bottom - itemrect.top);

	/* Draw the unit's name, type and side. */
	GetDItem(win, diCityName, NULL, &itemhandle, NULL);
	if (indep(unit))
		strcpy(buf, "Independent");
	else	strcpy(buf, side_adjective(unit->side));
	strcat(buf, " ");
	strcat(buf, u_type_name(unit->type));
	strcat(buf, " ");
	strcat(buf, unit->name);
	c2p(buf, pname);
	SetIText(itemhandle, pname);

	/* Draw the unit's size. */
	GetDItem(win, diCitySize, NULL, &itemhandle, NULL);
	strcpy(buf, "Size : ");
	NumToString(unit->size, pnumber);
	p2c(pnumber, cname);
	strcat(buf, cname);
	c2p(buf, pname);
	SetIText(itemhandle, pname);

	/* Draw the unit's transport if it exists (unlikely for an advanced unit but
	theoretically possible depending on game design). */
	if (unit->transport) {
		unit2 = unit->transport;

		/* Draw the transport icon. */
		GetDItem(win, diCityTransportIcon, NULL, NULL, &itemrect);
		draw_unit_image(win, itemrect.left, itemrect.top, 
					itemrect.right - itemrect.left, itemrect.bottom - itemrect.top,
					unit2->type, side_number(unit2->side), 
					(frontmap ? frontmap->sidecolors : default_sidecolors), 
					0, !completed(unit2),
					(frontmap ? frontmap->draw_emblems : default_draw_emblems));
		/* Draw transport size on top of the icon. */
		if (default_drawsizes) 
			draw_unit_size(unit2, win, itemrect.left, itemrect.top,
					itemrect.right - itemrect.left, itemrect.bottom - itemrect.top);

		/* Draw the "Inside:" text. */
		GetDItem(win, diCityTransportText, NULL, &itemhandle, NULL);
		strcpy(buf, "Inside:");
		c2p(buf, pname);
		SetIText(itemhandle, pname);

		/* Draw the transport's name. */
		GetDItem(win, diCityTransportName, NULL, &itemhandle, NULL);
		strcpy(buf, short_unit_handle(unit2));
		c2p(buf, pname);
		SetIText(itemhandle, pname);
	}
	
	/* Draw AI control info. */
	GetDItem(win, diCityStats, NULL, &itemhandle, NULL);
	strcpy(buf, "Under ");
	if (indep(unit) && unit->plan->aicontrol)
		strcat(buf, "brainless");
	else if (ai_controlled(unit))
		strcat(buf, unit->side->player->aitypename);
	else 	strcat(buf, "manual"); 
	strcat(buf, " control");
	c2p(buf, pname);
	SetIText(itemhandle, pname);

	/* Draw cyan background for materials panel. */ 
	GetDItem(win, diCityMatPanel, NULL, NULL, &itemrect);
	tmpcolor.red = 0xBFFF;
	tmpcolor.green = 0xFFFF; 
	tmpcolor.blue = 0xFFFF;
	RGBForeColor(&tmpcolor);
	FillRect(&itemrect, QDPat(black));
	ForeColor(blackColor);
	FrameRect(&itemrect);
	
	/* Draw green background for facilities panel. */ 
	GetDItem(win, diCityFacPanel, NULL, NULL, &itemrect);
	tmpcolor.red = 0xBFFF;
	tmpcolor.green = 0xFFFF; 
	tmpcolor.blue = 0xBFFF;
	RGBForeColor(&tmpcolor);
	FillRect(&itemrect, QDPat(black));
	ForeColor(blackColor);
	FrameRect(&itemrect);

	/* Draw pink background for garrison panel. */ 
	GetDItem(win, diCityOccPanel, NULL, NULL, &itemrect);
	tmpcolor.red = 0xFFFF;
	tmpcolor.green = 0xBFFF; 
	tmpcolor.blue = 0xFFFF; 
	RGBForeColor(&tmpcolor);
	FillRect(&itemrect, QDPat(black));
	ForeColor(blackColor);
	FrameRect(&itemrect);

	/* Plot material statistics headings. */
	GetDItem(win, diCityMaterial, NULL, &itemhandle, NULL);
	SetIText(itemhandle, "\pMaterial");
	GetDItem(win, diCityProduction, NULL, &itemhandle, NULL);
	SetIText(itemhandle, "\pProduction");
	GetDItem(win, diCitySupply, NULL, &itemhandle, NULL);
	SetIText(itemhandle, "\pSupply");
	GetDItem(win, diCityTreasury, NULL, &itemhandle, NULL);
	SetIText(itemhandle, "\pTreasury");

	/* Draw statistics for material types 0 to 3. */
	for (m = 0; m < min(4, nummtypes); m++) {
		if (m_resource_icon(m)) {
			/* Plot material icon. */
			GetDItem(win, diCityMatBase + (5 * (m + 1)), NULL, NULL, &itemrect);
			cicnhandle = GetCIcon(LARGE_CICNS + 10 * m_resource_icon(m) + 6);
			PlotCIcon(&itemrect, cicnhandle);
			DisposeCIcon(cicnhandle);
		}
		/* Plot material name. */
		GetDItem(win, diCityMatBase + (5 * (m + 1)) + 1, NULL, &itemhandle, NULL);
		c2p(mtypes[m].name, pname);
		SetIText(itemhandle, pname);
		/* Plot material production. */
		GetDItem(win, diCityMatBase + (5 * (m + 1)) + 2, NULL, &itemhandle, NULL);
		NumToString(unit->production[m], pnumber);
		SetIText(itemhandle, pnumber);
		/* Plot material supply. */
		GetDItem(win, diCityMatBase + (5 * (m + 1)) + 3, NULL, &itemhandle, NULL);
		NumToString(unit->supply[m], pnumber);
		SetIText(itemhandle, pnumber);
		/* Plot global treasury supply. */
		GetDItem(win, diCityMatBase + (5 * (m + 1)) + 4, NULL, &itemhandle, NULL);
		if (unit->side && m_treasury(m))
			NumToString(unit->side->treasury[m], pnumber);
		else	NumToString(0, pnumber);
		SetIText(itemhandle, pnumber);
	}

	/* Draw construction status. */
	if (unit->plan && unit->plan->tasks && unit->plan->tasks->type == TASK_BUILD) {
		GetDItem(win, diCityBuildStatus, NULL, &itemhandle, NULL);

		/* Print cps of current unit if it exists, else print "0". */
	    	if (find_unit(unit->plan->tasks->args[1]) != NULL) {
			 NumToString((find_unit(unit->plan->tasks->args[1]))->cp, pnumber);
			 p2c(pnumber, cname);
		} else	 strcpy(cname, "0");

		strcat(cname, " / ");
		NumToString(u_cp(unit->plan->tasks->args[0]), pnumber);
		p2c(pnumber, sname);
		strcat(cname, sname);
		
		/* Also print run progress. */
		if (unit->plan->tasks->args[3] > 1) {
			strcat(cname, "   ( ");
			NumToString(unit->plan->tasks->args[2] + 1, pnumber);
			p2c(pnumber, sname);
			strcat(cname, sname);
			strcat(cname, " of ");
			NumToString(unit->plan->tasks->args[3], pnumber);
			p2c(pnumber, sname);
			strcat(cname, sname);
			strcat(cname, " )");			
		}			
		c2p(cname, pnumber);
		SetIText(itemhandle, pnumber);
	}

	/* Draw research status. */
	if (unit->curadvance > 0 && unit->side) { 
		GetDItem(win, diCityResearchStatus, NULL, &itemhandle, NULL);
		NumToString(unit->side->advance[unit->curadvance], pnumber);
		p2c(pnumber, cname);
		strcat(cname, " / ");
		NumToString(a_rp(unit->curadvance), pnumber);
		p2c(pnumber, sname);
		strcat(cname, sname);
		c2p(cname, pnumber);
		SetIText(itemhandle, pnumber);
	}
	/* Sort the occupants. First load facilities. */
	i = 0;
	for_all_occupants(unit, unit2)
		if (alive(unit2) && u_facility(unit2->type))
			occupants[i++] = unit2->id;

	/* Move to the end of the facilities list. */
	i = MAX_DISPLAYED_FACS;

	/* Then load other units. */
	for_all_occupants(unit, unit2)
		if (alive(unit2) &! u_facility(unit2->type))
			occupants[i++] = unit2->id;

	/* Save the old Dialog background color. */
	oldBack = ((CGrafPtr) win)->rgbBkColor;
	BackColor(whiteColor);

	/* Draw all facilities and other occupants. */
	for (i = 0; i < MAX_DISPLAYED_OCCS; i++) {
		unit2 = find_unit(occupants[i]);
		if (unit2) {
			/* First plot each unit icon. */
			GetDItem(win, diCityOccBase + 2 * i, 0, 0, &itemrect);
			draw_unit_image(win, itemrect.left, itemrect.top,
						itemrect.right - itemrect.left, itemrect.bottom - itemrect.top,
						unit2->type, side_number(unit2->side), 
						(frontmap ? frontmap->sidecolors : default_sidecolors), 
						0, !completed(unit2),
						(frontmap ? frontmap->draw_emblems : default_draw_emblems));
			/* Then plot each unit name. */
			GetDItem(win, diCityOccBase + 2 * i + 1, NULL, &itemhandle, NULL);
			if (unit2->name) {
			   	strcpy(buf, u_type_name(unit2->type));
			    	strcat(buf, " ");
			    	strcat(buf, unit2->name);
			/* Skip the unit number for facilities. */
			} else if (unit2->number > 0 &! u_facility(unit2->type)) {
			   	NumToString(unit2->number, pnumber);
				p2c(pnumber, cname);
		 	   	strcpy(buf, cname);
		 	    	strcat(buf, ordinal_suffix(unit2->number));
			    	strcat(buf, " ");
		 	    	strcat(buf, u_type_name(unit2->type));
			} else	strcpy(buf, u_type_name(unit2->type));

			/* Write out cps for each unit if debugging. */
			if (Debug || DebugG || DebugM) {
				strcat(buf, " ");
				NumToString(unit2->cp, pnumber);
				p2c(pnumber, cname);
				strcat(buf, cname);
				strcat(buf, "/");
				NumToString(u_cp(unit2->type), pnumber);
				p2c(pnumber, cname);
				strcat(buf, cname);
			}
			c2p(buf, pname);
			SetIText(itemhandle, pname);
		} else {
			GetDItem(win, diCityOccBase + 2 * i + 1, NULL, &itemhandle, NULL);
			c2p("", pname);
			SetIText(itemhandle, pname);
		}
	}
	/* Restore dialog background. */
	RGBBackColor(&oldBack);
}

/* 
	Draw the size number of a city on top of its unit icon. 
*/

void
draw_unit_size(Unit *unit, WindowPtr win, int sx, int sy, int sw, int sh)
{
	UnitCloseup	*closeup;
	Map 			*map;
	List			*list;
	Str255  		pnumber;
	short 		curfont, cursize, curstyle;
	int			e;

	/* Filter out very small images. */
	if (sw < 16)
		return; 
	/* NULL pointer check. */
	if (!unit || !unit->size)
		return;
		 
	/* Find current closeup, map or list. */
	closeup = unit_closeup_from_window(win);
	map = map_from_window(win);
	list = list_from_window(win);
	e = side_number(unit->side);
		
	/* Save the current font. */
	curfont = QD(thePort)->txFont;
	cursize = QD(thePort)->txSize;
	curstyle = QD(thePort)->txFace;

	/* Default colors used in the absence of defined side colors. */
	RGBForeColor(&forecolor);
	RGBBackColor(&maskcolor);

		/* Load city size into pascal string. */
		NumToString(unit->size, pnumber);

		/* Use Chicago. */
		TextFont(0);

		/* Use fixed optimized fonts if asked to do so. */
	if ((map && map->optimize_fonts)
	    || (list && default_optimize_fonts)
	    || (closeup && default_optimize_fonts)) {

			if (sh < 32)
				TextSize(9);
			else if (sh < 64)
				TextSize(12);
			else if (sh < 128)
				TextSize(24);
			else 	TextSize(48);

		/* Else scale text sizes according to formula. */
		} else TextSize(min(max(9, sh/2), 48));

		/* First use the mask color to plot the core. */
	if (((map && map->sidecolors) 
	    || (list && list->sidecolors)
	    || (closeup && default_sidecolors)) 
		    && icon_mask_color[e]) {
		  	RGBForeColor(&(get_sideColor(e, icon_mask_color[e])));
		} else	RGBForeColor(&maskcolor);
		TextFace(bold + condense);

		/* Handtuned setting for 16 x 16. */
		if (sh < 32) MoveTo(sx + 1, sy + 10);
		else MoveTo(sx + sw/5, sy + 3 * sh/5);
		DrawString(pnumber);

		/* Then use the main color to plot the outline. */
	if (((map && map->sidecolors) 
	    || (list && list->sidecolors) 
	    || (closeup && default_sidecolors)) 
		    && main_icon_color[e]) {
		  	RGBForeColor(&(get_sideColor(e, main_icon_color[e])));
		} else	RGBForeColor(&forecolor);
		TextFace(shadow + condense);

		/* Handtuned setting for 16 x 16. */
		if (sh < 32) MoveTo(sx + 1, sy + 10);
		else MoveTo(sx + sw/5, sy + 3 * sh/5);
		DrawString(pnumber);

	/* Restore the current font. */
	TextFont(curfont);
	TextSize(cursize);
	TextFace(curstyle);

	/* Restore colors. */
	ForeColor(blackColor);
	BackColor(whiteColor);
}

/* 
	Draw landuse in all cells near unit. Assumes the port is already set. 
*/

static void
draw_landuse_near_unit(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int near)
{
	int 			mapsx, mapsy, winsx, winsy, sx, sy, x, y;
	Rect 			imagerect;
	Str255		pnumber;
	RgnHandle 		tmprgn;

	/* Return if landuse is not defined. */
	if (!user_defined())
		return;

	/* Clip to itemrect. */
	tmprgn = NewRgn();
	GetClip(tmprgn);
	ClipRect(&itemrect);

	/* Find win coordinates of unit (city) cell. */ 
	winsx = (itemrect.right + itemrect.left) / 2 - map->vp->hw/2;
	winsy = (itemrect.bottom + itemrect.top) / 2 - map->vp->hh/2;
	
	/* Find map coordinates of unit (city) cell. */
	xform(map, unit->x, unit->y, &mapsx, &mapsy);
	
	/* Zero the number of used cells. */
	unit->usedcells = 0;
	/* Go through all cells within "near" steps from unit. */
	for_all_cells_within_range(unit->x, unit->y, near, x, y) {
		/* Skip if cell is unused. */
		if (user_at(x, y) == NOUSER)
			continue;
		/* Skip if cell is outside area. */
		if (!inside_area(x, y))
			continue;
		/* Skip if the cell is not visible to the unit's side. */
		if (!terrain_visible(unit->side, x, y))
			continue;
		/* Recalculate the number of used cells. */
		if (user_at(x, y) == unit->id)
			unit->usedcells += 1;
		/* Find map coordinates of cell. */
		xform(map, x, y, &sx, &sy);
		/* Add win-map offset. */
		sx += winsx - mapsx;
		sy += winsy - mapsy;
		/* Adjust to unit part of cell. */
		sx += (map->vp->hw - map->vp->uw) / 2;  
		sy += (map->vp->hh - map->vp->uh) / 2;
		/* Set imagerect. */
		SetRect(&imagerect, sx, sy, sx + map->vp->uw, sy + map->vp->uh);
		/* Draw a colored square if cell is used by another unit. */
		if (find_unit(user_at(x, y)) && user_at(x, y) != unit->id) {
			int e = side_number(find_unit(user_at(x, y))->side);
			PenSize(2, 2);
			RGBForeColor(&(get_sideColor(e, main_icon_color[e])));
			FrameRect(&imagerect);
			PenNormal();
			ForeColor(blackColor);
		/* Else plot the resource cicns for that cell. */
		} else plot_resource_cicns(map, imagerect, x, y);
	}
	/* Restore old clip. */
	SetClip(tmprgn);
	DisposeRgn(tmprgn);
}

/* 
	Plot resource cicns in single cell. Assumes that the port already is set.
	Max 4 material types can be displayed at the same time. The icon used for
	each material type is determined by the mtype property m_resource_icon. 
*/

void
plot_resource_cicns(Map *map, Rect plotrect, int x, int y)
{
	CIconHandle cicnhandle;
	int m, n, i;

	n = (map->vp->uh > 16 ? LARGE_CICNS : SMALL_CICNS);
	for_all_material_types(m) {
		i = m_resource_icon(m);
		if (i) {
			cicnhandle = GetCIcon(n + 10 * i + production_at(x, y, m));
			PlotCIcon(&plotrect, cicnhandle);
			DisposeCIcon(cicnhandle);
		}
	}
}

int
do_mouse_down_unit_closeup(UnitCloseup *unitcloseup, Point mouse, int mods)
{
	int	count = 0, occs = 0, occx = 0, occy = 0;
	DialogPtr win = unitcloseup->window;
	Unit	*unit, *unit2;
	Rect	tmprect;
	Map	*map;
	Plan	*plan;
	Task	*task;
	short ditem;

	/* Check if a dialog item was hit and branch to dialog handling code in that case.
	This makes it possible to avoid using the Dialog Manager. */
	ditem = FindDItem(win, mouse) + 1;	/* FindDItem returns (ditem -1)! */
	if (ditem > 0) {
		/* First undo previous conversion to locals by do_mouse_down. */
		LocalToGlobal(&mouse);
		hit_closeup_dialog(win, ditem, mouse, mods);
		return;
	}
	/* Handle clicks on transport or occupants in simple closeups. */
	unit = unitcloseup->unit;
	if (u_advanced(unit->type) != TRUE) {
		occy = 60;
		if (unit->transport) {
			SetRect(&tmprect, 0, occy - 16, closeupwinwid, occy + 4);
			if (PtInRect(mouse, &tmprect)) {			
				/* Close the current closeup unless ctrl-clicking. */
				if ((mods & controlKey) == 0) {
					close_window(win);
				}
				/* Show the transport's closeup. */
				show_unit_closeup(unit->transport);			
				return;
			}
		}
		if (unit->occupant) {
			occy +=  closeup_spacing * (nummrows[unit->type] + 1);
			if (unit->plan) {
				occy += closeup_spacing;
				if (unit->plan->maingoal) {
					occy += closeup_spacing;
				}
				if (unit->plan->formation) {
					occy += closeup_spacing;
				}
				if (unit->plan->tasks) {
					for_all_tasks(unit->plan, task) {
				  		occy += closeup_spacing;
					}
				}
			}
			/* Count the occupants. */
			for_all_occupants(unit, unit2) {	
				occs += 1;
			}
			for_all_occupants(unit, unit2) {
				count += 1;	
				SetRect(&tmprect, occx, occy, occx + closeupwinwid / 2, occy +22);
				if (PtInRect(mouse, &tmprect)) {			
					/* Close the current closeup unless ctrl-clicking. */
					if ((mods & controlKey) == 0) {
						close_window(win);
					}
					/* Show closeup of occupant that was clicked on. */
					show_unit_closeup(unit2);			
					return;
				}
				occy += 22;
				/* Check if we should switch to second column. */
				if (2 * count == occs || 2 * count == occs + 1) {
					occx += closeupwinwid / 2;
					occy -= count * 22;	
				}
			}
		}
	}
	/* The click was in an empty part of the closeup window. We call
	show_unit_closeup in order to select the unit on the frontmap and 
	make it the current unit. */
	show_unit_closeup(unit);
}

TEHandle runlength_text = nil;

/* This function examines keystrokes destined for closeup dialogs. 
It returns TRUE for keys that got handled, and FALSE for all other keys. */ 

int 
do_key_down_closeup(UnitCloseup *closeup, char key, char code, int mods)
{
	Point mouse = {0, 0};		/* Dummy variable. */
	int	advanced;
	
	advanced = u_advanced(closeup->unit->type);

	/* We hit the Enter or Return key. */
	if (key == 0x4C || key == 0x24) {
		if (advanced)
			hit_closeup_dialog(closeup->window, OkButton, mouse, mods);
		else	close_window(closeup->window);
		return TRUE;
	/* We hit the Escape key. */
	} else if (key == 0x35) {
		close_window(closeup->window);
		return TRUE;
	/* 0-9 should be forwarded to the dialog's Edit text field instead of 
	being interpreted as command prefixes. Also forward backspace. */
	} else if (advanced 
		    && (between(0x12, key, 0x17)	/* Keyboard 1-6 */ 
		    	|| key == 0x19				/* Keyboard 9 */ 
		    	|| key == 0x1A				/* Keyboard 7 */ 
		    	|| key == 0x1C				/* Keyboard 8 */ 
		    	|| key == 0x1D				/* Keyboard 0 */ 
		    	|| key == 0x33)) {			/* Backspace   */ 

	/* The Dialog Manager no longer handles Edit Text fields for us. */
		TEKey(code, ((DialogPeek) closeup->window)->textH);
		return TRUE;
	}
	return FALSE;
}

/* Second half of the old modal city_dialog. */

void
hit_closeup_dialog(WindowPtr win, int ditem, Point mouse, int mods)
{
	int			run =0, width, height, downsx, downsy, downx, downy, sx, sy, m;
	short			aicontrol, autoplan, autoresearch, autobuild, plantype, advance, build;
	short 		mitem, done = FALSE, disabled = TRUE, i, u, u2, a;
	char			buf[32], cname[32], uname[32], sname[32];
	int			occupants[MAX_DISPLAYED_OCCS] = {0};
	MenuHandle	buildMenu, advanceMenu;
	Str255 		pname, pnumber;
	UnitCloseup	*unitcloseup;
	Unit 			*unit, *unit2;
	HelpNode 		*helpnode;
	Handle 		itemhandle;  
	Rect 			itemrect;
	GrafPtr 		oldport;
	Map			*map;
	Side			*side;

	unitcloseup = unit_closeup_from_window(win);
	unit = unitcloseup->unit;
	side = unit->side;
	map = frontmap;
	buildMenu = build_construction_menu(unit);
	advanceMenu = build_research_menu(side);

	/* Give controlling side (including debuggers 
	and designers) full control. */
	if (side_controls_unit(dside, unit))
		disabled = FALSE;

	/* Sort the occupants. First load facilities. */
	i = 0;
	for_all_occupants(unit, unit2)
		if (alive(unit2) && u_facility(unit2->type))
			occupants[i++] = unit2->id;

	/* Move to the end of the facilities list. */
	i = MAX_DISPLAYED_FACS;

	/* Then load other units. */
	for_all_occupants(unit, unit2)
		if (alive(unit2) &! u_facility(unit2->type))
			occupants[i++] = unit2->id;

	GetPort(&oldport);	
	SetPort(win);
	DrawDialog(win);

	switch (ditem) {

	case 	diCityMap:		/* Find the cell that was clicked. */
		if (disabled	/* Don't allow other sides to change things. */	
		    || ai_controlled(unit))
			break;
		GetDItem(win, diCityMap, NULL, NULL, &itemrect);
		width = itemrect.right - itemrect.left;
		height = itemrect.bottom - itemrect.top;
		/* Find the city position. */
		xform(map, unit->x, unit->y, &sx, &sy);
		GlobalToLocal(&mouse);
		downsx = mouse.h - itemrect.left + sx + map->vp->hw/2 - width/2;
		downsy = mouse.v - itemrect.top + sy + map->vp->hh/2 - height/2;
		m_nearest_cell(map, downsx, downsy, &downx, &downy);
		if (!inside_area(downx, downy)) 
			break;
		if (!cell_is_within_reach(unit, downx, downy))
			break;
		/* Toggle landuse on or off for the cell. */
		toggle_landuse_one_cell(map, win, itemrect, unit, downx, downy);
	break;

	case diCityAICheck: 	/* Toggle AI control checkbox. */
		if (disabled)	/* Don't allow other sides to change things. */	
			break;
		GetDItem(win, ditem, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
		/* This is the only control we want to set as soon as it is clicked. */
		net_set_unit_ai_control(side, unit, GetCtlValue((ControlHandle) itemhandle), FALSE);
		/* Update the dialog to reflect altered control hiliting. */
		draw_unit_closeup(unitcloseup);
	break;

	case diCityPlanPopup: 		/* Handle Popup menus. */
	case diCityAdvancePopup:
	case diCityBuildPopup: 
		if (disabled || ai_controlled(unit))
			break;
		GetDItem(win, ditem, NULL, &itemhandle, NULL);
		/* This is no longer automatically done by the dialog manager. */
		TrackControl((ControlHandle) itemhandle, mouse, (void *) -1);
		/* Make sure the new menu selection is drawn. */
		DrawControls(win);
	break;

	case diCityPlanCheck: 		/* Toggle autoplan checkbox. */
		if (disabled || ai_controlled(unit))
			break;
		GetDItem(win, ditem, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
	break;

	case diCityAdvanceCheck: 	/* Toggle autoresearch checkbox. */
		if (disabled || ai_controlled(unit))
			break;
		GetDItem(win, ditem, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
	break;

	case diCityBuildCheck: 	/* Switch autobuild button. */
		if (disabled || ai_controlled(unit))
			break;
		GetDItem(win, ditem, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
		GetDItem(win, diCityBuildRepeat, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
	break;

	case diCityBuildRepeat: 	/* Switch manual build button. */
		if (disabled || ai_controlled(unit))
			break;
		GetDItem(win, ditem, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
		GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
		SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
	break;

	case 	OkButton:
		/* Don't allow others to save any changes. */
		if (disabled || ai_controlled(unit)) {
			done = TRUE;
			break;
		}
		/* Get the AI control state. */
		GetDItem(win, diCityAICheck, NULL, &itemhandle, NULL);
		aicontrol = GetCtlValue((ControlHandle) itemhandle);

		/* Get the autoplan state. */
		GetDItem(win, diCityPlanCheck, NULL, &itemhandle, NULL);
		autoplan = GetCtlValue((ControlHandle) itemhandle);

		/* Get the autoresearch state. */
		GetDItem(win, diCityAdvanceCheck, NULL, &itemhandle, NULL);
		autoresearch = GetCtlValue((ControlHandle) itemhandle);

		/* Get the autobuild state. */
		GetDItem(win, diCityBuildCheck, NULL, &itemhandle, NULL);
		autobuild = GetCtlValue((ControlHandle) itemhandle);

		/* Get the selected plan type. */
		GetDItem(win, diCityPlanPopup, NULL, &itemhandle, NULL);
		plantype = GetCtlValue((ControlHandle) itemhandle) - 1;

		/* Get the selected research task. */
		GetDItem(win, diCityAdvancePopup, NULL, &itemhandle, NULL);
		mitem = GetCtlValue((ControlHandle) itemhandle);
		GetItem(advanceMenu, mitem, pname);
		p2c(pname, sname);
		advance = NOADVANCE;
		for_all_advance_types(a) {
			if (strcmp(a_type_name(a), sname) == NULL) {
				advance = a;
				break;
			}
		}

		/* Always set runlength to doctrine if on autobuild. */
		if (unit->autobuild || ai_controlled(unit)) {
			run = construction_run_doctrine(unit, 0);
		} else {					
			/* Else get the runlength from its text field. */
			GetDItem(win, diCityBuildEdit, NULL, &itemhandle, NULL);
        			GetIText(itemhandle, pname);
			StringToNum(pname, (long *) &run);
			run = min(max(run, 0), CLEAR_AGENDA);
		}

		/* Get the selected build task. */
		GetDItem(win, diCityBuildPopup, NULL, &itemhandle, NULL);
		mitem = GetCtlValue((ControlHandle) itemhandle);
		GetItem(buildMenu, mitem, pname);
		p2c(pname, uname);
		build = NONUTYPE;
		for_all_unit_types(u) {
			/* Find the utype that was selected in the menu. */
			if (strcmp(u_type_name(u), uname) == NULL) {
				build = u;
				break;				
			}
		}

		/* We need to do this last of all since the net_set functions call
		update_unit_display which redraws the closeup itself. The first
		call to a net_set function therefore resets all popup menus to the
		initial values, thus destroying the settings we want to save. */

		net_set_unit_ai_control(side, unit, aicontrol, FALSE);
		net_set_unit_autoplan(side, unit, autoplan);
		net_set_unit_autoresearch(side, unit, autoresearch);
		net_set_unit_autobuild(side, unit, autobuild);
		net_set_unit_plan_type(side, unit, plantype);
		net_set_unit_curadvance(side, unit, advance);
		if (build == NONUTYPE) {
			net_clear_task_agenda(side, unit);
			 if (strcmp(uname, "Asleep") == NULL) {
				net_set_unit_asleep(side, unit, TRUE, FALSE);
			} else if (strcmp(uname, "Skip Turn") == NULL) {
				net_set_unit_reserve(side, unit, TRUE, FALSE);
			}
		} else	net_set_build_task(unit, build, run, 0, 0);

		done = TRUE;
	break;
	
	case CancelButton:
		done = TRUE;
	break;

	case HelpButton:		
		helpnode = find_help_node(first_help_node, u_type_name(unit->type));
		if (helpnode)
			show_help_window(helpnode);
		else 	beep();
	break;
		
	default:
		/* Open new closeup if click was on occupant icon or text . */
		if (between(diCityOccBase, ditem, diCityOccBase + 2 * MAX_DISPLAYED_OCCS - 1)) {
			unit2 = find_unit(occupants[(ditem - diCityOccBase) / 2]);
			if (unit2) {
				/* Close the old closeup unless ctrl-clicking. */
				if ((mods & controlKey) == 0) {
					close_window(win);
				}
				show_unit_closeup(unit2);
			}
		}
		/* Open new closeup if click was on the transport's icon or text. */
		if (unit->transport
		    && (ditem == diCityTransportText
		        || ditem == diCityTransportIcon
		        || ditem == diCityTransportName)) {
			/* Close the old closeup unless ctrl-clicking. */
			if ((mods & controlKey) == 0) {
				close_window(win);
			}
			show_unit_closeup(unit->transport);
		}
	break;
	
	}

	if (done)
	    close_window(win);

	/* Restore old port. */
	SetPort(oldport);
}

/* 
	Toggle landuse for a single cell. Assumes the port already is set. 
*/

void
toggle_landuse_one_cell(Map *map, WindowPtr win, Rect itemrect, Unit *unit, int x, int y)
{
	int 			mapsx, mapsy, winsx, winsy, sx, sy, nearx, neary;
	Rect 			maprect, winrect;
	RgnHandle		tmprgn;

	/* Return if landuse is undefined. */
	if (!user_defined())
		return;
	/* Return if the cell is not visible to this side. */
	if (!terrain_visible(unit->side, x, y))
		return;
	/* Return if the cell is used by another unit. */
	if (user_at(x, y) != NOUSER && user_at(x, y) != unit->id)
		return;
	/* Return if using maxcells and we are trying to add one more. */
	if (unit->usedcells >= unit->maxcells && user_at(x, y) != unit->id)
		return;
	/* Return if independents or untrusted side has a unit in the cell. */
	if (unit_at(x, y) != NULL) {
		Unit *unit2;
		for_all_stack(x, y, unit2)
			if (!trusted_side(unit->side, unit2->side))
				return;
	}
	/* Toggle landuse by unit either on or off for the cell. */
	if (user_at(x, y) == NOUSER) {
		set_user_at(x, y, unit->id);
		unit->usedcells += 1;
	} else if (user_at(x, y) == unit->id) {
		set_user_at(x, y, NOUSER);
		unit->usedcells -= 1;
	} else return;
	
	/* Clip to itemrect. */
	tmprgn = NewRgn();
	GetClip(tmprgn);
	ClipRect(&itemrect);

	/* Find the cell position. */
	xform(map, x, y, &sx, &sy);

	/* Adjust to unit part of cell. */
	sx += (map->vp->hw - map->vp->uw) / 2;  
	sy += (map->vp->hh - map->vp->uh) / 2;

	/* Find cell rect position in the map. */
	SetRect(&maprect, sx, sy, sx + map->vp->uw, sy + map->vp->uh);

	/* Find cell rect position in the offscreen gworld. */
	OffsetRect(&maprect, map->offsetx + map->bufx - map->conw, 
				        map->offsety + map->bufy - map->toph);

	/* Find win coordinates of unit (city) cell. */ 
	winsx = (itemrect.right + itemrect.left) / 2 - map->vp->hw/2;
	winsy = (itemrect.bottom + itemrect.top) / 2 - map->vp->hh/2;
	
	/* Find map coordinates of unit (city) cell. */
	xform(map, unit->x, unit->y, &mapsx, &mapsy);
	
	/* Add win-map offset. */
	sx += winsx - mapsx;
	sy += winsy - mapsy;

	/* Find cell rect position in the dialog window. */
	SetRect(&winrect, sx, sy, sx + map->vp->uw, sy + map->vp->uh);

	/* Copy cellrect from gworld to dialog window. */
	LockPixels(GetGWorldPixMap(map->gworldPortPtr));
	CopyBits(  &((GrafPtr) map->gworldPortPtr)->portBits,
			&((GrafPtr) win)->portBits,
			&maprect, &winrect, srcCopy, NULL  );
	UnlockPixels(GetGWorldPixMap(map->gworldPortPtr));

	/* Plot resource cicns on top if cell is used by city. */
	if (user_at(x, y) == unit->id) 
		plot_resource_cicns(map, winrect, x, y);

	/* Restore old clip. */
	SetClip(tmprgn);
	DisposeRgn(tmprgn);
}

void
destroy_unit_closeup(UnitCloseup *unitcloseup)
{
	UnitCloseup *unitcloseup2;
	Unit	*unit;
	Map	*map;

	unit = unitcloseup->unit;
	update_cell_display(dside, unit->x, unit->y, UPDATE_ALWAYS);
	if (unitcloseuplist == unitcloseup) {
		unitcloseuplist = unitcloseup->next;
	} else {
		for_all_unit_closeups(unitcloseup2) {
			if (unitcloseup2->next == unitcloseup) {
				unitcloseup2->next = unitcloseup->next;
			}
		}
	}
	/* (should destroy substructs) */
	free(unitcloseup);
}

DialogPtr buildwin = nil;

/* This is the top-level access to bring up the scores window, can be called
   anywhere, anytime. */

void
show_unit_build_dialog(Unit *unit)
{
	int 		mainwidth, mainheight;
	Rect		winRect;		
	Point		winUL;
	Point		winDR;
	GrafPtr 	oldport;
	Point		upoint;
	int 		sx, sy;

	/* Create the dialog if necessary. */
	if (buildwin == nil) {
		buildwin = GetNewDialog(dBuild, NULL, NULL);
		/* Don't show it until we have moved it. */
		HideTheWindow(buildwin);
	}
	if (frontmap) {
		/* Position the dialog next to the unit. */
		GetPort(&oldport);
		SetPort(frontmap->window);
		xform(frontmap, unit->x + 1, unit->y - 1, &sx, &sy);
		upoint.h = sx; 
		upoint.v = sy + (frontmap->vp->power < 5 ? frontmap->vp->hh : frontmap->vp->hh/2);
		LocalToGlobal(&upoint);
		MoveWindow(buildwin, upoint.h, upoint.v, true);

		/* Find the dialog's global coordinates. */
		SetPort(buildwin);
		winRect = buildwin->portRect;			
		winUL.h = winRect.left;
		winUL.v = winRect.top;
		winDR.h = winRect.right;
		winDR.v = winRect.bottom;
		LocalToGlobal(&winUL);			
		LocalToGlobal(&winDR);		

		/* Make sure it stays within the main screen. */
		get_main_screen_size(&mainwidth, &mainheight);
		if (winDR.h + 3 > mainwidth
		     || winUL.h < 3
		     || winDR.v + 3 > mainheight
		     || winUL.v < GetMBarHeight() + 3) {
			MoveWindow(buildwin, 
				max(3, min(winUL.h, winUL.h + mainwidth - winDR.h - 3)),
				max(GetMBarHeight() + 3, min(winUL.v, winUL.v + mainheight - winDR.v - 3)),
				false);
		}
		SetPort(oldport);
	}
	/* Mark the unit as buildwin_unit. */
	buildwin_unit = unit;
	MakeDialogFloat(buildwin);
	SelectTheWindow(buildwin);
	update_window(buildwin);
}

void
draw_unit_build_dialog(int force)
{
	char			cname[32], uname[32], sname[32], buf[BUFSIZE], buf3[BUFSIZE];
	short 		ditem, mitem, done = FALSE, i, m, d, u, u2;
	Unit			*unit = buildwin_unit;
	Task 			*lasttask = &(unit->plan->last_task);
	Task 			*task = unit->plan->tasks;
	Str255 		pname, pnumber;
	WindowPtr		win = buildwin;
	Handle 		itemhandle; 
	MenuHandle	buildMenu;
	GrafPtr 		oldport;
	int			run;

	/* Skip if we lack a display. */
	if (!active_display(dside))
		return;
	/* Blast the dialog if the unit is dead or has defected. */
	if (!in_play(unit)
	      || !side_controls_unit(dside, unit)) { 
		close_window(win);
		return;
	}
	GetPort(&oldport);
	SetPort(win);

	buildMenu = build_construction_menu(unit);

	/* Get the type name of unit that is being constructed, if any. */
	if (task 
	      && task->type == TASK_BUILD
	      && task->args[3] > 0
	      && unit->plan->tasks->args[2] < task->args[3]
	      && is_unit_type(task->args[0])) {
	   	strcpy(uname, u_type_name(task->args[0]));
	} else if (unit->plan->asleep)
		strcpy(uname, "Asleep");
	else if (unit->plan->reserve)
		strcpy(uname, "Skip Turn");
	else	strcpy(uname, u_type_name(favored_type(unit)));

	/* Set build popup to uname. */
	m = CountMItems(buildMenu);
	GetDItem(win, diBuildPopup, NULL, &itemhandle, NULL);
	SetCtlMax((ControlHandle) itemhandle, m);
	for (i = 1; i <= m; i++) {
		GetItem(buildMenu, i, pname);
		p2c(pname, cname);
		if (strcmp(uname, cname) != 0)
			continue;
		SetCtlValue((ControlHandle) itemhandle, i);
	}

	/* Check if a build task just was completed. */
	if (lasttask
	     && lasttask->type == TASK_BUILD
	     && unit->plan->last_task_outcome == TASK_IS_COMPLETE
	     && lasttask->args[3] > 0) {
		if (lasttask->args[3] > 1) {
	   		NumToString(lasttask->args[3], pnumber);
			p2c(pnumber, cname);
 	   		strcpy(uname, cname);
	    		strcat(uname, " ");
			strcat(uname, plural_form(u_type_name(lasttask->args[0])));
		} else strcpy(uname, u_type_name(lasttask->args[0]));
		strcpy(buf, unit->name ? unit->name : u_type_name(unit->type));
		strcat(buf, " completes ");
		strcat(buf, uname);
		strcat(buf, ". Pick a new task.");
	/* We are building something. */
	} else if (task
	     && task->type == TASK_BUILD
	     && task->args[3] > 0
	     && unit->plan->tasks->args[2] < task->args[3]) {
		if (lasttask->args[3] > 1) {
	   		NumToString(task->args[2] + 1, pnumber);
			p2c(pnumber, uname);
 	   		strcat(uname, ordinal_suffix(task->args[2] + 1));
 	   		strcat(uname, " of ");
	   		NumToString(task->args[3], pnumber);
			p2c(pnumber, cname);
 	   		strcat(uname, cname);
	    		strcat(uname, " ");
			strcat(uname, plural_form(u_type_name(task->args[0])));
		} else strcpy(uname, u_type_name(lasttask->args[0]));
		strcpy(buf, unit->name ? unit->name : u_type_name(unit->type));
		strcat(buf, " is building ");
		strcat(buf, uname);
		strcat(buf, ".\r");
		/* Print cps of current unit if it exists, else print "0". */
	    	if (find_unit(task->args[1]) != NULL) {
			NumToString((find_unit(task->args[1]))->cp, pnumber);
			p2c(pnumber, cname);
		} else	 strcpy(cname, "0");
		strcat(cname, " of ");
		NumToString(u_cp(task->args[0]), pnumber);
		p2c(pnumber, sname);
		strcat(cname, sname);
		strcat(cname, " points completed.");
		strcat(buf, cname);		
	/* We are not building anything at all. */
	} else {
		strcpy(buf, "The workers in ");
		strcat(buf, unit->name ? unit->name : u_type_name(unit->type));
		strcat(buf, " are idle. Pick a new task.");
	}
	/* Check if the text field needs an update. */
	GetDItem(win, diBuildText, NULL, &itemhandle, NULL);
	GetIText(itemhandle, pname);
	p2c(pname, buf3);
	if (strcmp(buf, buf3) != 0) {
		c2p(buf, pname);
		SetIText(itemhandle, pname);
		force = TRUE;
	}
	/* Set the autobuild radio button. */
	GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
	if (acp_indep(unit)) {
		HiliteControl((ControlHandle) itemhandle, 0); 
		SetCtlValue((ControlHandle) itemhandle, unit->autobuild);
	} else {
		HiliteControl((ControlHandle) itemhandle, 255); 
		SetCtlValue((ControlHandle) itemhandle, FALSE);
	}	
	/* Set the manual build radio button. */
	GetDItem(win, diBuildRepeat, NULL, &itemhandle, NULL);
	if (acp_indep(unit)) {
		SetCtlValue((ControlHandle) itemhandle, !unit->autobuild);
	} else {
		SetCtlValue((ControlHandle) itemhandle, TRUE);
	}	

	/* Draw the run in its textfield. */
	GetDItem(win, diBuildEdit, NULL, &itemhandle, NULL);
	if (task
	    && task->type == TASK_BUILD
	    && task->args[3] > 0)
		NumToString(task->args[3], pname);
	else	NumToString(construction_run_doctrine(unit, 0), pname);
	SetIText(itemhandle, pname);
	SelIText(win, diBuildEdit, 0, 32767);

	/* Unhilite all controls when under active AI control. */
	if (ai_controlled(unit)) {
		GetDItem(win, diBuildPopup, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diBuildRepeat, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		GetDItem(win, diBuildEdit, NULL, &itemhandle, NULL);
		HiliteControl((ControlHandle) itemhandle, 255); 
		/* Hide the runlength Edit field. */
		HideDItem(win, diBuildEdit);
		HideDItem(win, diBuildTimes);
	}
	
	if (force) {
		DrawDialog(win);
		draw_default_button(win, OkButton);
		MoveTo(10, 132);
		TextFont(small_font_id);
		TextSize(small_font_size);
		DrawString("\pHit Return to confirm and Escape to close.");
		TextFont(0);
		TextSize(0);
	}
	SetPort(oldport);
}

/* This function examines keystrokes destined for closeup dialogs. 
It returns TRUE for keys that got handled, and FALSE for all other keys. */ 

int 
do_key_down_build(char key, char code, int mods)
{
	Point mouse = {0, 0};		/* Dummy variable. */

	/* We hit the Enter or Return key. */
	if (key == 0x4C || key == 0x24) {
		hit_unit_build_dialog(OkButton, mouse, mods);
		return TRUE;
	/* We hit the Escape key. */
	} else if (key == 0x35) {
		close_window(buildwin);
		return TRUE;
	/* 0-9 should be forwarded to the dialog's Edit text field instead of 
	being interpreted as command prefixes. Also forward backspace. */
	} else if (between(0x12, key, 0x17)		/* Keyboard 1-6 */ 
		    	|| key == 0x19				/* Keyboard 9 */ 
		    	|| key == 0x1A				/* Keyboard 7 */ 
		    	|| key == 0x1C				/* Keyboard 8 */ 
		    	|| key == 0x1D				/* Keyboard 0 */ 
		    	|| key == 0x33) {			/* Backspace   */ 

	/* The Dialog Manager no longer handles Edit Text fields for us. */
		TEKey(code, ((DialogPeek) buildwin)->textH);
		return TRUE;
	}
	return FALSE;
}

int
do_mouse_down_build(Point mouse, int mods)
{
	int	count = 0, occs = 0, occx = 0, occy = 0;
	DialogPtr win = buildwin;
	Unit	*unit2;
	Rect	tmprect;
	Map	*map;
	Plan	*plan;
	Task	*task;
	short ditem;

	/* Check if a dialog item was hit and branch to dialog handling code in that case.
	This makes it possible to avoid using the Dialog Manager. */
	ditem = FindDItem(win, mouse) + 1;	/* FindDItem returns (ditem -1)! */
	if (ditem > 0) {
		/* First undo previous conversion to locals by do_mouse_down. */
		LocalToGlobal(&mouse);
		hit_unit_build_dialog(ditem, mouse, mods);
		return;
	}
}

void
hit_unit_build_dialog(int ditem, Point mouse, int mods)
{
	int			run =0, width, height, downsx, downsy, downx, downy, sx, sy, m;
	short			autobuild, plantype, build;
	short 		mitem, done = FALSE, disabled = TRUE, i, u, u2, a;
	char			buf[32], cname[32], uname[32], sname[32];
	MenuHandle	buildMenu;
	Str255 		pname, pnumber;
	UnitCloseup	*unitcloseup;
	Unit 			*unit, *unit2;
	HelpNode 		*helpnode;
	Handle 		itemhandle;  
	Rect 			itemrect;
	GrafPtr 		oldport;
	Map			*map;
	Side			*side;
	WindowPtr		win;
	
	win = buildwin;
	map = frontmap;
	unit = frontmap->curunit;
	side = unit->side;
	buildMenu = build_construction_menu(unit);

	GetPort(&oldport);	
	SetPort(win);
	DrawDialog(win);

	switch (ditem) {

		case diBuildAuto: 	/* Toggle autobuild and manual build buttons. */
		  if (!acp_indep(unit) || ai_controlled(unit))
			    break;
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
			GetDItem(win, diBuildRepeat, NULL, &itemhandle, NULL);
			SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
			break;

		case diBuildRepeat: 	/* Toggle autobuild and manual build buttons. */
		  if (!acp_indep(unit) || ai_controlled(unit))
			    break;
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
			GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
			SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
			break;

		case 	diBuildPopup: 	/* Find selected unit type. */
			if (ai_controlled(unit))
			    break;
			GetDItem(win, ditem, NULL, &itemhandle, NULL);
			/* This is no longer automatically done by the dialog manager. */
			TrackControl((ControlHandle) itemhandle, mouse, (void *) -1);
			/* Make sure the new menu selection is drawn. */
			DrawControls(win);
			mitem = GetCtlValue((ControlHandle) itemhandle);
			GetItem(buildMenu, mitem, pname);
			p2c(pname, uname);
			/* If a real choice was made, we exit the dialog through this 
			recursive call. */
			if (strcmp(uname, "Idle") != NULL) {
				hit_unit_build_dialog(OkButton, mouse, mods);
				return;
			}
	    		break;

		/* The OK and Cancel buttons are now hidden from view, but can be activated
		by passing the corresponding item from do_key_down_build in response to
		the Return and Cancel keys. The OkButton case is also called recursively above
		in order to execute the menu choice. */

		case 	OkButton:
			if (ai_controlled(unit)) {
				done = TRUE;
				break;
			}
			/* Get the autobuild state. */
			GetDItem(win, diBuildAuto, NULL, &itemhandle, NULL);
			autobuild = GetCtlValue((ControlHandle) itemhandle);

			/* Always set runlength to default if on autobuild. */
			if (unit->autobuild || ai_controlled(unit))
				run = construction_run_doctrine(unit, 0);
			else {					
				/* Else get the runlength from its text field. */
				GetDItem(win, diBuildEdit, NULL, &itemhandle, NULL);
	        			GetIText(itemhandle, pname);
				StringToNum(pname, (long *) &run);
				run = min(max(run, 0), CLEAR_AGENDA);
			}

			/* Get the selected build task. */
			GetDItem(win, diBuildPopup, NULL, &itemhandle, NULL);
			mitem = GetCtlValue((ControlHandle) itemhandle);
			GetItem(buildMenu, mitem, pname);
			p2c(pname, uname);
			build = NONUTYPE;
			for_all_unit_types(u) {
				/* Find the utype that was selected in the menu. */
				if (strcmp(u_type_name(u), uname) == NULL) {
					build = u;
					break;				
				}
			}

			/* We need to do this last of all since the net_set functions call
			update_unit_display which redraws the closeup itself. The first
			call to a net_set function therefore resets all popup menus to the
			initial values, thus destroying the settings we want to save. */

			net_set_unit_autobuild(side, unit, autobuild);
			/* No matching utype means "Idle", "Asleep" or "Wait til next turn"
			 was selected. */
			if (build == NONUTYPE) {
				net_clear_task_agenda(side, unit);
				 if (strcmp(uname, "Asleep") == NULL) {
					net_set_unit_asleep(side, unit, TRUE, FALSE);
				} else if (strcmp(uname, "Skip Turn") == NULL) {
					net_set_unit_reserve(side, unit, TRUE, FALSE);
				}
			} else	net_set_build_task(unit, build, run, 0, 0);
			done = TRUE;
			break;

		case 	CancelButton:
			done = TRUE;
			break;

		default:
			break;
	}
	if (done)
	    close_window(win);

	/* Restore old port. */
	SetPort(oldport);
}





/*
	Research dialog for a single advanced unit. 
*/

void
unit_research_dialog(Unit *unit)
{
	short 		ditem, mitem, done = FALSE, i, m, d, s;
	char			cname[32], sname[32], buf[BUFSIZE];
	Str255 		pname, pnumber;
	Handle 		itemhandle; 
	MenuHandle	popMenu;
	GrafPtr 		oldport;
 	DialogPtr		win;
	
	/* Open the dialog. */
	win = GetNewDialog(dAdvance, NULL, (DialogPtr) -1);
	GetPort(&oldport);	
	SetPort(win);

	/* Hide all scientists. */
	HideDItem(win, diAdvanceMadScientist);
	HideDItem(win, diAdvanceBusyScientist);
	HideDItem(win, diAdvanceIdleScientist);
	popMenu = GetMenu(mResearchPopup);

	s = unit->curadvance;
	/* Research is going on. */
	if (s >= 0) { 
	   	strcpy(sname, a_type_name(s));
		/* Check if current advance has been completed. */
		if (has_advance(unit->side, s)) {
			/* Do bells and whistles. */
			strcpy(buf, "Scientists in ");
			strcat(buf, unit->name);
			strcat(buf, " discover ");
			strcat(buf, sname);
			strcat(buf, "!\r\r");
			strcat(buf, "Pick new research:");
			/* Show mad scientist. */
			ShowDItem(win, diAdvanceMadScientist);
			/* Only do it once. */
				unit->curadvance = NOADVANCE;
		} else {
			/* Report ongoing research. */
			strcpy(buf, "The scientists in ");
			strcat( buf, unit->name);
			strcat( buf, " are researching ");
			strcat(buf, sname);
			strcat(buf, ".");			
			/* Show busy scientist. */
			ShowDItem(win, diAdvanceBusyScientist);
		}
	/* No current research. */
	} else {
		strcpy(buf, "The scientists in ");
		strcat( buf, unit->name);
		strcat( buf, " are idle.\r");
		strcat( buf, "You should put them to work!\r\r");
		strcat( buf, "Pick new research:");
		/* Show idle scientist. */
		ShowDItem(win, diAdvanceIdleScientist);
	}
	c2p(buf, pname);
	GetDItem(win, diAdvanceMainText, NULL, &itemhandle, NULL);
	SetIText(itemhandle, pname);
	/* Set the advance auto checkbox. */
	GetDItem(win, diAdvanceCheck, NULL, &itemhandle, NULL);
	SetCtlValue((ControlHandle) itemhandle, unit->autoresearch);
	/* Load researchable advance types into popup menu. */
	for_all_advance_types(s) {
		if (side_can_research(unit->side, s)) {
   			c2p(a_type_name(s), pname);
			AppendMenu(popMenu, pname);
		}
	}

	ShowWindow(win);
	while (!done) {
		SetCursor(&QD(arrow));
		draw_default_button(win, OkButton);

		/* Set popup to sname. */
		m = CountMItems(popMenu);
		GetDItem(win, diAdvancePopup, NULL, &itemhandle, NULL);
		SetCtlMax((ControlHandle) itemhandle, m);	/* Important! */
		for (i = 1; i <= m; i++) {
			GetItem(popMenu, i, pname);
			p2c(pname, cname);
			if (strcmp(sname, cname) != 0)
				continue;
			SetCtlValue((ControlHandle) itemhandle, i);
		}

		ModalDialog(NULL, &ditem);
		switch (ditem) {
			case diAdvanceCheck: /* Toggle advance auto checkbox. */
				GetDItem(win, ditem, NULL, &itemhandle, NULL);
				SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
				break;

			case 	diAdvancePopup: /* Current research popup menu. */
				GetDItem(win, ditem, NULL, &itemhandle, NULL);
				mitem = GetCtlValue((ControlHandle) itemhandle);
				GetItem(popMenu, mitem, pname);
				p2c(pname, sname);
		    		break;

			case 	OkButton:
				/* Get the advance auto checkbox state. */
				GetDItem(win, diAdvanceCheck, NULL, &itemhandle, NULL);
				unit->autoresearch = GetCtlValue((ControlHandle) itemhandle);
				/* Stop any research if "Idle" was selected. */
				if (strcmp("Idle", sname) == 0) {
					unit->curadvance = NOADVANCE;
				/* Else select new advance to research. */
				} else for_all_advance_types(s) {
					/* Skip until we find atype that is selected in popMenu. */
					if (strcmp(a_type_name(s), sname) != 0)
						continue;
					unit->curadvance = s;
					}
				done = TRUE;
				break;

			case 	CancelButton:
				done = TRUE;
				break;

			default:
				break;
		}
	}

	/* Close the dialog. */
	DisposeDialog(win);
	/* Restore old port. */
	SetPort(oldport);
}

#if 0

/*
	Planning dialog for a single advanced unit. 
*/

void
unit_plan_dialog(Unit *unit)
{
	short 		ditem, mitem, done = FALSE, i, m, d, s;
	char			cname[32], sname[32], buf[BUFSIZE];
	Str255 		pname, pnumber;
	Handle 		itemhandle; 
	MenuHandle	planMenu;
	GrafPtr 		oldport;
 	DialogPtr		win;
	
	/* Open the dialog. */
	win = GetNewDialog(dPlan, NULL, (DialogPtr) -1);
	GetPort(&oldport);	
	SetPort(win);

	strcpy( buf, unit->name);
	strcat( buf, " needs a plan.\r\rPick one:\r");
	c2p(buf, pname);
	GetDItem(win, diPlanMainText, NULL, &itemhandle, NULL);
	SetIText(itemhandle, pname);

	/* Set plan type popup menu to current plan. */
	GetDItem(win, diPlanPopup, NULL, &itemhandle, NULL);
	if (unit->plan->type == PLAN_NONE)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypeNone);
	else if (unit->plan->type == PLAN_PASSIVE)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypePassive);
	else if (unit->plan->type == PLAN_DEFENSIVE)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypeDefensive);
	else if (unit->plan->type == PLAN_EXPLORATORY)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypeExploratory);
	else if (unit->plan->type == PLAN_OFFENSIVE)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypeOffensive);
	else if (unit->plan->type == PLAN_COLONIZING)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypeColonizing);
	else if (unit->plan->type == PLAN_IMPROVING)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypeImproving);
	else if (unit->plan->type == PLAN_RANDOM)
		SetCtlValue((ControlHandle) itemhandle, miPlanTypeRandom);
	/* Also update the checkmarks, since this menu is used elsewhere. */
	planMenu = GetMenu(mPlanTypes);
	CheckItem(planMenu, miPlanTypeNone, (unit->plan->type == PLAN_NONE));
	CheckItem(planMenu, miPlanTypePassive, (unit->plan->type == PLAN_PASSIVE));
	CheckItem(planMenu, miPlanTypeDefensive, (unit->plan->type == PLAN_DEFENSIVE));
	CheckItem(planMenu, miPlanTypeExploratory, (unit->plan->type == PLAN_EXPLORATORY));
	CheckItem(planMenu, miPlanTypeOffensive, (unit->plan->type == PLAN_OFFENSIVE));
	CheckItem(planMenu, miPlanTypeColonizing, (unit->plan->type == PLAN_COLONIZING));
	CheckItem(planMenu, miPlanTypeImproving, (unit->plan->type == PLAN_IMPROVING));
	CheckItem(planMenu, miPlanTypeRandom, (unit->plan->type == PLAN_RANDOM));

	ShowWindow(win);
	while (!done) {
		SetCursor(&QD(arrow));
		draw_default_button(win, OkButton);

		ModalDialog(NULL, &ditem);
		switch (ditem) {
			case diPlanCheck: /* Toggle plan auto checkbox. */
				GetDItem(win, ditem, NULL, &itemhandle, NULL);
				SetCtlValue((ControlHandle) itemhandle, !GetCtlValue((ControlHandle) itemhandle));
				break;

			case 	OkButton:
				/* Get the advance auto checkbox state. */
				GetDItem(win, diPlanCheck, NULL, &itemhandle, NULL);
				unit->autoplan = GetCtlValue((ControlHandle) itemhandle);
				/* Get selected plan type. */
				GetDItem(win, diPlanPopup, NULL, &itemhandle, NULL);
				mitem = GetCtlValue((ControlHandle) itemhandle);
				switch (mitem)  {
					case miPlanTypeNone:
						net_set_unit_plan_type(unit->side, unit, PLAN_NONE);
						break;
					case miPlanTypePassive:
						net_set_unit_plan_type(unit->side, unit, PLAN_PASSIVE);
						break;
					case miPlanTypeDefensive:
						net_set_unit_plan_type(unit->side, unit, PLAN_DEFENSIVE);
						break;
					case miPlanTypeExploratory:
						net_set_unit_plan_type(unit->side, unit, PLAN_EXPLORATORY);
						break;
					case miPlanTypeOffensive:
						net_set_unit_plan_type(unit->side, unit, PLAN_OFFENSIVE);
						break;
					case miPlanTypeColonizing:
						net_set_unit_plan_type(unit->side, unit, PLAN_COLONIZING);
						break;
					case miPlanTypeImproving:
						net_set_unit_plan_type(unit->side, unit, PLAN_IMPROVING);
						break;
					case miPlanTypeRandom:
						net_set_unit_plan_type(unit->side, unit, PLAN_RANDOM);
						break;
				}
				done = TRUE;
				break;

			case 	CancelButton:
				done = TRUE;
				break;

			default:
				break;
		}
	}

	/* Close the dialog. */
	DisposeDialog(win);
	/* Restore old port. */
	SetPort(oldport);
}

#endif

MenuHandle
build_construction_menu(Unit *unit)
{
	MenuHandle menu;
	Str255 pname;
	int i, u, items;

	menu = GetMenu(mBuildPopup);
	/* First clean out all old items. */
	items = CountMenuItems(menu);
	for (i = 0; i < items; i++)
		DelMenuItem(menu, 1);
	items = 0;
	/* Load buildable unit type names into popup menu. */
	for_all_unit_types(u) {
		if (side_can_build(unit->side, u)
		      && could_create(unit->type, u) 
			    /* There may be room inside the creator. */
		      && (type_can_occupy(u, unit)
		    	   /* We are already building it anyway. */
		    	   || u == unit->plan->tasks->args[0]
		    	   /* There may be room in an adjacent cell. */
		    	   || uu_create_range(unit->type, u) > 0
		    	   /* There may be room in the same cell. */
		    	   || type_can_occupy_cell(u, unit->x, unit->y)
		    	   /* Special case for settlers that build a city and disappear. */
		    	   || (type_can_occupy_cell_without(u, unit->x, unit->y, unit)
		    	      && uu_hp_to_garrison(unit->type, u) >= u_hp(unit->type)))) {
	   		c2p(u_type_name(u), pname);
			AppendMenu(menu, pname);
			++items;
		}
	}
	/* Add a separator if we have buildable units. */
	if (items)
	    InsMenuItem(menu, "\p(-", 0);
	InsMenuItem(menu, "\pIdle", 0);
	InsMenuItem(menu, "\pSkip Turn", 0);
	InsMenuItem(menu, "\pAsleep", 0);
 	return menu;
 }

MenuHandle
build_research_menu(Side *side)
{
	MenuHandle menu;
	Str255 pname;
	int i, a, items;

	menu = GetMenu(mResearchPopup);
	/* First clean out all old items. */
	items = CountMenuItems(menu);
	for (i = 0; i < items; i++)
		DelMenuItem(menu, 1);
	items = 0;
	/* Load scientific advances that can be researched by side. */
	for_all_advance_types(a) {
		if (side_can_research(side, a)) {
   			c2p(a_type_name(a), pname);
			AppendMenu(menu, pname);
			++items;
		}
	}
	/* Add a separator if we have any advances. */
	if (items)
	    InsMenuItem(menu, "\p(-", 0);
	InsMenuItem(menu, "\pIdle", 0);
	InsMenuItem(menu, "\pStop Research", 0);
 	return menu;
 }

