/* Handling of assorted minor windows for the Mac interface to Xconq.
   Copyright (C) 1992-1998 Stanley T. Shebs.

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"
extern char *curdatestr;
extern char *goal_desc PARAMS ((char *buf, Goal *goal));
extern int update_total_hist_lines PARAMS ((Side *side));
extern int build_hist_contents PARAMS ((Side *side, int n, HistEvent **histcontents, int numvis));

#define unit_doctrine(unit)  \
  ((unit->side ? unit->side : indepside)->udoctrine[unit->type])

#include "kpublic.h"
#include "macconq.h"
extern void set_construction_run_length(int len);

#define DEFAULT_RUN (99)

static void adjust_construction_items(void);

static pascal void history_scroll_fn(ControlHandle control, short code);
static pascal void notice_vscroll_fn(ControlHandle control, short code);
static pascal void scores_vscroll_fn(ControlHandle control, short code);

static void calc_history_layout(void);
static void set_history_scrollbar(void);

// --- NOW DECLARED IN MACCONQ.H ------------------------------------------

// extern char **current_colorscheme;		/* Color schemes by name (red,pink,blue) */
									/* Defined in maccolors.c */

// --------------------------------------------------------------------


/* Globals for the game window. */

WindowPtr gamewin = nil;

int gamewinw = 200;
int gamedatehgt = 18;
int gameclockoffset;
int gameclockhgt = 15;
int gamenoteoffset;
int gamenotehgt = 15;
int gametophgt;

int gamesidehgt = 24;
int gamesideclockhgt = 15;
int gamesideclockoffset;
int gamesidescorehgt = 15;
int gamesidescoreoffset;

int gamenumsides;

Handle aisicnhandle = nil;
Handle facesicnhandle[3];

char *game_progress_str = "";

time_t lastnow;

/* Globals for the construction window. */

WindowPtr constructionwin = nil;

static ListHandle construction_unit_list = nil;
static ListHandle construction_type_list = nil;

static int maxtypewid;
static int constructmargin = 5;
static int constructtop = 32;

/* This is the vector of units that can do construction or research. */

static UnitVector *constructor_units = NULL;

static int numposstypes;
static int *possibletypes = NULL;

int currunlength;

int editedrunlength = -1;

/* (this is referenced from the end-turn command, so can't make static) */
ControlHandle constructbutton;
static ControlHandle researchbutton;

TEHandle run_length_text = nil;

static Rect runlengthrect;
static Rect unitlistrect;
static Rect typelistrect;

/* Globals for unit closeup windows. */

int lastunitcloseuph = -1, lastunitcloseupv = -1;

static int m_per_row = 2;

static char *nummrows;

int closeup_spacing = 16;

int closeupwinwid = 200;

/* Globals for the history window. */

WindowPtr historywin = nil;

int maxvishistlines = 200;

HistEvent **histcontents = NULL;

int numhistcontents = 0;

ControlHandle histvscrollbar;

int history_line_spacing;

int history_top_line_height;

int numvishistlines;

int total_history_lines = 0;

ControlActionUPP history_scroll_proc;

/* Globals for the notice window. */

DialogPtr noticewin = nil;

static TEHandle notice_text = nil;

static ControlHandle notice_v_scrollbar;

ControlActionUPP notice_vscroll_proc;

/* Globals for the scores window. */

DialogPtr scoreswin = nil;

static TEHandle scores_text = nil;

static ControlHandle scores_v_scrollbar;

ControlActionUPP scores_vscroll_proc;

/* The game progress window. */

/* Create the game progress window. */

void
create_game_window()
{
	int screenwidth;
	extern int numscores;

	/* Create the window, color if possible, since emblems may be in color. */
	if (hasColorQD) {	
		gamewin = GetNewCWindow(wGame, NULL, (WindowPtr) -1L);
	} else {
		gamewin = GetNewWindow(wGame, NULL, (WindowPtr) -1L);
	}
	gametophgt = gamedatehgt;
	if (g_rt_per_turn() > 0 || g_rt_for_game() > 0) {
		gameclockoffset = gametophgt;
		gametophgt += gameclockhgt;
	}
	gamenoteoffset = gametophgt;
	gametophgt += gamenotehgt;
	/* Add some space if sides have a per-turn and/or per-game clock. */
	if (g_rt_per_side() > 0) {
		gamesideclockoffset = gamesidehgt;
		gamesidehgt += gamesideclockhgt;
	}
	/* Add additional space for each two scorekeepers. */
	if (keeping_score()) {
		gamesidescoreoffset = gamesidehgt;
		gamesidehgt += gamesidescorehgt * ((numscorekeepers + 1) / 2);
	}
	/* This is not growable, so we have to ensure it's big enough to start with. */
	/* (Record the current numsides so we can grow the window later.) */
	gamenumsides = numsides;
	SizeWindow(gamewin, gamewinw, gametophgt + gamenumsides * gamesidehgt, 1);
	if (first_windows) {
		get_main_screen_size(&screenwidth, NULL);
		MoveWindow(gamewin, screenwidth - gamewinw - 3, 40, FALSE);
	}
	/* Get handles to useful sicns. */
	aisicnhandle = GetNamedResource('SICN', "\pmplayer");
	facesicnhandle[0] = GetNamedResource('SICN', "\phostile");
	facesicnhandle[1] = GetNamedResource('SICN', "\pneutral");
	facesicnhandle[2] = GetNamedResource('SICN', "\pfriendly");
}

char *last_status;
char *last_status_left;
char *last_status_resv;
char *last_status_fini;
char **last_status_score1;

void
draw_game()
{
	Side *side2;
	GrafPtr oldport;

	if (gamewin == nil)
	  return;
	/* The window might need to get bigger. */
	if (numsides != gamenumsides) {
		last_status = NULL;
		last_status_left = NULL;
		last_status_resv = NULL;
		last_status_fini = NULL;
		last_status_score1 = NULL;
		gamenumsides = numsides;
		SizeWindow(gamewin, gamewinw, gametophgt + gamenumsides * gamesidehgt, 1);
	}
	if (last_status == NULL)
	  last_status = xmalloc(numsides + 1);
	if (last_status_left == NULL)
	  last_status_left = xmalloc(numsides + 1);
	if (last_status_resv == NULL)
	  last_status_resv = xmalloc(numsides + 1);
	if (last_status_fini == NULL)
	  last_status_fini = xmalloc(numsides + 1);
	if (last_status_score1 == NULL) {
		int i;
		last_status_score1 = (char **) xmalloc((numsides + 1) * sizeof(char *));
		for (i = 0; i <= numsides; ++i)
		  last_status_score1[i] = xmalloc(100);
	}
	GetPort(&oldport);
	SetPort(gamewin);
	TextFont(large_font_id);
	TextSize(large_font_size);
	draw_game_date();
	draw_game_progress();
	/* Draw a solid separating line between date info and side list. */
	MoveTo(0, gametophgt);
	Line(gamewinw, 0);
	for_all_sides(side2)
	  draw_game_side(side2);
	SetPort(oldport);
}

/* Display the current time and date and any realtime countdowns. */

void
draw_game_date()
{
	Rect tmprect;

	SetRect(&tmprect, 0, 0, gamewinw, gamedatehgt - 1);
	EraseRect(&tmprect);
	MoveTo(tmprect.left + 10, tmprect.top + 12);
	TextFace(bold);
	DrawText(curdatestr, 0, strlen(curdatestr));
	TextFace(0);
	draw_game_clocks();
	if (endofgame) {
		gray_out_rect(&tmprect);
	}
#ifdef DEBUGGING
    /* Indicate the state of all the debug flags. */
	if (Debug || DebugM || DebugG) {
		sprintf(spbuf, "%c%c%c%c",
				(Debug ? 'D' : ' '), (DebugM ? 'M' : ' '), (DebugG ? 'G' : ' '),
				(Profile ? 'P' : ' '));
		MoveTo(tmprect.right - 30, tmprect.top + 12);
		DrawText(spbuf, 0, strlen(spbuf));
	}
#endif /* DEBUGGING */
}

void
draw_game_clocks()
{
	int elapsed, s2, sy;
    time_t now;
	Rect tmprect;
	Side *side2;

	/* Draw per-turn and per-game time limits that for the game as a whole. */
	SetRect(&tmprect, 0, gamedatehgt, gamewinw / 2, gamedatehgt + gameclockhgt - 1);
	if (g_rt_per_turn() > 0) {
		time(&now);
    	elapsed = (int) difftime(now, turn_play_start_in_real_time);
		time_desc(spbuf, g_rt_per_turn() - elapsed, g_rt_per_turn());
		EraseRect(&tmprect);
		MoveTo(tmprect.left + 20, tmprect.top + 10);
		DrawText(spbuf, 0, strlen(spbuf));
		lastnow = now;
	}
	OffsetRect(&tmprect, 100, 0);
	if (g_rt_for_game() > 0) {
		time(&now);
    	elapsed = (int) difftime(now, game_start_in_real_time);
		time_desc(spbuf, g_rt_for_game() - elapsed, g_rt_for_game());
		EraseRect(&tmprect);
		MoveTo(tmprect.left + 10, tmprect.top + 10);
		DrawText(spbuf, 0, strlen(spbuf));
		lastnow = now;
	}
	/* Draw per-side clocks if any limits defined. */
	if (g_rt_per_side() > 0) {
		for_all_sides(side2) {
			if (side2->ingame) {
				s2 = side_number(side2);
				sy = gametophgt + (s2 - 1) * gamesidehgt + gamesideclockoffset;
				elapsed = 0;
				if (!side2->finishedturn)
    			  elapsed = (int) difftime(now, turn_play_start_in_real_time); /* should be side start */
				time_desc(spbuf, g_rt_per_side() - side2->totaltimeused - elapsed, g_rt_per_side());
				SetRect(&tmprect, 0, sy, gamewinw, sy + gamesideclockhgt - 1);
				EraseRect(&tmprect);
				MoveTo(tmprect.left + 20, tmprect.top + 10);
				DrawText(spbuf, 0, strlen(spbuf));
				/* (should draw per-turn side usage) */
			}
		}
	}
}

void
draw_game_progress()
{
	Rect tmprect;

	SetRect(&tmprect, 0, gamenoteoffset, gamewinw, gamenoteoffset + gamenotehgt - 1);
	EraseRect(&tmprect);
	MoveTo(1, gamenoteoffset + 12);
	DrawText(game_progress_str, 0, strlen(game_progress_str));
}

/* Draw info about a given side. */

void
draw_game_side(Side *side2)
{
	int s2 = side_number(side2);
	int sx = 20, sy = gametophgt + (s2 - 1) * gamesidehgt;

	draw_side_emblem(gamewin, 2, sy + 4, 16, 16, s2, shadow_emblem);
	strcpy(spbuf, short_side_title(side2));
	MoveTo(sx, sy + 12);
	/* Put the name of our side in boldface. */
	TextFace((side2 == dside ? bold : 0));
	DrawText(spbuf, 0, strlen(spbuf));
	TextFace(0);
	if (side_has_ai(side2) && side2->ingame) {
		/* Show that the side is run by an AI. */
		plot_sicn(gamewin, 182, sy + 2, aisicnhandle, 0, TRUE, srcOr);
	}
	if (side2 != dside
	    && side2->ingame
	    && (side_has_ai(side2) || side_has_display(side2))) {
		/* Indicate attitude of other side. */
		plot_sicn(gamewin, 164, sy + 2,
			facesicnhandle[feeling_towards(side2, dside)], 0, TRUE, srcOr);
	}
	last_status[s2] = -1;
	last_status_left[s2] = -1;
	last_status_resv[s2] = -1;
	last_status_fini[s2] = -1;
	*(last_status_score1[s2]) = '\0';
	draw_side_status(side2);
	/* Draw a separating line. */
	PenPat(QDPat(gray));
	MoveTo(0, sy + gamesidehgt);
	Line(gamewinw, 0);
	PenNormal();
}

/* (should make this more generic) */

int
feeling_towards(Side *side, Side *side2)
{
	if (trusted_side(side2, side)) {
		return 2;
	} else if (side_has_ai(side) && should_try_to_win(side)) {
		return 0;
	} else {
		return 1;
	}
}

/* Draw the current details about a side. */

void
draw_side_status(Side *side2)
{
	int s2 = side_number(side2);
	int sx, sy, i;
	int totacp, resvacp, acpleft, percentleft, percentresv;
	int newstatus;
	char *scoredesc;
	Rect siderect, tmprect, progressrect;
    Scorekeeper *sk;
    extern int curpriority;

    /* Be safe.  It's probably not great to be passing through here before the game
       window is actually set up, but it's easier to just ignore the attempt than
       to figure out why it's attempting... */
	if (last_status == NULL)
	  return;
	sy = gametophgt + (s2 - 1) * gamesidehgt;
	newstatus = last_status[s2];
	percentleft = 0;
	SetRect(&siderect, 0, sy + 1, gamewinw, sy + gamesidehgt);
	/* Set up the area where we show progress. */
	SetRect(&progressrect, 20, sy + 12 + 4, 20 + 100, sy + 12 + 4 + 7);
	if (!side2->ingame || endofgame) {
		if (last_status[s2] != 0) {
			gray_out_rect(&siderect);
			if (side_won(side2)) {
				/* (should) Indicate that this side has won. */
				/* draw like a trophy or flourishes or some such?) */
			} else if (side_lost(side2)) {
				/* Draw a (solid) line crossing out the loser.  Simple and obvious. */
				MoveTo(1, sy + 8);
				Line(gamewin->portRect.right - 3, 0);
			}
			newstatus = 0;
		}
#ifdef DESIGNERS
	} else if (numdesigners > 0) {
		if (last_status[s2] != 0) {
			tmprect = progressrect;
			EraseRect(&tmprect);
			newstatus = 0;
		}
#endif /* DESIGNERS */
	} else {
		if (!g_use_side_priority() || curpriority == side2->priority) {
			/* Show the current acp totals/progress of the side. */
			/* This is not quite the security hole it might seem,
			   you don't get much advantage out of seeing how far along each side is,
			   and it gives you a feel for how the turn is progressing. */
			totacp = side_initacp(side2);
			if (totacp > 0) {
				acpleft = side_acp(side2);
				resvacp = side_acp_reserved(side2);
				if (totacp > 0) {
					percentleft = (100 * acpleft) / totacp;
					percentleft = max(0, min(99, percentleft));
					percentresv = (100 * resvacp) / totacp;
					/* Acp in reserve should be less than acp total. */
					percentresv = max(0, min(percentleft, percentresv));
				} else {
					percentleft = percentresv = 0;
				}
				/* Only draw if there's been any actual change. */
				if (last_status[s2] != 1
					|| last_status_left[s2] != percentleft
					|| last_status_resv[s2] != percentresv) {
					if (percentleft > 0) {
						tmprect = progressrect;
						InsetRect(&tmprect, 1, 1);
						EraseRect(&tmprect);
						tmprect.right = tmprect.left + percentleft;
						FillRect(&tmprect, QDPat(black));
					}
					if (percentresv > 0) {
						tmprect = progressrect;
						InsetRect(&tmprect, 1, 1);
						tmprect.right = tmprect.left + percentresv;
						FillRect(&tmprect, QDPat(dkGray));
					}
					last_status_left[s2] = percentleft;
					last_status_resv[s2] = percentresv;
				}
				newstatus = 1;
			} else {
				if (last_status[s2] != 2) {
					tmprect = progressrect;
					InsetRect(&tmprect, 1, 1);
					EraseRect(&tmprect);
					newstatus = 2;
				}
			}
		} else if (g_use_side_priority() && curpriority != side2->priority) {
			newstatus = 0;
		}
		/* (should this be a generic kernel test?) */
		if (side2->finishedturn || !(side_has_ai(side2) || side_has_display(side2))) {
			if (last_status_fini[s2] != 1) {
				tmprect = progressrect;
				InsetRect(&tmprect, 1, 1);
				EraseRect(&tmprect);
				tmprect.right = tmprect.left + percentleft;
				FillRect(&tmprect, QDPat(gray));
				last_status_fini[s2] = 1;
			}
		} else if (!side2->finishedturn) {
			last_status_fini[s2] = 0;
		}
	}
	/* Decide how to frame the progress bar - each shade indicates something. */
	if (newstatus != last_status[s2]) {
		if (newstatus == 0) {
			EraseRect(&progressrect);
			PenPat(QDPat(white));
		} else if (newstatus == 1) {
			PenPat(QDPat(black));
		} else if (newstatus == 2) {
			PenPat(QDPat(gray));
		} else {
			PenPat(QDPat(ltGray));
		}
		FrameRect(&progressrect);
		PenNormal();
		last_status[s2] = newstatus;
	}
	/* Always draw the score, and always ungrayed. */
	if (keeping_score()) {
		siderect.top += gamesidescoreoffset;
		siderect.bottom = siderect.top + gamesidescorehgt - 1;
		i = 0;
		for_all_scorekeepers(sk) {
			scoredesc = side_score_desc(spbuf, side2, sk);
			if ((i == 0 && strcmp(scoredesc, last_status_score1[s2]) != 0)
				|| i > 0) {
				if ((i & 1) == 0)
				  EraseRect(&siderect);
				/* Draw two scorekeepers per line. */
				sx = (((i & 1) == 1) ? gamewinw / 2 : 0);
				/* Draw the scorekeeper's status. */
				MoveTo(sx + 10, siderect.top + 10);
				DrawText(scoredesc, 0, strlen(scoredesc));
				if (i == 0)
				  strcpy(last_status_score1[s2], scoredesc);
			}
			++i;
			/* Offset rectangle to next row. */
			if ((i & 1) == 0)
			  OffsetRect(&siderect, 0, gamesidescorehgt);
		}
	}
}

void
do_mouse_down_game(Point mouse, int mods)
{
	beep();
}

/* The construction planning window. */

void
create_construction_window()
{
	int done = FALSE, mainheight;
	Point cellsize;
	Rect listrect, tmprect;

	if (hasColorQD) {
		constructionwin = GetNewCWindow(wConstruction, NULL, (WindowPtr) -1L);
	} else {
		constructionwin = GetNewWindow(wConstruction, NULL, (WindowPtr) -1L);
	}
	constructbutton = GetNewControl(cConstructButton, constructionwin);
	researchbutton = GetNewControl(cResearchButton, constructionwin);
	SetPort(constructionwin);
	TextFont(small_font_id);
	TextSize(small_font_size);
	calc_construction_rects();
	run_length_text = TENew(&runlengthrect, &runlengthrect);
	set_construction_run_length(DEFAULT_RUN);
	editedrunlength = -1;
	/* Set up the list of all constructing units. */
	tmprect = unitlistrect;
	tmprect.right -= sbarwid;
	SetRect(&listrect, 0, 0, 1, 0);
	SetPt(&cellsize, 300, 12);
	/* Create the list of units itself. */
	construction_unit_list =
		LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
			 FALSE, FALSE, FALSE, TRUE);
	/* Now set up the list of types. */
	tmprect = typelistrect;
	tmprect.right -= sbarwid;
	SetRect(&listrect, 0, 0, 1, 0);
	/* (should calc this from the desired font) */
	SetPt(&cellsize, 300, 12);
	construction_type_list =
		LNew(&tmprect, &listrect, cellsize, 128, constructionwin,
			 FALSE, FALSE, FALSE, TRUE);
	init_construction_lists();
	if (1 /* position construction at bottom of main screen */) {
		get_main_screen_size(NULL, &mainheight);
		tmprect = constructionwin->portRect;
		MoveWindow(constructionwin,
				   4,
				   mainheight - (tmprect.bottom - tmprect.top) - 3,
				   FALSE);
	}
	ShowWindow(constructionwin);
}

/* Build the list of constructing units and the list of constructible types. */

void
init_construction_lists()
{
	int u;
	Unit *unit;
	Cell tmpcell;

	/* Update the list of units. */
	LDoDraw(0, construction_unit_list);
	LDelRow(0, 0, construction_unit_list);
	SetPt(&tmpcell, 0, 0);
	/* Create the vector of constructing units, at a reasonable initial size. */
	if (constructor_units == NULL) {
		constructor_units = make_unit_vector(max(50, numunits));
	}
	clear_unit_vector(constructor_units);
	for_all_side_units(dside, unit) {
		maybe_add_unit_to_construction_list(unit);
	}
	LDoDraw(1, construction_unit_list);
	/* Update the list of types. */
	LDoDraw(0, construction_type_list);
	LDelRow(0, 0, construction_type_list);
	SetPt(&tmpcell, 0, 0);
	if (possibletypes == NULL)
	  possibletypes = (int *) xmalloc(numutypes * sizeof(int));
	numposstypes = 0;
	for_all_unit_types(u) {
		if (construction_possible(u) && type_allowed_on_side(u, dside)) {
			LAddRow(1, tmpcell.v, construction_type_list);
			constructible_desc(spbuf, dside, u, NULL);
			LSetCell(spbuf, strlen(spbuf), tmpcell, construction_type_list);
			++tmpcell.v;
			possibletypes[numposstypes++] = u;
		}
	}
	LDoDraw(1, construction_type_list);
	adjust_construction_controls();
}

void
reinit_construction_lists()
{
	init_construction_lists();
}

void
set_construction_run_length(int len)
{
	/* Do nothing if no change. */
    if (len == currunlength)
      return;
    currunlength = len;
	TESetSelect(0, 32767, run_length_text);
	TEDelete(run_length_text);
	sprintf(tmpbuf, "%d", len);
	TEInsert(tmpbuf, strlen(tmpbuf), run_length_text);
}

/* Draw the construction window by updating the lists and framing them. */

void
draw_construction()
{
	Rect tmprect;

	calc_construction_rects();
	TEUpdate(&(constructionwin->portRect), run_length_text);
	tmprect = runlengthrect;
	InsetRect(&tmprect, -1, -1);
	FrameRect(&tmprect);
	LUpdate(constructionwin->visRgn, construction_unit_list);
	tmprect = unitlistrect;
	InsetRect(&tmprect, -1, -1);
	FrameRect(&tmprect);
	LUpdate(constructionwin->visRgn, construction_type_list);
	tmprect = typelistrect;
	InsetRect(&tmprect, -1, -1);
	FrameRect(&tmprect);
	/* Maybe show the construct button as the default. */
	draw_construction_default();
}

/* Draw a heavy outline around the construction button. */

void
draw_construction_default()
{
	Rect tmprect;
	GrafPtr oldport;

	GetPort(&oldport);
	SetPort(constructionwin);
	tmprect = (*constructbutton)->contrlRect;
	PenSize(3, 3);
	InsetRect(&tmprect, -4, -4);
	if ((*constructbutton)->contrlHilite != 0) {
		PenMode(patBic);
	}
	FrameRoundRect(&tmprect, 16, 16);
	PenNormal();
	SetPort(oldport);
}

/* Figure out how to subdivide the construction window for the two lists. */

void
calc_construction_rects()
{
	int wid, hgt, divide;
	Rect tmprect;

	/* (should compute based on num chars needed, plus font size) */
	maxtypewid = 250;
	tmprect = constructionwin->portRect;
	runlengthrect = tmprect;
	runlengthrect.left = runlengthrect.right - 100;  runlengthrect.top = 5;
	runlengthrect.right -= 20;  runlengthrect.bottom = 25;
	wid = tmprect.right - tmprect.left - sbarwid;
	hgt = tmprect.bottom - tmprect.top - sbarwid;
	if (wid / 2 > maxtypewid) {
		divide = wid - maxtypewid;
	} else {
		divide = wid / 2;
	}
	SetRect(&unitlistrect, 0, constructtop, divide, hgt);
	InsetRect(&unitlistrect, constructmargin, constructmargin);
	SetRect(&typelistrect, divide, constructtop, wid, hgt);
	InsetRect(&typelistrect, constructmargin, constructmargin);
}

void
activate_construction(int activate)
{
	if (activate)
	  TEActivate(run_length_text);
	else
	  TEDeactivate(run_length_text);
	LActivate(activate, construction_unit_list);
	LActivate(activate, construction_type_list);
}

Unit *
get_selected_construction_unit()
{
	Point tmpcell;
	Unit *unit;

	SetPt(&tmpcell, 0, 0);
	if (LGetSelect(TRUE, &tmpcell, construction_unit_list)) {				
		if (tmpcell.v < constructor_units->numunits) {
			unit = (constructor_units->units)[tmpcell.v].unit;
			if (is_active(unit))
			  return unit;
		}
	}
	return NULL;
}

int
get_selected_construction_type()
{
	Point tmpcell;

	SetPt(&tmpcell, 0, 0);
	if (LGetSelect(TRUE, &tmpcell, construction_type_list)) {				
		if (tmpcell.v < numposstypes) {
			return possibletypes[tmpcell.v];
		}
	}
	return NONUTYPE;
}

void
scroll_to_selected_construction_unit()
{
	Unit *unit;

	/* Beep and return if there are no maps open currently. */
	if (maplist == NULL) {
		beep();
		return;
	}
	unit = get_selected_construction_unit();
	if (unit != NULL && inside_area(unit->x, unit->y))
	  scroll_best_map_to_unit(unit, FALSE);
}

/* Handle a click anywhere within the construction window. */

void
do_mouse_down_construction(Point mouse, int mods)
{
	ControlHandle control;
	short part;
	int u;
	Unit *unit;
	extern int modal_construction;
	extern WindowPtr window_behind_construction;

	part = FindControl(mouse, constructionwin, &control);
	if (control == constructbutton) {
		unit = get_selected_construction_unit();
		if (unit != NULL) {
			u = get_selected_construction_type();
			if (u != NONUTYPE) {
				net_push_build_task(unit, u, currunlength);
				update_construction_unit_list(unit);
				if (modal_construction && window_behind_construction != nil) {
					SelectWindow(window_behind_construction);
					update_window(window_behind_construction);
				}
				window_behind_construction = NULL;
				modal_construction = FALSE;
				return;
			}
		}
	} else if (control == researchbutton) {
		unit = get_selected_construction_unit();
		if (unit != NULL) {
			u = get_selected_construction_type();
			if (u != NONUTYPE) {
				net_push_research_task(unit, u, u_tech_to_build(u));
				update_construction_unit_list(unit);
				if (modal_construction && window_behind_construction != nil) {
					SelectWindow(window_behind_construction);
					update_window(window_behind_construction);
				}
				window_behind_construction = NULL;
				modal_construction = FALSE;
				return;
			}
		}
	} else if (PtInRect(mouse, &runlengthrect)) {
		TEClick(mouse, mods, run_length_text);
		/* (should switch this to be current item) */
	} else if (PtInRect(mouse, &unitlistrect)) {
		LClick(mouse, mods, construction_unit_list);
		/* Update the type list to show what could be built and in how long. */
		update_type_list_for_unit(get_selected_construction_unit());
	} else if (PtInRect(mouse, &typelistrect)) {
		LClick(mouse, mods, construction_type_list);
		/* Update the unit list to show what could build the type */
		update_unit_list_for_type(get_selected_construction_type());
	} else {
		/* Click was not in any useful part of the window. */ 
	}
}

int
do_key_down_construction(key)
int key;
{
	int len, runlength, i;
	char buffer[10];
	CharsHandle text;

	if (isdigit(key)) {
		TEKey(key, run_length_text);
		text = TEGetText(run_length_text);
		/* Pick out only the initial digits (up to 9). */
		len = min((*run_length_text)->teLength, 9);
		strncpy(buffer, *text, len);
		buffer[len] = '\0';
		runlength = atoi(buffer);
		if (between(1, runlength, 32767)) {
			currunlength = runlength;
			editedrunlength = runlength;
			return TRUE;
		}
	} else if (key == 13 || key == 3) {
		/* Pass enters and returns back to Dialog Manager. */
		return FALSE;
	} else {
		for (i = 0; i < numposstypes; ++i) {
			if (key == unitchars[possibletypes[i]]
				/* Skip over types that the selected unit can't build. */
				&& est_completion_time(get_selected_construction_unit(), possibletypes[i]) >= 0) {
				select_type_in_construction_window(possibletypes[i]);
				return TRUE;
			}
		}
		/* If not recognized as a unit type shortcut, hand it to the
		   run length field. */
		if (isalpha(key)) {
			beep();
			return FALSE;
		}
		TEKey(key, run_length_text);

	}
	return FALSE;
}

/* Highlight exactly one specific unit in the construction window, and unhighlight
   any others. */

void
select_unit_in_construction_window(Unit *unit)
{
	int i;
	Point tmpcell;

	for (i = 0; i < constructor_units->numunits; ++i) {
		SetPt(&tmpcell, 0, i);
		LSetSelect((unit == (constructor_units->units)[i].unit), tmpcell, construction_unit_list);
		LAutoScroll(construction_unit_list);
	}
	update_type_list_for_unit(get_selected_construction_unit());
}

void
select_type_in_construction_window(int u)
{
	int i;
	Point tmpcell;

	for (i = 0; i < numposstypes; ++i) {
		SetPt(&tmpcell, 0, i);
		LSetSelect((u == possibletypes[i]), tmpcell, construction_type_list);
		LAutoScroll(construction_type_list);
	}
	if (u == NONUTYPE)
	  return;
	update_unit_list_for_type(get_selected_construction_type());
}

/* Given a unit (which may be any unit), update the list of constructing units. */

void
update_construction_unit_list(Unit *unit)
{
	int i, u;
	Point tmpcell;

	if (constructionwin == nil)
	  return;
	u = get_selected_construction_type();
	/* We need to look for it even if it might not be ours, since it might
	   have been captured or otherwise lost, and needs to be removed. */
	for (i = 0; i < constructor_units->numunits; ++i) {
		if (unit == (constructor_units->units)[i].unit) {
			SetPt(&tmpcell, 0, i);
			if (is_active(unit)
				&& can_build_or_help(unit)
				&& side_controls_unit(dside, unit)) {
				construction_desc(spbuf, unit, u);
				LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
			} else {
				remove_unit_from_vector(constructor_units, unit, i);
				LDelRow(1, tmpcell.v, construction_unit_list);
			}
			return;
		}
	}
	/* Unit was not found, try to add it to the list. */
	maybe_add_unit_to_construction_list(unit);
}

void
maybe_add_unit_to_construction_list(Unit *unit)
{
	Point tmpcell;

	if (is_active(unit)
		&& can_build_or_help(unit)
		&& side_controls_unit(dside, unit)) {
		/* Add this unit to the vector of constructing units. */
		constructor_units = add_unit_to_vector(constructor_units, unit, 0);
		/* (should sort and maybe rearrange list here) */
		/* Add a row at the end of the list. */
		SetPt(&tmpcell, 0, constructor_units->numunits - 1);
		LAddRow(1, constructor_units->numunits - 1, construction_unit_list);
		construction_desc(spbuf, unit, get_selected_construction_type());
		LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
	}
}

void
update_unit_list_for_type(int u)
{
	int i;
	Point tmpcell;
	Unit *unit;

	for (i = 0; i < constructor_units->numunits; ++i) {
		unit = (constructor_units->units)[i].unit;
		if (unit != NULL) {
			SetPt(&tmpcell, 0, i);
			if (is_active(unit) && unit->side == dside) {
				construction_desc(spbuf, unit, u);
				LSetCell(spbuf, strlen(spbuf), tmpcell, construction_unit_list);
			} else {
/*				LDelRow(1, tmpcell.v, construction_unit_list); */
				LSetCell("", 0, tmpcell, construction_unit_list);
			}
		}
	}
	adjust_construction_controls();
}

void
update_construction_type_list()
{
	int u;

	if (constructionwin == nil)
	  return;
	u = get_selected_construction_type();
	update_type_list_for_unit(get_selected_construction_unit());
}

void
update_type_list_for_unit(Unit *unit)
{
	int i;
	Point tmpcell;

	for (i = 0; i < numposstypes; ++i) {
		constructible_desc(spbuf, dside, possibletypes[i], get_selected_construction_unit());
		SetPt(&tmpcell, 0, i);
		LSetCell(spbuf, strlen(spbuf), tmpcell, construction_type_list);
	}
	adjust_construction_controls();
}

/* Enable/disable controls according to whether the selected list elements can
   do construction activities. */

void
adjust_construction_controls()
{
	int u, canconstruct = FALSE, canresearch = FALSE, len;
	Unit *unit;

	unit = get_selected_construction_unit();
	if (unit != NULL) {
		u = get_selected_construction_type();
		if (u != NONUTYPE) {
			if (uu_acp_to_create(unit->type, u) > 0)
			  canconstruct = TRUE;
			if (uu_acp_to_research(unit->type, u) > 0)
			  canresearch = TRUE;
		}
	}
	HiliteControl(constructbutton, (canconstruct ? 0 : 255));
	HiliteControl(researchbutton, (canresearch ? 0 : 255));
	draw_construction_default();
	/* If there is doctrine on construction run length, use it to seed the
	   run length in the dialog. */
	if (editedrunlength < 0 && unit != NULL && u != NONUTYPE) {
		len = DEFAULT_RUN;
		if (unit_doctrine(unit)->construction_run[u] > 0) {
			len = unit_doctrine(unit)->construction_run[u];
		}
		set_construction_run_length(len);
	}
}

/* Resize the construction window to the given size. */

void
grow_construction(int h, int v)
{
	EraseRect(&constructionwin->portRect);
	SizeWindow(constructionwin, h, v, 1);
	adjust_construction_items();
	/* This will force a full redraw at the next update. */
	InvalRect(&constructionwin->portRect);
}					

/* Zooming "rightsizes" the window. */

void
zoom_construction(int part)
{
	int titleh, vislinesavail;
	Rect zoomrect;
	GDHandle zoomgd;

	EraseRect(&constructionwin->portRect);
	if (part == inZoomOut) {
		if (hasColorQD) {
			zoomgd = best_zoom_screen(&constructionwin->portRect);
			zoomrect = (*zoomgd)->gdRect;
			if (zoomgd == GetMainDevice()) {
				zoomrect.top += GetMBarHeight();
			}
		} else {
			/* If no Color QD, then there is only the one screen. */
			zoomrect = QD(screenBits).bounds;
			zoomrect.top += GetMBarHeight();
		}
		titleh = 20; /* (should calc) */
		zoomrect.top += titleh;
		InsetRect(&zoomrect, 4, 4);
		/* If not many units or types, shrink the zoomed window to fit. */
		vislinesavail = (zoomrect.bottom - zoomrect.top - sbarwid) / 15;
		if (0) {
			zoomrect.bottom = zoomrect.top + 20  * 15 + sbarwid;
		}
		(*((WStateDataHandle) ((WindowPeek) constructionwin)->dataHandle))->stdState = zoomrect;
	}
	ZoomWindow(constructionwin, part, (constructionwin == FrontWindow()));
	adjust_construction_items();
	/* This will force a full redraw at the next update. */
	InvalRect(&constructionwin->portRect);
}

/* Move and resize the list and text objects in the construction window. */

static void
adjust_construction_items()
{
	int listwid;
	Point cellsize;

	/* Recalculate size and position. */
	calc_construction_rects();
	/* Resize the run length text item. */
	(*run_length_text)->viewRect = runlengthrect;
	(*run_length_text)->destRect = runlengthrect;
	TECalText(run_length_text);
	/* Resize the unit list. */
	listwid = unitlistrect.right - unitlistrect.left - sbarwid;
	LSize(listwid, unitlistrect.bottom - unitlistrect.top,
		  construction_unit_list);
	SetPt(&cellsize, max(300, listwid), 12);
	LCellSize(cellsize, construction_unit_list);
	/* Move the type list (is this the approved way to do it?) */
	(*construction_type_list)->rView.left = typelistrect.left;
	listwid = typelistrect.right - typelistrect.left - sbarwid;
	LSize(listwid, typelistrect.bottom - typelistrect.top,
		  construction_type_list);
	SetPt(&cellsize, max(300, listwid), 12);
	LCellSize(cellsize, construction_type_list);
}

/* The side renaming dialog includes places for all the different name-related
   properties of a side. */

static void seed_side_rename_string(DialogPtr win, short ditem, char *str);
static char *get_side_rename_string(DialogPtr win, short ditem);

void
side_rename_dialog(Side *side)
{
	short done = FALSE, changed = TRUE, ditem;
	char *str;
	DialogPtr win;

	win = GetNewDialog(dSideRename, NULL, (DialogPtr) -1L);
	while (!done) {
		if (changed) {
			/* Seed the items with the current side names. */
			seed_side_rename_string(win, diSideRenameName, side->name);
			seed_side_rename_string(win, diSideRenameFullName, side->longname);
			seed_side_rename_string(win, diSideRenameAcronym, side->shortname);
			seed_side_rename_string(win, diSideRenameNoun, side->noun);
			seed_side_rename_string(win, diSideRenamePluralNoun, side->pluralnoun);
			seed_side_rename_string(win, diSideRenameAdjective, side->adjective);
			seed_side_rename_string(win, diSideRenameEmblemName, side->emblemname);
			seed_side_rename_string(win, diSideRenameColorScheme, side->colorscheme);
			ShowWindow(win);
			changed = FALSE;
		}
		draw_default_button(win, diSideRenameOK);
		SetCursor(&QD(arrow));
		ModalDialog(NULL, &ditem);
		switch (ditem) {
			case diSideRenameOK:
				/* Actually change the side's slots. */
				net_set_side_name(dside, side, get_side_rename_string(win, diSideRenameName));
				net_set_side_longname(dside, side, get_side_rename_string(win, diSideRenameFullName));
				net_set_side_shortname(dside, side, get_side_rename_string(win, diSideRenameAcronym));
				net_set_side_noun(dside, side, get_side_rename_string(win, diSideRenameNoun));
				net_set_side_pluralnoun(dside, side, get_side_rename_string(win, diSideRenamePluralNoun));
				net_set_side_adjective(dside, side, get_side_rename_string(win, diSideRenameAdjective));
				net_set_side_emblemname(dside, side, get_side_rename_string(win, diSideRenameEmblemName));
				net_set_side_colorscheme(dside, side, get_side_rename_string(win, diSideRenameColorScheme));

				/* Trash and reload all emblem cicns at the top of the heap zone,
				   so as to avoid fragmentation */				
				reinit_all_emblem_images();
				current_colorscheme[side_number(side)] = side->colorscheme;
				dissect_colorscheme(side_number(side));
				ui_update_state();
				save_preferences();
				
				/* Tweak the side menu. */
				update_side_menu(dside);
				/* Force redisplay of everything that might use any side names. */
				force_overall_update();
				/* Fall into next case. */
			case diSideRenameCancel:
				done = TRUE;
				break;
			case diSideRenameRandom:
				net_set_side_name(dside, side, NULL);
				net_set_side_noun(dside, side, NULL);
				/* Always need to clear the plural noun cache before renaming... */
				net_set_side_pluralnoun(dside, side, NULL);
				net_set_side_adjective(dside, side, NULL);
				make_up_side_name(side);
				init_emblem_images(side);
				changed = TRUE;
				break;
		}
	}
	DisposeDialog(win);
	update_all_map_windows();
}

static void
seed_side_rename_string(DialogPtr win, short ditem, char *str)
{
	Str255 tmpstr;
	short itemtype;  Handle itemhandle;  Rect itemrect;

	GetDItem(win, ditem, &itemtype, &itemhandle, &itemrect);
	c2p((str ? str : ""), tmpstr);
	SetIText(itemhandle, tmpstr);
}

static char *
get_side_rename_string(DialogPtr win, short ditem)
{
	char *str;
	short itemtype;  Handle itemhandle;  Rect itemrect;

	GetDItem(win, ditem, &itemtype, &itemhandle, &itemrect);
	str = get_string_from_item(itemhandle);
	if (empty_string(str))
	  str = NULL;
	return str;
}

/* Unit naming/renaming. */

int
unit_rename_dialog(Unit *unit)
{
	short done = FALSE, ditem;
	char *newname;
	char *namer = unit_namer(unit);
	Str255 tmpstr;
	DialogPtr win;
	short itemtype;  Handle itemhandle;  Rect itemrect;

	if (unit == NULL)
	  return FALSE;
	win = GetNewDialog(dRename, NULL, (DialogPtr) -1L);
	/* Seed the text item with the original name. */
	newname = unit->name;
	if (newname == NULL)
	  newname = "";
	GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
	c2p(newname, tmpstr);
	SetIText(itemhandle, tmpstr);
	/* Gray out the random renaming button if no namers available. */
	GetDItem(win, diRenameRandom, &itemtype, &itemhandle, &itemrect);
	HiliteControl((ControlHandle) itemhandle, (!empty_string(namer) ? 0 : 255));
	ShowWindow(win);
	while (!done) {
		draw_default_button(win, diRenameOK);
		SetCursor(&QD(arrow));
		ModalDialog(NULL, &ditem);
		switch (ditem) {
			case diRenameOK:
				GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
				net_set_unit_name(dside, unit, get_string_from_item(itemhandle));
				/* Fall into the next case. */
			case diRenameCancel:
				done = TRUE;
				break;
			case diRenameRandom:
				newname = propose_unit_name(unit);
				if (!empty_string(newname)) {
					GetDItem(win, diRenameName, &itemtype, &itemhandle, &itemrect);
				 	c2p(newname, tmpstr);
					SetIText(itemhandle, tmpstr);
				}
				break;
		}
	}
	DisposeDialog(win);
	return TRUE;
}

/* Unit closeups. */

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

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

void
create_unit_closeup(Unit *unit)
{
	int u, w, h;
	WindowPtr win;
	UnitCloseup *unitcloseup = (UnitCloseup *) xmalloc(sizeof(UnitCloseup));

	if (!active_display(dside) || unit == NULL)
	  return;
	DGprintf("Creating a closeup of %s\n", unit_desig(unit));
	u = unit->type;
	unitcloseup->unit = unit;
	if (hasColorQD) {
		win = GetNewCWindow(wUnitCloseup, nil, (WindowPtr) -1L);
	} else {
		win = GetNewWindow(wUnitCloseup, nil, (WindowPtr) -1L);
	}
	unitcloseup->window = win;
	stagger_window(unitcloseup->window, &lastunitcloseuph, &lastunitcloseupv);
	preferred_closeup_size(u, &w, &h);
	SizeWindow(win, w, h, 1);
	sprintf(spbuf, "Closeup on %s", short_unit_handle(unit));
	add_window_menu_item(spbuf, win);
	unitcloseup->next = unitcloseuplist;
	unitcloseuplist = unitcloseup;
	/* We're now ready to show this closeup to the world. */
	ShowWindow(win);
}

/* Compute the right size for a unit's closeup.  More complicated units
   with more state get bigger closeups. */

void
preferred_closeup_size(int u, int *widp, int *hgtp)
{
	int wid = closeupwinwid, hgt, m, u2, count;

	closeup_spacing = small_line_spacing + 1;
	hgt = 4 + 32 + 4 + 3 * closeup_spacing;
	if (nummrows == NULL) {
		/* Compute and cache the space needed to display each unit's supply. */
		nummrows = xmalloc(numutypes);
		for_all_unit_types(u2) {
			nummrows[u2] = count = 0;
			for_all_material_types(m) {
				if (um_storage_x(u2, m) > 0)
		  		  ++count;
		  	}
		  	if (count > 0) {
		  		nummrows[u2] = max(1, (count + (m_per_row - 1)) / m_per_row);
		  	}
		}
	}
	hgt += nummrows[u] * closeup_spacing;
	if (u_acp(u) > 0)
	  hgt += 5 * closeup_spacing;
	/* (should make some room for tooling state) */
	*widp = wid;  *hgtp = hgt;
}

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

void
draw_unit_closeup(UnitCloseup *unitcloseup)
{
	int u, m, sx = 4, sy, count, mrow, sx2, sy2;
	char infobuf[BUFSIZE];
	Rect tmprect;
	GrafPtr oldport;
	WindowPtr win = unitcloseup->window;
	Unit *unit = unitcloseup->unit;

	if (!active_display(dside))
	  return;
	if (!in_play(unit) || !(side_controls_unit(dside, unit) || endofgame)) {
		/* If the unit is no longer alive and ours, shut down the window. */
		remove_window_menu_item(win);
		destroy_unit_closeup(unitcloseup);
		HideWindow(win);
		return;
	}
	GetPort(&oldport);
	SetPort(win);
	/* 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, sx, sx + 32, sx + 32); 
	EraseRect(&tmprect);
	draw_unit_image(win, tmprect.left, tmprect.top,
					tmprect.right - tmprect.left, tmprect.bottom - tmprect.top,
					u, side_number(unit->side), !completed(unit), TRUE);

	/* 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 name. */
	name_or_number(unit, infobuf);
	MoveTo(44, 4 + closeup_spacing);
	DrawText(infobuf, 0, strlen(infobuf));
	if (Debug || DebugG || DebugM) {
		sprintf(infobuf, " %d ", unit->id);
		MoveTo(closeupwinwid - 30, 15);
		DrawText(infobuf, 0, strlen(infobuf));
	}
	/* Draw the unit's side and type. */
	side_and_type_name(infobuf, dside, u, unit->side);
	MoveTo(44, 4 + closeup_spacing * 2);
	DrawText(infobuf, 0, strlen(infobuf));
	/* Draw the unit's location. */
	location_desc(infobuf, dside, unit, unit->type, unit->x, unit->y);
	sy = 4 + 32 + 4 + closeup_spacing;
	MoveTo(sx, sy);
	DrawText(infobuf, 0, strlen(infobuf));
	/* Very briefly list the numbers and types of the occupants. */
	occupants_desc(infobuf, unit);
	sy += closeup_spacing;
	MoveTo(sx, sy);
	DrawText(infobuf, 0, strlen(infobuf));
	/* Draw the unit's hit points. */
	hp_desc(infobuf, unit, TRUE);
	strcat(infobuf, "   ");
	acp_desc(infobuf + strlen(infobuf), unit, TRUE);
	sy += closeup_spacing;
	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(infobuf, unit);
		sy += closeup_spacing;
		MoveTo(sx, sy);
		DrawText(infobuf, 0, strlen(infobuf));
    	if (plan->maingoal) {
			strcpy(infobuf, "Goal: ");
    		goal_desc(infobuf+strlen(infobuf), plan->maingoal);
			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));
    	}
    	for_all_tasks(plan, task) {
			task_desc(infobuf, unit->side, task);
			sy += closeup_spacing;			
			MoveTo(sx, sy);
			DrawText(infobuf, 0, strlen(infobuf));
		}
	}
	SetPort(oldport);
}


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

int
do_mouse_down_unit_closeup(UnitCloseup *unitcloseup, Point mouse, int mods)
{
	ControlHandle control;
	short part;
	WindowPtr window = unitcloseup->window;

	part = FindControl(mouse, window, &control);
	if (0 /* some control */) {
	} else {
		/* This just forces a redraw of the window - kind of crude. */
		update_unit_display(dside, unitcloseup->unit, TRUE);
		return TRUE;
	}
}

void
destroy_unit_closeup(UnitCloseup *unitcloseup)
{
	UnitCloseup *unitcloseup2;
	
	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);
}

/* History window. */

void
create_history_window()
{
	Rect vscrollrect;

	historywin = GetNewWindow(wHistory, NULL, (WindowPtr) -1L);
	SetPort(historywin);
	TextFont(small_font_id);
	TextSize(small_font_size);
	/* (should calc max based on size of font and height of screen) */
	histcontents = (HistEvent **) xmalloc(maxvishistlines * sizeof(HistEvent *));
	vscrollrect = historywin->portRect;
	vscrollrect.top -= 1;
	vscrollrect.bottom -= sbarwid - 1;
	vscrollrect.left = vscrollrect.right - sbarwid;
	vscrollrect.right += 1;
	histvscrollbar = NewControl(historywin, &vscrollrect, "\p", TRUE,
			 					0, 0, 100, scrollBarProc, 0L);
	total_history_lines = update_total_hist_lines(dside);
	set_history_scrollbar();
	ShowWindow(historywin);
}

static void
calc_history_layout()
{
	total_history_lines = update_total_hist_lines(dside);
	set_history_scrollbar();
	numhistcontents = build_hist_contents(dside, GetCtlValue(histvscrollbar), histcontents,
										  numvishistlines);
}

static void
set_history_scrollbar()
{
	int hgt, oldval, oldmax;
	HistEvent *nexthevt;

	/* Compute the number of lines available for displaying history. */
	hgt = historywin->portRect.bottom - historywin->portRect.top;
	history_line_spacing = small_line_spacing;
	history_top_line_height = 2 * small_line_spacing;
	numvishistlines = (hgt - history_top_line_height - sbarwid) / history_line_spacing;
	/* Tweak the scrollbar appropriately. */
	oldval = GetCtlValue(histvscrollbar);
	oldmax = GetCtlMax(histvscrollbar);
	SetCtlMax(histvscrollbar, max(0, total_history_lines - numvishistlines + 1));
	HiliteControl(histvscrollbar, (GetCtlMax(histvscrollbar) > 0 ? 0 : 255));
	/* If the thumb was present and at max, move it to the new max. */
	if (oldmax > 0 && oldval == oldmax)
	  SetCtlValue(histvscrollbar, GetCtlMax(histvscrollbar));
}

void
draw_history()
{
	int i, n, headdate;
    HistEvent *hevt;
	int numchars;
	char *datestr, hdatebuf[100];

	if (!active_display(dside) || historywin == nil)
	  return;
	numhistcontents = build_hist_contents(dside, GetCtlValue(histvscrollbar), histcontents, numvishistlines);
	/* Draw the header line. */
	MoveTo(2, small_line_spacing);
	headdate = (histcontents[0] ? histcontents[0] : histcontents[1])->startdate; 
	/* (should be relative) */
	datestr = absolute_date_string(headdate);
	sprintf(hdatebuf, "(%s)", datestr);
	/* (should clip to drawing only visible chars) */
	numchars = strlen(hdatebuf);
	DrawText(hdatebuf, 0, numchars);
	/* Now draw each event or date. */
	for (i = 0; i < numhistcontents; ++i) {
		if (histcontents[i] != NULL) {
			draw_historical(histcontents[i], i, TRUE);
		} else {
			draw_historical(histcontents[i+1], i, FALSE);
		}
	}
}

void
draw_historical(HistEvent *hevt, int y, int drawevt)
{
	int hgt, pos, numchars;
	char *datestr, buf[500];

	if (hevt == NULL)
	  return;
	TextFont(small_font_id);
	TextSize(small_font_size);
	pos = history_line_spacing * y + history_top_line_height;
	hgt = historywin->portRect.bottom - historywin->portRect.top;
	/* Don't draw the line if it will intrude on the bottom scrollbar. */
	if (pos + history_line_spacing > hgt - sbarwid)
	  return;
	MoveTo((drawevt ? 20 : 2), pos);
	if (drawevt)
	  historical_event_desc(dside, hevt, buf);
	else {
		datestr = absolute_date_string(hevt->startdate);
		strcpy(buf, datestr);
	}
	/* (should clip to drawing only visible chars) */
	numchars = strlen(buf);
	DrawText(buf, 0, numchars);
}

void
update_history_window(HistEvent *hevt)
{
	HistEvent *prevfirst, *prevsecond;

	prevfirst = histcontents[0];
	prevsecond = histcontents[1];
	SetPort(historywin);
	TextFont(small_font_id);
	TextSize(small_font_size);
	calc_history_layout();
	if (histcontents[0] != prevfirst
	    || histcontents[1] != prevsecond
	    || numvishistlines > total_history_lines) {
		force_update(historywin);
	}
}

static pascal void
history_scroll_fn(ControlHandle control, short code)
{
	int curvalue, maxvalue, pagesize, jump;

	curvalue = GetCtlValue(control);
	maxvalue = GetCtlMax(control);
	pagesize = numvishistlines;
	switch (code) {
		case inPageDown:
			jump = max(1, pagesize - 2);
			break;
		case inDownButton:
			jump = 1;
			break;
		case inPageUp:
			jump = min(-1, - (pagesize - 2));
			break;
		case inUpButton:
			jump = -1;
			break;
		default:
			jump = 0;
			break;
	}
	curvalue = max(min(curvalue + jump, maxvalue), 0);
	SetCtlValue(control, curvalue);
}

void
do_mouse_down_history(Point mouse, int mods)
{
	ControlHandle control;
	short oldval, part, value;

	if (history_scroll_proc == NULL)
	  history_scroll_proc = NewControlActionProc(history_scroll_fn);

	oldval = GetCtlValue(histvscrollbar);

	part = FindControl(mouse, historywin, &control);
	if (control == histvscrollbar) {
		switch (part) {
			case inThumb:
				part = TrackControl(control, mouse, NULL);
				break;
			default:
				part = TrackControl(control, mouse, history_scroll_proc);
				break;
		}
		value = GetCtlValue(control);
		if (value != oldval) {
			Dprintf("New scroll is %d, old was %d\n", value, oldval);
			numhistcontents = build_hist_contents(dside, value, histcontents, numvishistlines);
			force_update(historywin);
		}
	} else {
		/* anything to do here? */
	}
}

/* Grow/shrink the history window to the given size. */

void
grow_history(int h, int v)
{
	EraseRect(&historywin->portRect);
	SizeWindow(historywin, h, v, 1);
	move_history_scrollbar(h, v);
	/* This will force a full redraw at the next update. */
	InvalRect(&historywin->portRect);
}					

/* Zoom the history window to its best maximal size. */

void
zoom_history(int part)
{
	int titleh, vislinesavail;
	Rect zoomrect;
	GDHandle zoomgd;

	EraseRect(&historywin->portRect);
	if (part == inZoomOut) {
		if (hasColorQD) {
			zoomgd = best_zoom_screen(&historywin->portRect);
			zoomrect = (*zoomgd)->gdRect;
			if (zoomgd == GetMainDevice()) {
				zoomrect.top += GetMBarHeight();
			}
		} else {
			/* If no Color QD, then there is only one screen. */
			zoomrect = QD(screenBits).bounds;
			zoomrect.top += GetMBarHeight();
		}
		titleh = 20; /* (should calc) */
		zoomrect.top += titleh;
		InsetRect(&zoomrect, 4, 4);
		/* If not much history, shrink the zoomed window to fit. */
		vislinesavail = (zoomrect.bottom - zoomrect.top - sbarwid) / history_line_spacing;
		total_history_lines = update_total_hist_lines(dside);
		if (vislinesavail > total_history_lines) {
			zoomrect.bottom = zoomrect.top + total_history_lines * history_line_spacing + sbarwid;
		}
		(*((WStateDataHandle) ((WindowPeek) historywin)->dataHandle))->stdState = zoomrect;
	}
	ZoomWindow(historywin, part, (historywin == FrontWindow()));
	move_history_scrollbar(window_width(historywin), window_height(historywin));
	/* This will force a full redraw at the next update. */
	InvalRect(&historywin->portRect);
}

void
move_history_scrollbar(int h, int v)
{
	MoveControl(histvscrollbar, h - sbarwid, 0);
	SizeControl(histvscrollbar, sbarwid + 1, v - sbarwid + 1);
	set_history_scrollbar();
}

/* notice window. */

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

void
notice_dialog()
{
	if (noticewin == nil) {
		create_notice_window();
	}
	ShowWindow(noticewin);
	SelectWindow(noticewin);
}

void
create_notice_window()
{
	int h, v, mainheight;
	Rect destrect, viewrect, vscrollrect, tmprect;

	/* Create the window, color if possible, since images may be in color. */
	if (hasColorQD) {	
		noticewin = GetNewCWindow(wNotice, NULL, (WindowPtr) -1L);
	} else {
		noticewin = GetNewWindow(wNotice, NULL, (WindowPtr) -1L);
	}
	SetPort(noticewin);
	TextFont(small_font_id);
	TextSize(small_font_size);
	h = window_width(noticewin);  v = window_height(noticewin);
	SetRect(&viewrect, 5, 5, h - sbarwid, v - sbarwid); 
	destrect = viewrect;
	notice_text = TENew(&destrect, &destrect);
	/* Set up a vertical scrollbar. */
	vscrollrect = noticewin->portRect;
	vscrollrect.top = 5;
	vscrollrect.bottom -= sbarwid - 1;
	vscrollrect.left = vscrollrect.right - sbarwid;
	vscrollrect.right += 1;
	notice_v_scrollbar =
		NewControl(noticewin, &vscrollrect, "\p", TRUE, 0, 0, 0, scrollBarProc, 0L);
	HiliteControl(notice_v_scrollbar, 0);

// --- WINDOW MENU BUG FIX ---------------------------------------

	/* The Notices window is a permanent item (#2) so it should not be added here. */

//	add_window_menu_item("Notices", noticewin);

// ------------------------------------------------------------

	if (1 /* position notices at bottom of main screen */) {
		get_main_screen_size(NULL, &mainheight);
		tmprect = noticewin->portRect;
		MoveWindow(noticewin,
				   4,
				   mainheight - (tmprect.bottom - tmprect.top) - 3,
				   FALSE);
	}
}

void
append_notice(char *str)
{
	/* Delete old notices. */
	if (((*notice_text)->teLength) > 30000) {
		TESetSelect(0, (*notice_text)->teLength - 30000, notice_text);
		TEDelete(notice_text);
	}
#if defined(THINK_C) || defined(__MWERKS__)
	/* Hack up newlines so that TextEdit recognizes them. */
	{
		int i;
	
		for (i = 0; i < strlen(str); ++i) {
			if (str[i] == '\n')
			  str[i] = '\r';
		}
	}
#endif
	TESetSelect(32767, 32767, notice_text);
	TEInsert(str, strlen(str), notice_text);
	TEInsert("\r", strlen("\r"), notice_text);
	TESetSelect(32767, 32767, notice_text);
	(*notice_text)->destRect = (*notice_text)->viewRect;
	/* Update on the screen. */
	adjust_notice_scrollbar();
	draw_notice();
}

void
draw_notice()
{
	Rect tmprect;
	GrafPtr oldport;

	GetPort(&oldport);
	SetPort(noticewin);
	TextFont(small_font_id);
	TextSize(small_font_size);
	SetRect(&tmprect, 5, 40, 5 + 32, 40 + 32);
	EraseRect(&tmprect);
	TEUpdate(&(noticewin->portRect), notice_text);
	SetPort(oldport);
	adjust_notice_scrollbar();
}

void
adjust_notice_scrollbar()
{
	int lines, oldmax, newmax, oldvalue, newvalue;

	oldvalue = GetCtlValue(notice_v_scrollbar);
	oldmax = GetCtlMax(notice_v_scrollbar);
	lines = (*notice_text)->nLines;
	/* Account for a return at the end of the text. */
	if (*(*(*notice_text)->hText + (*notice_text)->teLength - 1) == 0x0d)
	  ++lines;
	newmax = lines - (((*notice_text)->viewRect.bottom - (*notice_text)->viewRect.top)
					 / (*notice_text)->lineHeight);
	if (newmax < 0)
	  newmax = 0;
	SetCtlMax(notice_v_scrollbar, newmax);
	if (oldvalue == oldmax) {
		/* If the thumb was at max, move it to the new max. */
		newvalue = newmax;
	} else {
		/* Otherwise adjust it proportionally. */
		newvalue = ((*notice_text)->viewRect.top - (*notice_text)->destRect.top)
					/ (*notice_text)->lineHeight;
		if (newvalue < 0)
		  newvalue = 0;
		if (newvalue > newmax)
		  newvalue = newmax;
	}
	SetCtlValue(notice_v_scrollbar, newvalue);
	TEScroll(0, ((*notice_text)->viewRect.top - (*notice_text)->destRect.top)
				 - (GetCtlValue(notice_v_scrollbar) * (*notice_text)->lineHeight),
			 notice_text);
}

void
activate_notice(int activate)
{
	HiliteControl(notice_v_scrollbar, (activate ? 0 : 255));
	if (activate)
	  TEActivate(notice_text);
	else
	  TEDeactivate(notice_text);
}

static pascal void
notice_vscroll_fn(ControlHandle control, short code)
{
	int oldvalue, curvalue, minvalue, maxvalue, pagesize, jump;

	curvalue = GetCtlValue(control);
	minvalue = GetCtlMin(control);
	maxvalue = GetCtlMax(control);
	pagesize = ((*notice_text)->viewRect.bottom - (*notice_text)->viewRect.top) /
				(*notice_text)->lineHeight;
	if (pagesize > 1)
	  pagesize -= 1;
	switch (code) {
		case inPageDown:
			jump = pagesize;
			break;
		case inDownButton:
			jump = 1;
			break;
		case inPageUp:
			jump = - pagesize;
			break;
		case inUpButton:
			jump = -1;
			break;
		default:
			jump = 0;
			break;
	}
	oldvalue = curvalue;
	curvalue = max(min(curvalue + jump, maxvalue), minvalue);
	SetCtlValue(control, curvalue);
	/* Calculate the actual jump and use it to adjust the text. */
	jump = curvalue - oldvalue;
	if (jump != 0)
	  TEScroll(0, - jump * (*notice_text)->lineHeight, notice_text);
}

/* Respond to an event occurring in the notice window. */

void
do_mouse_down_notice(Point mouse, int mods)
{
	ControlHandle control;
	short part, value;

	if (notice_vscroll_proc == NULL)
	  notice_vscroll_proc = NewControlActionProc(notice_vscroll_fn);

	part = FindControl(mouse, noticewin, &control);
	if (control == notice_v_scrollbar) {
		if (part != 0) {
			switch (part) {
				case inPageDown:
				case inDownButton:
				case inPageUp:
				case inUpButton:
					value = TrackControl(control, mouse, notice_vscroll_proc);
					break;
				case inThumb:
					value = GetCtlValue(control);
					if ((part = TrackControl(control, mouse, nil)) != 0) {
						value -= GetCtlValue(control);
						if (value != 0) {
							TEScroll(0, value * (*notice_text)->lineHeight, notice_text);
						}
					}
					break;
			}
		}
	} else if (PtInRect(mouse, &((*notice_text)->viewRect))) {
		TEClick(mouse, 0, notice_text);
	}
}

void
grow_notice(int h, int v)
{
	EraseRect(&noticewin->portRect);
	SizeWindow(noticewin, h, v, 1);
	MoveControl(notice_v_scrollbar, h - sbarwid, 5);
	SizeControl(notice_v_scrollbar, sbarwid + 1, v - 5 - sbarwid + 1);
	(*notice_text)->viewRect.right = h - sbarwid;
	(*notice_text)->viewRect.bottom = v - sbarwid;
	(*notice_text)->destRect.right = h - sbarwid;
	TECalText(notice_text);
	InvalRect(&noticewin->portRect);
}					

void
zoom_notice(int part)
{
	int titleh, h, v;
	Rect zoomrect;
	GDHandle gd, zoomgd;

	EraseRect(&noticewin->portRect);
	if (part == inZoomOut) {
		if (hasColorQD) {
			zoomgd = best_zoom_screen(&noticewin->portRect);
			zoomrect = (*zoomgd)->gdRect;
			if (zoomgd == GetMainDevice()) {
				zoomrect.top += GetMBarHeight();
			}
			InsetRect(&zoomrect, 3, 3);
		} else {
			/* If no Color QD, then there is only the one screen. */
			zoomrect = QD(screenBits).bounds;
			zoomrect.top += GetMBarHeight();
			InsetRect(&zoomrect, 4, 4);
		}
		titleh = 20; /* (should calc) */
		zoomrect.top += titleh;
		(*((WStateDataHandle) ((WindowPeek) noticewin)->dataHandle))->stdState = zoomrect;
	}
	ZoomWindow(noticewin, part, (noticewin == FrontWindow()));
	h = window_width(noticewin);  v = window_height(noticewin);
	MoveControl(notice_v_scrollbar, h - sbarwid, 0);
	SizeControl(notice_v_scrollbar, sbarwid + 1, v - sbarwid + 1);
	adjust_notice_scrollbar();
	(*notice_text)->viewRect.right = h - sbarwid;
	(*notice_text)->viewRect.bottom = v - sbarwid;
	(*notice_text)->destRect.right = h - sbarwid;
	TECalText(notice_text);
	/* This will force a full redraw at the next update. */
	InvalRect(&noticewin->portRect);
}

/* scores window. */

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

void
scores_dialog()
{
    extern char *get_scores(Side *side);

	if (scoreswin == nil) {
		create_scores_window();
		append_scores(get_scores(dside));
	}
	ShowWindow(scoreswin);
	SelectWindow(scoreswin);
}

void
create_scores_window()
{
	int h, v;
	Rect destrect, viewrect, vscrollrect;

	/* Create the window, color if possible, since images may be in color. */
	if (hasColorQD) {	
		scoreswin = GetNewCWindow(wScores, NULL, (WindowPtr) -1L);
	} else {
		scoreswin = GetNewWindow(wScores, NULL, (WindowPtr) -1L);
	}
	SetPort(scoreswin);
	TextFont(small_font_id);
	TextSize(small_font_size);
	h = window_width(scoreswin);  v = window_height(scoreswin);
	SetRect(&viewrect, 5, 5, h - sbarwid, v - sbarwid); 
	destrect = viewrect;
	scores_text = TENew(&destrect, &destrect);
	/* Set up a vertical scrollbar. */
	vscrollrect = scoreswin->portRect;
	vscrollrect.top = 5;
	vscrollrect.bottom -= sbarwid - 1;
	vscrollrect.left = vscrollrect.right - sbarwid;
	vscrollrect.right += 1;
	scores_v_scrollbar =
		NewControl(scoreswin, &vscrollrect, "\p", TRUE, 0, 0, 0, scrollBarProc, 0L);
	HiliteControl(scores_v_scrollbar, 0);
	add_window_menu_item("Scores", scoreswin);
}

void
append_scores(char *str)
{
	/* Delete old scoress. */
	if (((*scores_text)->teLength) > 30000) {
		TESetSelect(0, (*scores_text)->teLength - 30000, scores_text);
		TEDelete(scores_text);
	}
#if defined(THINK_C) || defined(__MWERKS__)
	/* Hack up newlines so that TextEdit recognizes them. */
	{
		int i;
	
		for (i = 0; i < strlen(str); ++i) {
			if (str[i] == '\n')
			  str[i] = '\r';
		}
	}
#endif
	TESetSelect(32767, 32767, scores_text);
	TEInsert(str, strlen(str), scores_text);
	TEInsert("\r", strlen("\r"), scores_text);
	TESetSelect(32767, 32767, scores_text);
	(*scores_text)->destRect = (*scores_text)->viewRect;
	/* Update on the screen. */
	adjust_scores_scrollbar();
	draw_scores();
}

void
draw_scores()
{
	Rect tmprect;
	GrafPtr oldport;

	GetPort(&oldport);
	SetPort(scoreswin);
	TextFont(small_font_id);
	TextSize(small_font_size);
	SetRect(&tmprect, 5, 40, 5 + 32, 40 + 32);
	EraseRect(&tmprect);
	TEUpdate(&(scoreswin->portRect), scores_text);
	SetPort(oldport);
	adjust_scores_scrollbar();
}

void
adjust_scores_scrollbar()
{
	int lines, oldmax, newmax, oldvalue, newvalue;

	oldvalue = GetCtlValue(scores_v_scrollbar);
	oldmax = GetCtlMax(scores_v_scrollbar);
	lines = (*scores_text)->nLines;
	/* Account for a return at the end of the text. */
	if (*(*(*scores_text)->hText + (*scores_text)->teLength - 1) == 0x0d)
	  ++lines;
	newmax = lines - (((*scores_text)->viewRect.bottom - (*scores_text)->viewRect.top)
					 / (*scores_text)->lineHeight);
	if (newmax < 0)
	  newmax = 0;
	SetCtlMax(scores_v_scrollbar, newmax);
	if (oldvalue == oldmax) {
		/* If the thumb was at max, move it to the new max. */
		newvalue = newmax;
	} else {
		/* Otherwise adjust it proportionally. */
		newvalue = ((*scores_text)->viewRect.top - (*scores_text)->destRect.top)
					/ (*scores_text)->lineHeight;
		if (newvalue < 0)
		  newvalue = 0;
		if (newvalue > newmax)
		  newvalue = newmax;
	}
	SetCtlValue(scores_v_scrollbar, newvalue);
	TEScroll(0, ((*scores_text)->viewRect.top - (*scores_text)->destRect.top)
				 - (GetCtlValue(scores_v_scrollbar) * (*scores_text)->lineHeight),
			 scores_text);
}

void
activate_scores(int activate)
{
	HiliteControl(scores_v_scrollbar, (activate ? 0 : 255));
	if (activate)
	  TEActivate(scores_text);
	else
	  TEDeactivate(scores_text);
}

static pascal void
scores_vscroll_fn(ControlHandle control, short code)
{
	int oldvalue, curvalue, minvalue, maxvalue, pagesize, jump;

	curvalue = GetCtlValue(control);
	minvalue = GetCtlMin(control);
	maxvalue = GetCtlMax(control);
	pagesize = ((*scores_text)->viewRect.bottom - (*scores_text)->viewRect.top) /
				(*scores_text)->lineHeight;
	if (pagesize > 1)
	  pagesize -= 1;
	switch (code) {
		case inPageDown:
			jump = pagesize;
			break;
		case inDownButton:
			jump = 1;
			break;
		case inPageUp:
			jump = - pagesize;
			break;
		case inUpButton:
			jump = -1;
			break;
		default:
			jump = 0;
			break;
	}
	oldvalue = curvalue;
	curvalue = max(min(curvalue + jump, maxvalue), minvalue);
	SetCtlValue(control, curvalue);
	/* Calculate the actual jump and use it to adjust the text. */
	jump = curvalue - oldvalue;
	if (jump != 0)
	  TEScroll(0, - jump * (*scores_text)->lineHeight, scores_text);
}

/* Respond to an event occurring in the scores window. */

void
do_mouse_down_scores(Point mouse, int mods)
{
	ControlHandle control;
	short part, value;

	if (scores_vscroll_proc == NULL)
	  scores_vscroll_proc = NewControlActionProc(scores_vscroll_fn);

	part = FindControl(mouse, scoreswin, &control);
	if (control == scores_v_scrollbar) {
		if (part != 0) {
			switch (part) {
				case inPageDown:
				case inDownButton:
				case inPageUp:
				case inUpButton:
					value = TrackControl(control, mouse, scores_vscroll_proc);
					break;
				case inThumb:
					value = GetCtlValue(control);
					if ((part = TrackControl(control, mouse, nil)) != 0) {
						value -= GetCtlValue(control);
						if (value != 0) {
							TEScroll(0, value * (*scores_text)->lineHeight, scores_text);
						}
					}
					break;
			}
		}
	} else if (PtInRect(mouse, &((*scores_text)->viewRect))) {
		TEClick(mouse, 0, scores_text);
	}
}

void
grow_scores(int h, int v)
{
	EraseRect(&scoreswin->portRect);
	SizeWindow(scoreswin, h, v, 1);
	MoveControl(scores_v_scrollbar, h - sbarwid, 5);
	SizeControl(scores_v_scrollbar, sbarwid + 1, v - 5 - sbarwid + 1);
	(*scores_text)->viewRect.right = h - sbarwid;
	(*scores_text)->viewRect.bottom = v - sbarwid;
	(*scores_text)->destRect.right = h - sbarwid;
	TECalText(scores_text);
	InvalRect(&scoreswin->portRect);
}					

void
zoom_scores(int part)
{
	int titleh, h, v;
	Rect zoomrect;
	GDHandle gd, zoomgd;

	EraseRect(&scoreswin->portRect);
	if (part == inZoomOut) {
		if (hasColorQD) {
			zoomgd = best_zoom_screen(&scoreswin->portRect);
			zoomrect = (*zoomgd)->gdRect;
			if (zoomgd == GetMainDevice()) {
				zoomrect.top += GetMBarHeight();
			}
			InsetRect(&zoomrect, 3, 3);
		} else {
			/* If no Color QD, then there is only the one screen. */
			zoomrect = QD(screenBits).bounds;
			zoomrect.top += GetMBarHeight();
			InsetRect(&zoomrect, 4, 4);
		}
		titleh = 20; /* (should calc) */
		zoomrect.top += titleh;
		(*((WStateDataHandle) ((WindowPeek) scoreswin)->dataHandle))->stdState = zoomrect;
	}
	ZoomWindow(scoreswin, part, (scoreswin == FrontWindow()));
	h = window_width(scoreswin);  v = window_height(scoreswin);
	MoveControl(scores_v_scrollbar, h - sbarwid, 0);
	SizeControl(scores_v_scrollbar, sbarwid + 1, v - sbarwid + 1);
	adjust_scores_scrollbar();
	(*scores_text)->viewRect.right = h - sbarwid;
	(*scores_text)->viewRect.bottom = v - sbarwid;
	(*scores_text)->destRect.right = h - sbarwid;
	TECalText(scores_text);
	/* This will force a full redraw at the next update. */
	InvalRect(&scoreswin->portRect);
}
