/* Map interaction and misc 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 int side_owns_occupant PARAMS ((Side *side, Unit *unit));
#include "kpublic.h"
#include "macconq.h"
extern void set_position_modally(void);

#undef DEF_CMD
#define DEF_CMD(letter,name,args,FN,help) extern void FN(void);

#include "cmd.def"

#include "maccmd.def"

/* Adjust viewport for offscreen drawing and go offscreen.
   Then call draw_other_maps to erase other-map boxes */

#define GO_OFFSCREEN(m) {  \
	(m)->vp->sx -= (m)->offsetx + (m)->bufx - (m)->conw; \
	(m)->vp->sy -= (m)->offsety + (m)->bufy - (m)->toph; \
	LockPixels(GetGWorldPixMap((m)->gworldPortPtr)); \
	SetPort((GrafPtr)(m)->gworldPortPtr); \
	if ((m)->vp->draw_other_maps) draw_other_maps(m);\
}

/* Call draw_other_maps to redraw other-map boxes, then
   reset viewport for onscreen drawing and return onscreen. */

#define RETURN_ONSCREEN(m) {  \
	if ((m)->vp->draw_other_maps) draw_other_maps(m);\
	(m)->vp->sx += (m)->offsetx + (m)->bufx - (m)->conw; \
	(m)->vp->sy += (m)->offsety + (m)->bufy - (m)->toph; \
	UnlockPixels(GetGWorldPixMap((m)->gworldPortPtr)); \
	SetPort((GrafPtr)(m)->windowPortPtr); \
  }

/* These two macros are now used only in draw_selection_animation. */

#define SHIFT_ORIGIN(m) {  \
  (m)->osx = (m)->vp->sx - (m)->conw;  (m)->osy = (m)->vp->sy - (m)->toph;  \
  SetOrigin((m)->osx, (m)->osy);  \
  }

#define RESET_ORIGIN(m) {  \
  (m)->osx = 0;  (m)->osy = 0;  \
  SetOrigin((m)->osx, (m)->osy);  \
  }

/* These three macros are used to handle cases where an item stretches across the */
/* edge of a wrapped gworld. It is then necessary to draw it twice, once at each end. */
/* The macros should be applied to all drawing functions that take an sx value, rect */
/* or poly as a parameter. */

/* Repeat (function) once using sx shifted one area width to the right. */
#define WRAP_SX(map, sx, function) { \
	(function); \
	if (area.xwrap && (sx) < 0) { \
		(sx) += (map)->vp->sxmax; \
		(function); \
		(sx) -= (map)->vp->sxmax; \
	} \
}

/* Repeat (function) once using rect offset one area width to the right. */
#define WRAP_RECT(map, rect, function) { \
	(function); \
	if (area.xwrap && (rect).left < 0) { \
		OffsetRect(&(rect), (map)->vp->sxmax, 0); \
		(function); \
		OffsetRect(&(rect), -(map)->vp->sxmax, 0); \
	} \
}

/* Repeat (function) once using poly offset one area width to the right. */
#define WRAP_POLY(map, poly, function) { \
	(function); \
	if (area.xwrap && (*(*(poly))).polyBBox.left < 0) { \
		OffsetPoly((poly), (map)->vp->sxmax, 0); \
		(function); \
		OffsetPoly((poly), -(map)->vp->sxmax, 0); \
	} \
}

extern void draw_unit_names_only(Map *map, int x, int y);
extern void draw_gridlines(Map *map, int x0, int y, int len);
extern void draw_simple_feature_borders(Map *map, int x0, int y0, int len);
extern void draw_shorelines(Map *map, int x0, int y0, int len);
extern void draw_unit_names_only(Map *map, int x, int y);

extern Map *frontmap;

extern int mapnum, nummaps;

int topunithgt;

/* This is a temporary used by the map scroll proc. */

Map *curmap;

ControlActionUPP map_scroll_proc;

/* This scroll proc is shared by both the horizontal and vertical scrollbars. */

static pascal void
map_scroll_fn(ControlHandle control, short code)
{
	int pagesize, jump;

	/* The page jump should be most but not all of a screenful. */
	if (control == curmap->hscrollbar)
	  pagesize = (3 * curmap->vp->pxw) / 4;
	else
	  pagesize = (3 * curmap->vp->pxh) / 4;
	/* Adjust the pagesize to always be a multiple of 4. */
	pagesize = ((pagesize + 3) / 4) * 4;

	switch (code) {
		case inPageDown:
			jump = pagesize;
			break;
		case inDownButton:
			jump = 4;
			break;
		case inPageUp:
			jump = 0 - pagesize;
			break;
		case inUpButton:
			jump = -4;
			break;
		default:
			jump = 0;
			break;
	}
	if (control == curmap->hscrollbar) {
		scroll_map_window(curmap, jump, 0);
	} else if (control == curmap->vscrollbar){
		scroll_map_window(curmap, 0, jump);
	}
}

/* Handle a mouse down in the map window. */

void
do_mouse_down_map(Map *map, Point mouse, int mods)
{
	short part;
	int newsx, newsy, dummy;
	ControlHandle control;

	if (map_scroll_proc == NULL) 
	  map_scroll_proc = NewControlActionProc(map_scroll_fn);
	part = FindControl(mouse, map->window, &control);

	/* Horizontal scrollbar */
	if (control == map->hscrollbar) {
		switch (part) {
			case inThumb:
				part = TrackControl(control, mouse, NULL);
				if (part == inThumb) {
					/* Get apparent newsx from the new thumb position */
					newsx = GetCtlValue(control);
					/* Correct newsx for scrollbar scaling used to fix the 32K limit bug */				
					dummy = map->vp->sxmax;
					while (dummy > 32000) {	
						dummy /= 2;
						newsx *= 2;	/* Simulates the scaling but in reverse */
					}		
					/* Scroll the map window within its gworld */
					scroll_map_window(map, newsx - map->vp->sx, 0);
				}
				break;
			default:
				curmap = map;
				part = TrackControl(control, mouse, map_scroll_proc);
				break;
		}

	/* Vertical scrollbar */
	} else if (control == map->vscrollbar) {
		switch (part) {
			case inThumb:
				part = TrackControl(control, mouse, NULL);
				if (part == inThumb) {
					/* Get apparent newsy from the new thumb position */
					newsy = GetCtlValue(control);
					/* Correct newsy for scrollbar scaling used to fix the 32K limit bug */				
					dummy = map->vp->symax;
					while (dummy > 32000) {	
						dummy /= 2;
						newsy *= 2;	/* Simulates the scaling but in reverse */
					}		
					/* Scroll the map window within its gworld */
					scroll_map_window(map, 0, newsy - map->vp->sy);
				}
				break;
			default:
				curmap = map;
				part = TrackControl(control, mouse, map_scroll_proc);
				break;
		}
	} else if (mouse.h <= conwid) {
		/* Interpret as a control panel hit. */
		do_mouse_down_map_control_panel(map, mouse.h, mouse.v, mods);
	} else {
		do_mouse_down_map_content(map, mouse.h, mouse.v, mods);
	}
}

void
do_mouse_down_map_control_panel(Map *map, int h, int v, int mods)
{
	int winh = map->window->portRect.bottom - map->window->portRect.top;

	/* (should better organize tests here) */
	if (between(winh - 2 * sbarwid, v, winh)) {
		switch ((winh - v) / sbarwid) {
			case 0:
				magnify_map(map, ((h < conwid / 2) ? -1 : 1));
				break;
#if 0
			/* This was too confusing here, so it's now flushed.  However, the
			   capability still seems worthwhile, so it should reappear elsewhere. */
			case 1:
				map_modal = ZOOM_MODAL;
				break;
#endif
		}
	} else if (v < 32) {
		toggle_survey(map);
	} else if ((v - 32) < 5 * 15) {
		switch ((v - 32) / 15) {
			case 0:
				if (h < conwid / 2) {
					select_previous_awake_mover(map);
				} else {
					select_next_awake_mover(map);
				}
				break;
			case 1:
				if (h < conwid / 2) {
					select_previous_mover(map);
				} else {
					select_next_mover(map);
				}
				break;
			case 2:
				if (h < conwid / 2) {
					select_previous_actor(map);
				} else {
					select_next_actor(map);
				}
				break;
			case 3:
				if (h < conwid / 2) {
					select_previous_unit(map);
				} else {
					select_next_unit(map);
				}
				break;
			case 4:
				beep();
				break;
		}

	} else if (v - 32 - 5*15 - 2 - 7/*why?*/ < 9 * 11) {
		switch ((v - 32 - 5*15 - 2 - 7/*why?*/) / 11) {

			case 0:
				toggle_map_grid(map);
				break;
			case 1:
				map->vp->draw_names = !map->vp->draw_names;
				/* Also toggle featurenames if it had the same old setting as drawnames */
				/* but let it alone if the setting already was different */
				if (map->featurenames != map->vp->draw_names)
			 	      map->featurenames = !map->featurenames;
				force_map_update(map);
				break;
			case 2:
				toggle_map_people(map);
				break;
			case 3:
				toggle_map_control(map);
				break;
			case 4:
				toggle_map_plans(map);
				break;
			case 5:
				toggle_map_ai(map);
				break;
			case 6:
				if (dside->may_set_see_all) {
					map->see_all = !map->see_all;
					force_map_update(map);
				}
				break;
			case 7:
				toggle_map_sidecolors(map);
				break;
			case 8:
				map->iconmasks = !map->iconmasks;
				/* Also toggle boxmasks if it had the same old setting as iconmasks */
				/* but let it alone if the setting already was different */
				if (map->boxmasks != map->iconmasks)
				      map->boxmasks = !map->boxmasks;
				/* Also toggle textmasks if it had the same old setting as iconmasks */
				/* but let it alone if the setting already was different */
				if (map->textmasks != map->iconmasks)
				      map->textmasks = !map->textmasks;
				force_map_update(map);
				break;
		}
	} else {
		/* Unused area, ignore */
	}
}

void
toggle_survey(Map *map)
{
	int i;
	Unit *unit;

	map->moveonclick = !map->moveonclick;
	map->autoselect = !map->autoselect;
	draw_control_panel(map);
	if (map->autoselect) {
		if (map->numselections > 0) {
			for (i = 0; i < map->numselections; ++i) {
				unit = map->selections[i];
				if (unit != NULL) {
					map->curunit = autonext_unit(dside, unit);
					select_exactly_one_unit(map, map->curunit);
				}
			}
		}
	} else {
		/* Update appearance of selections. */
		draw_selections(map);
	}
}

void
magnify_map(Map *map, int inout)
{
	set_map_mag(map, map->vp->power + inout);
}

/* This sets the map's magnification directly and updates it. */

void
set_map_mag(Map *map, int newpower)
{
	newpower = clip_to_limits(0, newpower, NUMPOWERS-1);
	if (map->vp->power != newpower) {
	
		/* Erase other-map boxes in other windows */
		draw_related_maps(map);
		
		set_map_power(map, newpower);
		m_center_on_focus(map);
		set_map_scrollbars(map);

		/* Update the gworld (and the map) */
		update_gworld(map);

		/* Redraw other-map boxes in other windows */
		draw_related_maps(map);
		
	}
}

void
toggle_map_grid(Map *map)
{
	map->vp->draw_grid = !map->vp->draw_grid;
	/* (should not do a total redraw?) */
	force_map_update(map);
}

void
toggle_map_topline(Map *map)
{
	int oldtoph = map->toph;
	Rect tmprect;
	RgnHandle tmprgn;
	GrafPtr oldport;

	map->draw_topline = !map->draw_topline;
	map->toplineh = (map->draw_topline ? tophgt : 0);
	/* Erase other-map boxes in other windows. */
	draw_related_maps(map);
	map->toph = map->toplineh + map->topunith;
#if 0
	set_content_rect(map);
	if (map->toplineh) {
 		GetPort(&oldport);
		SetPort(map->window);
		tmprgn = NewRgn();
		tmprect = map->window->portRect;
		tmprect.left += conwid;
		tmprect.right -= sbarwid;  tmprect.bottom -= sbarwid;
		ScrollRect(&tmprect, 0, map->toplineh, tmprgn);
		draw_top_line(map);
		InvalRgn(tmprgn);
		DisposeRgn(tmprgn);
		SetPort(oldport);
	}
	/* Let update_gworld handle both cases. */
	update_gworld(map);
#endif

	/* Update without scrolling if we are expanding and the buffer is too small. */ 
	if (map->bufy - map->offsety < oldtoph - map->toph) {
		set_content_rect(map);
		update_gworld(map);
		/* This is not done by update_gworld (unlike update_resized_gworld). */
		if (map->toplineh > 0)
		  draw_top_line(map);
		if (map->topunith > 0)
		  draw_unit_info(map);
	} else {
		/* Always scroll the map if we are shrinking, or if the buffer is big enough. */
		update_resized_map(map);
	}

	/* Redraw other-map boxes in other windows. */
	draw_related_maps(map);
}

void
toggle_map_topunit(Map *map)
{
	int oldtoph;
	Rect tmprect;
	RgnHandle tmprgn;
	GrafPtr oldport;

	oldtoph = map->toph;
	map->draw_topunit = !map->draw_topunit;
	map->topunith = (map->draw_topunit ? topunithgt : 0);
	/* Erase other-map boxes in other windows. */
	draw_related_maps(map);
	map->toph = map->toplineh + map->topunith;
	set_content_rect(map);
	if (map->topunith) {
 		GetPort(&oldport);
		SetPort(map->window);
		tmprect = map->window->portRect;
		tmprect.left += conwid;  tmprect.top += oldtoph;
		tmprect.right -= sbarwid;  tmprect.bottom -= sbarwid;
		tmprgn = NewRgn();
		ScrollRect(&tmprect, 0, map->topunith, tmprgn);
		InvalRgn(tmprgn);
		DisposeRgn(tmprgn);
		draw_unit_info(map);
		SetPort(oldport);
	}
	/* Let update_gworld handle both cases. */
	update_gworld(map);

	/* Redraw other-map boxes in other windows. */
	draw_related_maps(map);
}

void
toggle_map_other_maps(Map *map)
{
	map->vp->draw_other_maps = !map->vp->draw_other_maps;
	/* (should not do a total redraw) */
	force_map_update(map);
}

void
toggle_map_lighting(Map *map)
{
	map->vp->draw_lighting = !map->vp->draw_lighting;
	/* We have to do a total redraw. */
	force_map_update(map);
}

void
toggle_map_coverage(Map *map)
{
	map->vp->draw_cover = !map->vp->draw_cover;
	/* (should only change newly dimmed/lightened cells) */
	force_map_update(map);
}

void
toggle_map_names(Map *map)
{
	map->vp->draw_names = !map->vp->draw_names;
	/* (if now on, should draw names on top of everything, don't redraw everything) */
	if (map->vp->hh > 5) {
		force_map_update(map);
	} else {
		/* (should be a force update on control panel alone) */
		draw_control_panel(map);
	}
}

void
toggle_map_people(Map *map)
{
    if (!people_sides_defined())
      return;
	map->vp->draw_people = !map->vp->draw_people;
	if (bwid2[map->vp->power] > 0) {
		force_map_update(map);
	} else {
		/* (should be a force update on control panel alone) */
		draw_control_panel(map);
	}
}

void
toggle_map_control(Map *map)
{
    if (!control_sides_defined())
      return;
	map->vp->draw_control = !map->vp->draw_control;
	if (bwid2[map->vp->power] > 0) {
		force_map_update(map);
	} else {
		/* (should be a force update on control panel alone) */
		draw_control_panel(map);
	}
}

void
toggle_map_elevations(Map *map)
{
	map->vp->draw_elevations = !map->vp->draw_elevations;
	force_map_update(map);
}

void
toggle_map_materials(Map *map, int m)
{
	map->vp->draw_materials[m] = !map->vp->draw_materials[m];
	map->vp->num_materials_to_draw += (map->vp->draw_materials[m] ? 1 : -1);
	force_map_update(map);
}

void
toggle_map_aux_terrain(Map *map, int t)
{
	map->vp->draw_aux_terrain[t] = !map->vp->draw_aux_terrain[t];
	force_map_update(map);
}

void
toggle_map_temperature(Map *map)
{
	map->vp->draw_temperature = !map->vp->draw_temperature;
	force_map_update(map);
}

void
toggle_map_winds(Map *map)
{
	map->vp->draw_winds = !map->vp->draw_winds;
	force_map_update(map);
}

void
toggle_map_clouds(Map *map)
{
	map->vp->draw_clouds = !map->vp->draw_clouds;
	force_map_update(map);
}

void
toggle_map_storms(Map *map)
{
	map->vp->draw_storms = !map->vp->draw_storms;
	force_map_update(map);
}

void
toggle_map_plans(Map *map)
{
	map->vp->draw_plans = !map->vp->draw_plans;
	if (map->numselections > 0) {
		force_map_update(map);
	} else {
		/* (should be a force update on control panel alone) */
		draw_control_panel(map);
	}
}

void
toggle_map_ai(Map *map)
{
	if (!side_has_ai(dside))
	  return;
	map->vp->draw_ai = !map->vp->draw_ai;
	force_map_update(map);
}

void
toggle_map_sidecolors(Map *map)
{
	map->sidecolors = !map->sidecolors;
	force_map_update(map);
}

void
toggle_iconmasks(Map *map)
{
	map->iconmasks = !map->iconmasks;
	force_map_update(map);
}

void
toggle_boxmasks(Map *map)
{
	map->boxmasks = !map->boxmasks;
	force_map_update(map);
}

void
toggle_textmasks(Map *map)
{
	map->textmasks = !map->textmasks;
	force_map_update(map);
}

void
toggle_featureborders(Map *map)
{
	map->featureborders = !map->featureborders;
	force_map_update(map);
}

void
toggle_featurenames(Map *map)
{
	map->featurenames = !map->featurenames;
	force_map_update(map);
}

void
toggle_shorelines(Map *map)
{
	map->shorelines = !map->shorelines;
	force_map_update(map);
}

void
toggle_simple_borders(Map *map)
{
	map->simple_borders = !map->simple_borders;
	force_map_update(map);
}

void
toggle_optimize_fonts(Map *map)
{
	map->optimize_fonts = !map->optimize_fonts;
	force_map_update(map);
}

void
toggle_draw_latlong(Map *map)
{
	map->vp->draw_latlong = !map->vp->draw_latlong;
	force_map_update(map);
}

void
toggle_solid_color_terrain(Map *map)
{
	map->solid_color_terrain = !map->solid_color_terrain;
	force_map_update(map);
}

void
toggle_erase_names(Map *map)
{
	map->erase_names = !map->erase_names;
	force_map_update(map);
}

static int selrect;
static int downx, downy, downdir;

void drag_for_distance(Map *map, int h0, int v0);

void
do_mouse_down_map_content(Map *map, int h, int v, int mods)
{
	int i, rslt, anysuccess;
	Unit *unit;
	
	/* Remember this cell. */
	m_nearest_cell(map, h, v, &downx, &downy);
	/* Assume that last place clicked is a reasonable focus. */
	if (inside_area(downx, downy)) {
		map->vp->vcx = downx;  map->vp->vcy = downy;
	}
	if (map_modal != NO_MODAL) {
		switch (map_modal) {
			case ATTACK_MODAL:
				set_position_modally();
				do_attack();
				break;
			case FIRE_MODAL:
				set_position_modally();
				do_fire();
				break;
			case FIRE_INTO_MODAL:
				set_position_modally();
				do_fire_into();
				break;
			case SET_FORMATION_MODAL:
				set_position_modally();
				do_set_formation();
				break;
			case MOVE_TO_MODAL:
				set_position_modally();
				do_move_to_command();
				break;
			case ADD_TERRAIN_MODAL:
				set_position_modally();
				do_add_terrain();
				break;
			case REMOVE_TERRAIN_MODAL:
				set_position_modally();
				do_remove_terrain();
				break;
			case DISTANCE_MODAL:
				drag_for_distance(map, h, v);
				break;
			case ZOOM_MODAL:
				select_area_and_zoom(map, h, v, mods);
				break;
			case GENERIC_MODAL:
				set_position_modally();
				break;
			default:
				run_error("unknown modal tool %d", map_modal);
				break;
		}
		/* Reset modality whether or not the command succeeded, otherwise
		   the player can get caught here if no commands can succeed. */
		map_modal = NO_MODAL;
	} else if (mods & cmdKey) {
		if (map->moveonclick && map->autoselect) {
			unselect_all(map);
			m_nearest_unit(map, h, v, &unit);
			if (unit != NULL && (side_controls_unit(dside, unit) || endofgame)) {
				/* The nearest unit will become the "current unit". */
				map->curunit = unit;
				select_unit_on_map(map, unit);
				draw_selections(map);
				move_on_drag(map, unit, mods);
			} else {
				select_all_dragged_over(map, h, v, mods);
				/* Pick the first of the multiple selection as the "current unit". */
				if (map->numselections > 0) {
					map->curunit = map->selections[0];
				}
			}
		} else {
			anysuccess = FALSE;
			for (i = 0; i < map->numselections; ++i) {
				if ((unit = map->selections[i]) != NULL) {
					rslt = move_the_selected_unit(map, unit, h, v);
					if (rslt)
					  anysuccess = TRUE;
				}
			}
			if (!anysuccess)
			  beep();
		}
	} else if (mods & optionKey) {
		for (i = 0; i < map->numselections; ++i) {
			if ((unit = map->selections[i]) != NULL) {
				fire_the_selected_unit(map, unit, h, v);
			}
		}
	} else if (mods & shiftKey) {
		m_nearest_unit(map, h, v, &unit);
		if (unit && side_sees_unit(dside, unit)) {
			/* Invert the selection status of the unit. */
			if (unit_is_selected(map, unit)) {
				unselect_unit_on_map(map, unit);
				erase_selection(map, unit);
			} else {
				select_unit_on_map(map, unit);
				draw_selections_at(map, unit->x, unit->y);
			}
			draw_unit_info(map);
		} else {
			select_all_dragged_over(map, h, v, mods);
		}

	/* Focus on clicked point and select closest own unit if control key is down. */
	} else if (mods & controlKey) {

		long unitdist, mindist = 32000;

		/* Focus on clicked point */
		set_focus(map, downx, downy);
		/* Find nearest own unit and select it */
	    	for (i = 0; i < dside->actionvector->numunits; ++i) {
	    		unit = (dside->actionvector->units)[i].unit;
   			 if (could_be_next_unit(unit) && side_controls_unit(dside, unit)) {
				/* Use least square method */
				unitdist = (downx - unit->x) * (downx - unit->x) + 
					        (downy - unit->y) * (downy - unit->y);
				if (unitdist < mindist) {
					mindist = unitdist;
					unselect_all(map);
					map->curunit = unit;
				}
		    	}
	    	}
		select_unit_on_map(map, map->curunit);
		draw_selections(map);

	} else {
		/* Interpret an unmodified mouse down. */
#ifdef DESIGNERS
		if (is_designer(dside) && tooltype != notool) {
			apply_designer_tool(map, h, v, mods);
		} else
#endif /* DESIGNERS */
		if (map->moveonclick && !is_designer(dside)) {
			/* Usually will only be one to move, but be general anyway. */
			anysuccess = FALSE;
			for (i = 0; i < map->numselections; ++i) {
				if ((unit = map->selections[i]) != NULL) {
					rslt = move_the_selected_unit(map, unit, h, v);
					if (rslt)
					  anysuccess = TRUE;
				}
			}
			if (!anysuccess)
			  beep();
			map->scrolltocurunit = TRUE;
		} else {
			unselect_all(map);
			m_nearest_unit(map, h, v, &unit);
			if (unit != NULL && (side_controls_unit(dside, unit) || endofgame)) {
				select_unit_on_map(map, unit);
				draw_selections(map);
				move_on_drag(map, unit, mods);
			} else {
				select_all_dragged_over(map, h, v, mods);
			}
		}
	}
}

void
select_all_dragged_over(Map *map, int h0, int v0, int mods)
{
	Point pt0, pt1, newmouse;
	int drawn = FALSE;
	Rect tmprect;

	SetPt(&pt0, h0, v0);
	SetPt(&pt1, h0, v0);
	SetRect(&tmprect, h0, v0, h0, v0);
	/* (should be a generic subr?) */
	PenMode(patXor);
	PenPat(QDPat(gray));
	while (WaitMouseUp()) {
		GetMouse(&newmouse);
		if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
			if (drawn) {
				tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
				tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
				WRAP_RECT(map, tmprect, 
					FrameRect(&tmprect));
			}
			pt1 = newmouse;
			tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
			tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
			WRAP_RECT(map, tmprect, 
				FrameRect(&tmprect));
			drawn = TRUE;
		}
	}
	if (drawn) {
		tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
		tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
		WRAP_RECT(map, tmprect, 
			FrameRect(&tmprect));
	}
	PenNormal();
	select_all_units_in_rect(map, &tmprect);
}

void
select_area_and_zoom(Map *map, int h0, int v0, int mods)
{
	Point pt0, pt1, newmouse;
	int drawn = FALSE, x, y;
	Rect tmprect;

	SetPt(&pt0, h0, v0);
	SetPt(&pt1, h0, v0);
	/* (should be a generic subr) */
	PenMode(patXor);
	PenPat(QDPat(gray));
	while (WaitMouseUp()) {
		GetMouse(&newmouse);
		if (!EqualPt(pt1, newmouse) /* && PtInRect(newmouse, &(map->window->portRect)) */) {
			if (drawn) {
				tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
				tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
				WRAP_RECT(map, tmprect,
					FrameRect(&tmprect));
			}
			pt1 = newmouse;
			tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
			tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
			WRAP_RECT(map, tmprect,
				FrameRect(&tmprect));
			drawn = TRUE;
		}
	}
	if (drawn) {
		tmprect.left = min(pt0.h, pt1.h);  tmprect.top = min(pt0.v, pt1.v);
		tmprect.right = max(pt0.h, pt1.h);  tmprect.bottom = max(pt0.v, pt1.v);
		WRAP_RECT(map, tmprect,
			FrameRect(&tmprect));
	}
	PenNormal();
	m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
	if (x != downx && y != downy) {
		magnify_to_fit(map, downx, downy, x, y);
	}
}

void
move_on_drag(Map *map, Unit *unit, int mods)
{
	int sx, sy, sw, sh, h0, v0, drawn = FALSE, x, y;
	Point pt0, pt1, newmouse;

	m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
	h0 = sx + sw / 2;  v0 = sy + sh / 2;
	SetPt(&pt0, h0, v0);
	SetPt(&pt1, h0, v0);
	/* (should be a generic subr?) */
	PenMode(patXor);
	while (WaitMouseUp()) {
		GetMouse(&newmouse);
		/* should scroll, then abort if we drag outside the window */
		if (0 /* PtInRect(newmouse, &(map->window->portRect)) */) {
		}
		if (!EqualPt(pt1, newmouse)) {
			if (drawn) {
				MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
			}
			pt1 = newmouse;
			MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
			drawn = TRUE;
		}
	}
	/* Erase the last drawn line. */
	if (drawn) {
		MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
	}
	PenNormal();
	m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
	if (x != downx || y != downy) {
		if (!move_the_selected_unit(map, unit, pt1.h, pt1.v))
		  beep();
	} else {
		/* (should try to enter another unit in this cell) */
	}
}

void
drag_for_distance(Map *map, int h0, int v0)
{
	Point pt0, pt1, newmouse;
	int drawn = FALSE, x, y;

	SetPt(&pt0, h0, v0);
	SetPt(&pt1, h0, v0);
	/* (should be a generic subr) */
	PenMode(patXor);
	PenPat(QDPat(gray));
	while (WaitMouseUp()) {
		GetMouse(&newmouse);
		/* should scroll, then abort if we drag outside the window */
		if (0 /* PtInRect(newmouse, &(map->window->portRect)) */) {
		}
		if (!EqualPt(pt1, newmouse)) {
			if (drawn) {
				MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
			}
			pt1 = newmouse;
			MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
			drawn = TRUE;
		}
	}
	/* Erase the last drawn line. */
	if (drawn) {
		MoveTo(h0, v0);  LineTo(pt1.h, pt1.v);
	}
	PenNormal();
	m_nearest_cell(map, pt1.h, pt1.v, &x, &y);
	notify(dside, "Distance from %d,%d to %d,%d is %d",
		   downx, downy, x, y, distance(downx, downy, x, y));
}

void
unselect_all(Map *map)
{
	int num = map->numselections;

	if (map->numselections > 0) {
		erase_selections(map);
		map->numselections = 0;
		draw_unit_info(map);
	}
}

/* Add the given unit to the array of units selected in the given map.  If we need
   more space, then grow the array by 50%. */

void
select_unit_on_map(Map *map, Unit *unit)
{
	if (map->numselections >= map->maxselections) {
		int newsize = map->maxselections + map->maxselections / 2;
		Unit **newarray = (Unit **) realloc((char *) map->selections, newsize * sizeof(Unit *));

		if (newarray == NULL) {
			run_warning("couldn't realloc map selection array");
			return;
		}
		map->maxselections = newsize;
		map->selections = newarray;
	}
	map->selections[map->numselections++] = unit;
}

int
unit_is_selected(Map *map, Unit *unit)
{
	int i;

	for (i = 0; i < map->numselections; ++i) {
		if (map->selections[i] == unit)
		  return TRUE;
	}
	return FALSE;
}

void
unselect_unit_on_map(Map *map, Unit *unit)
{
	int i, j;

	for (i = 0; i < map->numselections; ++i) {
		if (map->selections[i] == unit) {
			/* Keep selection list contiguous, move other units down. */
			for (j = i + 1; j < map->numselections; ++j) {
				map->selections[j - 1] = map->selections[j];
			}
			--map->numselections;
			draw_unit_info(map);
			return;
		}
	}
}

/* Given a map and a rectangle in it, select all the units whose images touch on
   that rectangle. */

void
select_all_units_in_rect(Map *map, Rect *rectptr)
{
	int rectissmall = FALSE;
	int sx, sy, sw, sh;
	Unit *unit;
	Rect unitrect, tmprect;
	
	/* First see if we're selecting over a large area or within a single cell. */
	if (rectptr->right - rectptr->left < map->vp->hw
		&& rectptr->bottom - rectptr->top < map->vp->hh) rectissmall = TRUE;
	/* Now look at all the plausible units and see if any's image intersects the rect. */
	for_all_units(unit) {
		if (in_play(unit)
			&& (side_controls_unit(dside, unit) || endofgame)
			&& (rectissmall || unit->transport == NULL)) {
			m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
			SetRect(&unitrect, sx, sy, sx + sw, sy + sh);
			if (SectRect(&unitrect, rectptr, &tmprect)) {
				select_unit_on_map(map, unit);
				/* (could do setport etc once...) */
				draw_selected_unit_setport(map, unit);
			}
		}
	}
}

/* This translates the user's "go to here" into appropriate tasks and/or actions. */

int
move_the_selected_unit(Map *map, Unit *unit, int h, int v)
{
	int x, y, rslt;
	Unit *other = NULL;

	m_nearest_cell(map, h, v, &x, &y);
	if (unit_at(x, y) != NULL) {
		m_nearest_unit(map, h, v, &other);
	}
	rslt = advance_into_cell(dside, unit, x, y, other);
	return rslt;
}

void
fire_the_selected_unit(Map *map, Unit *unit, int h, int v)
{
	int x, y;
	Unit *other;

	m_nearest_cell(map, h, v, &x, &y);
	if (x != unit->x || y != unit->y) {
		if (unit->act && unit->plan) { /* (should be more sophisticated test?) */
			if ((other = unit_at(x, y)) != NULL) {
				/* There's a unit to fire at. */
				if (other->side == unit->side) {
					beep();
				} else {
					net_prep_fire_at_action(unit, unit, other, -1);
				}
			} else {
				beep();
			}
		}
	}
}

void
select_exactly_one_unit(Map *map, Unit *unit)
{
    Unit *thisunit;

	if (map->numselections > 0) {		
		thisunit = map->selections[0];
		if (thisunit == unit) return;
	}
	unselect_all(map);
	select_unit_on_map(map, unit);
	scroll_to_unit(map, unit);
	draw_selections(map);
}

void
select_next_unit(Map *map)
{
	select_another(map, find_next_unit);
}

void
select_previous_unit(Map *map)
{
	select_another(map, find_prev_unit);
}

void
select_next_actor(Map *map)
{
	select_another(map, find_next_actor);
}

void
select_previous_actor(Map *map)
{
	select_another(map, find_prev_actor);
}

void
select_next_mover(Map *map)
{
	select_another(map, find_next_mover);
}

void
select_previous_mover(Map *map)
{
	select_another(map, find_prev_mover);
}

void
select_next_awake_mover(Map *map)
{
	select_another(map, find_next_awake_mover);
}

void
select_previous_awake_mover(Map *map)
{
	select_another(map, find_prev_awake_mover);
}

/* Given a map and a searching function, go find the "next" matching unit and select it. */

void
select_another(Map *map, Unit *(*fn)(Side *side, Unit *unit))
{
    Unit *thisunit, *nextunit;

	if (fn == NULL) {
		beep();
		return;
	}
	if (map->numselections > 0) {
		thisunit = map->selections[0];
	} else {
		thisunit = NULL;
	}
	nextunit = (*fn)(dside, thisunit);
	if (nextunit != NULL) {
		unselect_all(map);
		select_unit_on_map(map, nextunit);
		scroll_to_unit(map, nextunit);
		draw_selections(map);
		if (map->autoselect) {
			map->curunit = nextunit;
		}
	} else if (thisunit != NULL) {
		scroll_to_unit(map, thisunit);
		/* (should not be done this way, but how else?) */
		if (map->autoselect
			&& (thisunit->act && thisunit->act->acp > 0)
			&& (thisunit->plan && !thisunit->plan->asleep && !thisunit->plan->reserve && !thisunit->plan->delayed)) {
			map->curunit = thisunit;
		}
	}
}

void
scroll_best_map_to_unit(Unit *unit, int bringtofront)
{
	Map *map, *bestmap;
	GrafPtr	oldport;
	
	/* Find the "best" (highest power, unit already visible) map to scroll over
	   to the unit. */
	bestmap = maplist;
	for_all_maps(map) {
		if (map->vp->power > bestmap->vp->power
		    || (map->vp->power == bestmap->vp->power
		        && in_middle(map, unit->x, unit->y)
			 	&& !in_middle(bestmap, unit->x, unit->y))) {
			bestmap = map;
		}
	}
	/* We have a map, now make it show the unit. */
	if (!in_middle(bestmap, unit->x, unit->y)) {
		/* Save the current port. */
		GetPort(&oldport);
		/* Important or junk may appear in the list window. */
		SetPort(bestmap->window);
		scroll_to_unit(bestmap, unit);
		/* Force a gworld update of the whole map content to
		   make sure the scrolled region is updated properly. */
		copy_from_gworld(bestmap, bestmap->contentrect);
		SetPort(oldport);
	}
	if (bringtofront) {
		SelectWindow(bestmap->window);
		update_window(bestmap->window);
		adjust_menus();
	}
}

/* Scroll the given map over to display the given unit. */

void
scroll_to_unit(Map *map, Unit *unit)
{
	int oldsx = map->vp->sx, oldsy = map->vp->sy;
	int	newsx, newsy;
	
	/* Return if unit is not at all on the map */
	if (!inside_area(unit->x, unit->y))
	  return;

	/* Dont scroll if the unit already is in the window */
	if (in_middle(map, unit->x, unit->y))
	  return;

	/* Find new vp */
	set_view_focus(map->vp, unit->x, unit->y);
	m_center_on_focus(map);
	newsx = map->vp->sx;
	newsy = map->vp->sy;

	/* Return if position is unchanged */
	if (oldsx == newsx && oldsy == newsy)
	  return;
	
	/* Restore old vp and let scroll_map_window do the job */
	map->vp->sx = oldsx;
	map->vp->sy = oldsy;
	
	scroll_map_window(map, newsx - oldsx, newsy - oldsy);
}

/* This routine changes a map's viewport and magnification to fit the given rectangle. */

void
magnify_to_fit(Map *map, int x1, int y1, int x2, int y2)
{
	int wid, hgt, wanted, power;

	DGprintf("Magnifying map to fit in area %d,%d - %d,%d\n", x1, y1, x2, y2);
	/* (still need to do y/2 correction) */
	wid = abs(x2 - x1) + 1;  hgt = abs(y2 - y1) + 1;
	map->vp->vcx = min(x1, x2) + wid / 2;  map->vp->vcy = min(y1, y2) + hgt / 2;
	/* Compute the "ideal" size of a displayed cell. */
	wanted = min(map->vp->pxw / wid, map->vp->pxh / hgt);
	/* Search for the best approximation. */
	for (power = NUMPOWERS-1; power > 0; --power) {
		if (hws[power] < wanted) break;
	}
	set_map_mag(map, power);
}

/* Draw a single selected unit on the given map.  Assumes that grafport already set. */

/*

The code now handles case with transport + occ and also third unit correctly, by using the transports 
enclosing box rather than the whole cell as selection box. Moreover, the selected occ is drawn at normal
magnification, instead of filling up the whole box (the latter caused a clutter of icons on top of each other).
The fact that a specific occ is selected is instead indicated by drawing the emblem for that particular occ, 
in addition to the transports emblem. The magnification lines have been removed since they are not needed
anymore, and since they caused problems with leakage into the grid. 

*/

void
draw_selected_unit(map, unit)
Map *map;
Unit *unit;
{
	int sx, sy, sw, sh, size, wholecell = FALSE, drawmag = FALSE;
	int sx1, sy1, sw1, sh1;
	Rect tmprect;

	if (!in_play(unit))
	  return; /* unselect it too? */
	if (map->vp->uw >= 32) {

		/* First draw the image in smaller ÒselfÓ box */
		m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);

		/* Be sure the selected unit is drawn (MOVED FROM BELOW) */
		WRAP_SX(map, sx, 
			draw_unit_image(map->window, sx, sy, sw, sh, unit->type, side_number(unit->side), 
														!completed(unit), TRUE));
		/* Then use bigger box enclosing also occs as for selection box */
		m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
		
		if (map->numselections == 1
			&& sw < 16
			&& (unit->transport != NULL) ) {

			/* Also use the box enclosing transport + occs for selected occ, NOT the whole cell */
			m_xform_unit(map, unit->transport, &sx, &sy, &sw, &sh);

			sx1 = sx;  sy1 = sy;  sw1 = sw;  sh1 = sh;
		}
	} else {
		wholecell = TRUE;
	}
	if (wholecell) {
		xform(map, unit->x, unit->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;
		sw = map->vp->uw;  sh = map->vp->uh;
		/* Be sure the selected unit is drawn. */
		WRAP_SX(map, sx, 
			draw_unit_image(map->window, sx, sy, sw, sh, unit->type, side_number(unit->side), 
														!completed(unit), TRUE));
	}
	if (0 /* not actually within visible area */)
	  return;
	/* Indicate a unit's plans/tasks in some useful fashion. */
	if (map->vp->draw_plans
		&& unit->plan
		&& unit->plan->tasks) {
		int sx2, sy2, sx3, sy3, rad;
		Task *task = unit->plan->tasks, *nexttask;

		if (task != NULL) {
			if ((nexttask = task->next) != NULL) {
				/* Draw the next task first so it can be overwritten by info
				   about the current task. */
				switch (nexttask->type) {
					case TASK_MOVE_TO:
					case TASK_HIT_UNIT:
						if (in_area(nexttask->args[0], nexttask->args[1])) {
							xform(map, nexttask->args[0], nexttask->args[1], &sx3, &sy3);
							if (task->type == TASK_MOVE_TO) {
								xform(map, task->args[0], task->args[1], &sx2, &sy2);
								sx2 += map->vp->hw/2;  sy2 += map->vp->hh/2;
							} else {
								sx2 = sx + sw/2;  sy2 = sy + sh/2;
							}
							PenPat(QDPat(ltGray));
							MoveTo(sx2, sy2);
							LineTo(sx3 + map->vp->hw/2, sy3 + map->vp->hh/2);
							rad = nexttask->args[3];
							if (nexttask->type == TASK_MOVE_TO && rad > 1) {
								Rect tmprect;

								MoveTo(sx3 - rad * map->vp->hh + map->vp->hw/2, sy3 + map->vp->hh/2);
								LineTo(sx3 + rad * map->vp->hh + map->vp->hw/2, sy3 + map->vp->hh/2);
								MoveTo(sx3 + map->vp->hw/2, sy3 - rad * map->vp->hw + map->vp->hh/2);
								LineTo(sx3 + map->vp->hw/2, sy3 + rad * map->vp->hw + map->vp->hh/2);
								tmprect.left  = sx3 - rad * map->vp->hh;  tmprect.top    = sy3 - rad * map->vp->hw;
								tmprect.right = sx3 + rad * map->vp->hh;  tmprect.bottom = sy3 + rad * map->vp->hw;
								OffsetRect(&tmprect, map->vp->hw/2, map->vp->hh/2);
								WRAP_RECT(map, tmprect, 
									FrameOval(&tmprect));
							}
							PenNormal();
						}
				}
			}
			switch (task->type) {
				case TASK_MOVE_TO:
				case TASK_HIT_UNIT:
					if (in_area(task->args[0], task->args[1])) {
						xform(map, task->args[0], task->args[1], &sx2, &sy2);
						PenPat(QDPat(dkGray));
						MoveTo(sx + sw/2, sy + sh/2);
						LineTo(sx2 + map->vp->hw/2, sy2 + map->vp->hh/2);
						rad = task->args[3];
						if (task->type == TASK_MOVE_TO && rad > 1) {
							Rect tmprect;

							MoveTo(sx2 - rad * map->vp->hh + map->vp->hw/2, sy2 + map->vp->hh/2);
							LineTo(sx2 + rad * map->vp->hh + map->vp->hw/2, sy2 + map->vp->hh/2);
							MoveTo(sx2 + map->vp->hw/2, sy2 - rad * map->vp->hw + map->vp->hh/2);
							LineTo(sx2 + map->vp->hw/2, sy2 + rad * map->vp->hw + map->vp->hh/2);
							tmprect.left  = sx2 - rad * map->vp->hh;  tmprect.top    = sy2 - rad * map->vp->hw;
							tmprect.right = sx2 + rad * map->vp->hh;  tmprect.bottom = sy2 + rad * map->vp->hw;
							OffsetRect(&tmprect, map->vp->hw/2, map->vp->hh/2);
								WRAP_RECT(map, tmprect, 
									FrameOval(&tmprect));
						}
						PenNormal();
					}
			}
		}
	}

	/* Note: not used any longer, drawmag always false. Remove? */
	/* Draw magnification lines pointing to the true location of the unit. */
	if (drawmag) {
		/* PenPat should already be black. */
		MoveTo(sx,      sy);       LineTo(sx1,       sy1);
		MoveTo(sx + sw, sy);       LineTo(sx1 + sw1, sy1);
		MoveTo(sx,      sy + sh);  LineTo(sx1,       sy1 + sh1);
		MoveTo(sx + sw, sy + sh);  LineTo(sx1 + sw1, sy1 + sh1);
	}

	/* Draw a highlighting rectangle. */
	SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
	/* A hack to prevent leakage into the grid. */
	if (map->vp->draw_grid && map->vp->power == 5)
	  --tmprect.bottom;
	/* First, draw an outer frame, for contrast. */
	if (map->autoselect && unit->act && unit->act->initacp > 0 && unit->act->acp > 0) {
		PenPat(&animation_patterns[animation_pattern_state]);
	} else {
		PenPat(QDPat(white));
	}
	WRAP_RECT(map, tmprect, 
		FrameRect(&tmprect));
	InsetRect(&tmprect, 1, 1);
	/* Black is for units that can still act, dark gray for actors, gray if the
	   unit can't do anything. */
	PenPat((unit->act && unit->act->initacp > 0) ?
			((unit->act->acp > 0) ? QDPat(black) : QDPat(dkGray)) : QDPat(gray));
	/* Wide border if awake, narrow if asleep or napping. */
	size = ((unit->plan && (unit->plan->asleep || unit->plan->reserve)) ? 1 : 2);
	PenSize(size, size);
	WRAP_RECT(map, tmprect, 
		FrameRect(&tmprect));
	PenNormal();
	DGprintf("draw selection of %s at %d,%d\n", unit_desig(unit));
}

void
draw_unselected_unit(Map *map, Unit *unit)
{
	Unit	*unit2;
	int 	cells, x, y, i;

	if (!in_play(unit)) return;
	x = unit->x;
	y = unit->y;
		
	/* Re-draw the hex containing the unselected unit */
	draw_row(map, x, y, 1, TRUE);

	/* Re-draw gridlines and shorelines of the two hexes below (x, y) */
	if (map->vp->draw_grid &! grid_matches_unseen) draw_gridlines(map, x, y - 1, 2);
	if (map->shorelines) draw_shorelines(map, x, y - 1, 2);

	/* Re-draw any unit names that were erased by the update */
	if (map->vp->draw_names) {
		/* Find the number of cells that a name can cover at this mag */
		switch(map->vp->power) {
			case 7: 	cells = 2; break; 	/* 128 x 128 hexes */
			case 6: 	cells = 3; break;		/*      64 x 64 hexes */	
			case 5: 	cells = 4; break;		/*      32 x 32 hexes */
			case 4: 	cells = 4; break;		/*      16 x 16 hexes */
			case 3: 	cells = 8; break;		/*          8 x  8 hexes */
			default: 	cells = 1; break;
		}
		/* Re-draw unit names in cells - 1 hexes to the left of (x, y) */
		for (i = x - cells + 1; i <  x; i++) {
			for_all_stack(wrapx(i), y, unit2) {
				if (unit2->name)	{							
					draw_unit_names_only(map, i, y);
					break;
				}
			}
		}
	}					

	DGprintf("erase selection of %s at %d,%d\n", unit_desig(unit));
}

void
force_map_update(map)
Map *map;
{
	force_update(map->window);
}

/* Remove and destroy the map object. */

void
destroy_map(map)
Map *map;
{
	Map *map2;

	/* Disconnect it from the list. */	
	if (maplist == map) {
		maplist = map->next;
	} else {
		for_all_maps(map2) {
			if (map2->next == map) {
				map2->next = map->next;
			}
			/* Clean up the map and window numbers. */
			if (map2->id > map->id) {
				remove_window_menu_item(map2->window);
				--map2->id;
				sprintf(spbuf, "Map %d", map2->id);
				add_window_menu_item(spbuf, map2->window);
			}
		}
	}
	if (frontmap == map)
	  frontmap = NULL;
	free_vp(map->vp);
	free(map->selections);

	/* Part of number clean-up */
	--mapnum; --nummaps;

	/* Trash the gworld */
	DisposeGWorld(map->gworldPortPtr);
	
	/* This is NOT done by close_windows! */
	DisposeWindow(map->window);

	/* Erase other-map boxes in other windows */
	draw_related_maps(map);

	/* Necessary to indicate somehow that the map no longer exists! */
	map->id = 0;
	free(map);
}

void
activate_map(map, activate)
Map *map;
int activate;
{
	Rect growRect;

	if (activate) {
		HiliteControl(map->vscrollbar, 0);
		HiliteControl(map->hscrollbar, 0);
		/* Controls need to be redrawn on activation. */
		(*(map->vscrollbar))->contrlVis = 255;
		(*(map->hscrollbar))->contrlVis = 255;
		InvalRect(&(*(map->vscrollbar))->contrlRect);
		InvalRect(&(*(map->hscrollbar))->contrlRect);
		/* The growbox needs to be redrawn on activation. */
		growRect = map->window->portRect;
		/* adjust for the scrollbars */
		growRect.top = growRect.bottom - sbarwid;
		growRect.left = growRect.right - sbarwid;
		InvalRect(&growRect);
		/* Remember this as the map most recently in front, even if it moves behind
		   later. */
		frontmap = map;
	} else {
		/* The scrollbars must be deactivated. */
		HiliteControl(map->vscrollbar, 255);
		HiliteControl(map->hscrollbar, 255);
#if 0  /* We don't want to hide them though, because the window bg is not white. */
		HideControl(map->vscrollbar);
		HideControl(map->hscrollbar);
#endif
		/* The growbox should be changed immediately on deactivation. */
		DrawGrowIcon(map->window);
	}
}

/* Return the frontmost map, whether or not it's the frontmost window. */

Map *
front_map()
{
	Map *map;

	map = map_from_window(FrontWindow());
	if (map != NULL)
	  return map;
	if (frontmap != NULL)
	  return frontmap;
	return NULL;
}

void
print_map(map)
Map *map;
{
	/* (should send postscript output from ps.c to printer port) */
}

/* This function draws feature borders as a single (not double) lines, and only
   between land areas.  It is an alternative to draw_feature_boundaries which is
   invoked by chosing map->simple_borders. */

void 
draw_simple_feature_borders(Map *map, int x0, int y, int len)
{
	int xx, x, dir, x1, y1, mag, wid, wid2, dx1, dy1, sx, sy;

	mag = map->vp->power;
	wid = bwid2[mag];
	if (wid == 0)
	  return;
	PenSize(wid, wid);
	RGBForeColor(&featurecolor);
	PenPat(QDPat(black));
	wid2 = wid / 2;
	
	for (xx = x0; xx < x0 + len; ++xx) {
		x = wrapx(xx);

		if (!terrain_visible(dside, x, y))
		  continue;
		if (t_liquid(terrain_at(x, y)))
		  continue;

		/* Very important to use UNWRAPPED xx here! */
		xform(map, xx, y, &sx, &sy);

		for_all_directions(dir) {
			if (!interior_point_in_dir(x, y, dir, &x1, &y1))
			  continue;
			if (t_liquid(terrain_at(x1, y1)))
			  continue;
			if (!terrain_visible(dside, x1, y1))
			  continue;
			if (feature_at(x, y) == feature_at(x1, y1))
			  continue;

			dx1 = bsx[mag][dir]; dy1 = bsy[mag][dir];
			if (map->vp->angle == 30)
			  dy1 /= 2;
			if (map->vp->angle == 15)
			  dy1 /= 4;
			MoveTo(sx + dx1 - wid2, sy + dy1 - wid2);

			dx1 = bsx[mag][dir+1]; dy1 = bsy[mag][dir+1];
			if (map->vp->angle == 30)
			  dy1 /= 2;
			if (map->vp->angle == 15)
			  dy1 /= 4;
			LineTo(sx + dx1 - wid2, sy + dy1 - wid2);
		}
	}
	PenNormal();
	ForeColor(blackColor);
}

/* This function draws shorelines in 3D relief, using shorecolor. */

void 
draw_shorelines(Map *map, int x0, int y, int len)
{
	int xx, x, dir, x1, y1, mag, wid, wid2, dx1, dy1, sx, sy, iswater;

	mag = map->vp->power;
	wid = bwid2[mag];
	if (wid == 0)
	  return;
	RGBForeColor(&shorecolor);
	PenPat(QDPat(black));
	wid2 = wid / 2;

	for (xx = x0; xx < x0 + len; ++xx) {
		x = wrapx(xx);
		/* Dont draw outside the area (causes crashes) */
		if (!in_area(x, y))
		  continue;
		/* Dont draw shores if (x, y) is invisible */
		if (!terrain_visible(dside, x, y))
		  continue;
		/* Very important to use UNWRAPPED xx here! */
		xform(map, xx, y, &sx, &sy);

		for_all_directions(dir) {
			/* Never draw SE, SW and E shores of a given hex */
			if (dir == EAST || dir == SOUTHEAST || dir == SOUTHWEST)
			  continue;
			/* Dont draw shores on the edge or to invisible hexes */
			if (!point_in_dir(x, y, dir, &x1, &y1))
			  continue;
			if (!terrain_visible(dside, x1, y1))
			  continue;

			/* Use narrow pen */
			PenSize(wid2, wid2);

			/* WATER */
			iswater = t_liquid(terrain_at(x, y));
			if (iswater) {
				/* Dont draw shores to other water hexes */
				if (t_liquid(terrain_at(x1, y1)))
				  continue;
				/* Draw NE and NW shores using wide pen. */
				if (dir != WEST)
				  PenSize(wid2, wid + wid2);
			/* LAND. Dont draw shores to other land hexes */
			} else if (!t_liquid(terrain_at(x1, y1)))
			  continue;

			dx1 = bsx[mag][dir]; dy1 = bsy[mag][dir];
			if (map->vp->angle == 30)
			  dy1 /= 2;
			if (map->vp->angle == 15)
			  dy1 /= 4;
			if (dir != WEST) {
				if (!iswater)
				  dx1 += 1; 
				else if (mag > 3)
				  dy1 -= 1;
			}
			MoveTo(sx + dx1 - wid2, sy + dy1 - 1);

			dx1 = bsx[mag][right_dir(dir)]; dy1 = bsy[mag][right_dir(dir)];
			if (map->vp->angle == 30)
			  dy1 /= 2;
			if (map->vp->angle == 15)
			  dy1 /= 4;
			if (dir != WEST) {
				if (!iswater)
				  dx1 += 1; 
				else if (mag > 3)
				  dy1 -= 1;
			}
			LineTo(sx + dx1 - wid2, sy + dy1 - 1);
		}
	}
	PenNormal();
	ForeColor(blackColor);
}


/* Draw all the selections of all the units. */

void
draw_selections(Map *map)
{
	int sx, sy, sw, sh, i;
	Unit *unit;
	Rect destRect;
	GrafPtr	oldport;

	GetPort(&oldport);

	GO_OFFSCREEN(map);

	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		draw_selected_unit(map, unit);
	}

	RETURN_ONSCREEN(map);
	
	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		/* Dont try to copy a selection outside the map! */
		if (!in_area(unit->x, unit->y))
		  break;
		/* Find the pixel coordinates of (x, y). */
		xform(map, unit->x, unit->y, &sx, &sy);
		/* Copy the whole cell in case this was an occ within a big box. */
		SetRect(&destRect, sx, sy, sx + map->vp->hw, sy + map->vp->hh);
		/* Now copy offscreen map to window. */
		copy_from_gworld(map, destRect);			
	}
	/* Update the unit info row. */
	if (map->numselections > 0) {
		draw_unit_info(map);
	}
	SetPort(oldport);
}

/* Draw all the selected units in the given cell. */

void
draw_selections_at(Map *map, int x, int y)
{
	int xw, sx1, sy1, i;
	Unit *unit;
	Rect destRect;
	GrafPtr	oldport;

	xw = wrapx(x);
	
	GetPort(&oldport);

	GO_OFFSCREEN(map);

	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		if (unit && unit->x == xw && unit->y == y) {
			draw_selected_unit(map, unit);
		}
	}

	RETURN_ONSCREEN(map);
	
	/* Find the pixel coordinates of (x, y) */
	xform(map, x, y, &sx1, &sy1);
	/* Set destRect to enclose the redrawn cell */
	SetRect(&destRect, sx1, sy1, sx1 + map->vp->hw, sy1 + map->vp->hh);
	/* Now copy offscreen map to window */
	copy_from_gworld(map, destRect);			

	/* Update unit info line */
	if (map->numselections > 0) {
		unit = map->selections[0];
		if (in_play(unit) && unit->x == xw && unit->y == y) {
			draw_unit_info(map);
		}
	}
	SetPort(oldport);
}

void
draw_selected_unit_setport(Map *map, Unit *unit)
{
	int sx, sy, sw, sh;
	Rect destRect;
	GrafPtr oldport;

	GetPort(&oldport);

	GO_OFFSCREEN(map);

	draw_selected_unit(map, unit);

	RETURN_ONSCREEN(map);

	/* Dont try to copy a selection outside the map! */
	if (!in_area(unit->x, unit->y))
	  return;
	/* Find the pixel coordinates of (x, y) */
	xform(map, unit->x, unit->y, &sx, &sy);
	/* Copy the whole cell in case this was an occ within a big box */
	SetRect(&destRect, sx, sy, sx + map->vp->hw, sy + map->vp->hh);
	/* Now copy offscreen map to window */
	copy_from_gworld(map, destRect);			

	/* Update unit info line */
	if (map->numselections > 0) {
		unit = map->selections[0];
		if (in_play(unit)) {
			draw_unit_info(map);
		}
	}
	SetPort(oldport);
}

/* The code now handles case with transport + occ and also third unit correctly,
   by using the transports enclosing box rather than the whole cell as selection
   box when either transport or occ is selected. The new definition of contentrect
   is also handled. The black enclosing box is now drawn here as well. */

void
draw_selection_animation(map, unit)
Map *map;
Unit *unit;
{
	int sx, sy, sw, sh, wholecell = FALSE, drawmag = FALSE;
	int sx1, sy1, sw1, sh1, size;
	Rect tmprect, tmpRect;
	GrafPtr oldport;
	RgnHandle tmprgn;

	if (!in_play(unit))
	  return; /* unselect it too? */
 	GetPort(&oldport);
	SetPort(map->window);
	tmprgn = NewRgn();
	GetClip(tmprgn);

	/* Clip to map content */
	tmpRect = map->contentrect;

	/* Nessary to do the offset the since we are drawing directly to the screen */
	OffsetRect(&tmpRect, map->vp->sx - map->conw, map->vp->sy - map->toph);

	ClipRect(&tmpRect);

	SHIFT_ORIGIN(map);
	if (map->vp->uw >= 32) {

		/* Use bigger box enclosing also occs for selection box */
		m_xform_unit(map, unit, &sx, &sy, &sw, &sh);

		if (map->numselections == 1
			&& sw < 16
			&& (unit->transport != NULL) ) {

			m_xform_unit(map, unit->transport, &sx, &sy, &sw, &sh);

			sx1 = sx;  sy1 = sy;  sw1 = sw;  sh1 = sh;
		}
	} else {
		xform(map, unit->x, unit->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;
		sw = map->vp->uw;  sh = map->vp->uh;
	}
	/* Draw a highlighting rectangle. */
	SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
	/* A hack to prevent leakage into the grid. */
	if (map->vp->draw_grid && map->vp->power == 5)
	  --tmprect.bottom;
	/* First, draw an outer white frame, for contrast. */
	if (unit->act && unit->act->initacp > 0 && unit->act->acp > 0) {
		PenPat(&animation_patterns[animation_pattern_state]);
	} else {
		PenPat(QDPat(white));
	}
	FrameRect(&tmprect);
	if (area.xwrap) {
		int rest = map->vp->pxw - tmprect.left;
		/* Draw one extra copy to the left if necessary */
		if (tmprect.right > map->vp->sxmax) {
			OffsetRect(&tmprect, -map->vp->sxmax, 0);
			FrameRect(&tmprect);
			OffsetRect(&tmprect, map->vp->sxmax, 0);
		}		
		/* Draw more copies to the right if necessary*/
		while (rest > 0) {
			OffsetRect(&tmprect, map->vp->sxmax, 0);
			FrameRect(&tmprect);
			rest -= map->vp->sxmax;
		} 
	}
	/* This is important or the black box will disappear for transports at low mag. */
	InsetRect(&tmprect, 1, 1);
	/* Black is for units that can still act, dark gray for actors, gray if the unit can't do anything. */
	PenPat((unit->act && unit->act->initacp > 0) ?
			((unit->act->acp > 0) ? QDPat(black) : QDPat(dkGray)) : QDPat(gray));
	/* Wide border if awake, narrow if asleep or napping. */
	size = ((unit->plan && (unit->plan->asleep || unit->plan->reserve)) ? 1 : 2);
	PenSize(size, size);
	FrameRect(&tmprect);
	if (area.xwrap) {
		int rest = map->vp->pxw - tmprect.left;
		/* Draw one extra copy to the left if necessary */
		if (tmprect.right > map->vp->sxmax) {
			OffsetRect(&tmprect, -map->vp->sxmax, 0);
			FrameRect(&tmprect);
			OffsetRect(&tmprect, map->vp->sxmax, 0);
		}		
		/* Draw more copies to the right if necessary*/
		while (rest > 0) {
			OffsetRect(&tmprect, map->vp->sxmax, 0);
			FrameRect(&tmprect);
			rest -= map->vp->sxmax;
		} 
	}
	PenNormal();
	PenPat(QDPat(black));
	RESET_ORIGIN(map);
	SetClip(tmprgn);
	DisposeRgn(tmprgn);
	SetPort(oldport);
}

/* (should only redraw any given cell once) */

void
erase_selections(Map *map)
{
	int sx, sy, sw, sh, i;
	Unit *unit;
	Rect destRect;
	GrafPtr	oldport;

	GetPort(&oldport);

	GO_OFFSCREEN(map);

	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		draw_unselected_unit(map, unit);
	}

	RETURN_ONSCREEN(map);
	
	for (i = 0; i < map->numselections; ++i) {
		unit = map->selections[i];
		/* Dont try to find a unit that just died, or xform will freak! */
		if (!in_area(unit->x, unit->y))
		  return;
		/* Find the pixel coordinates of (x, y) */
		xform(map, unit->x, unit->y, &sx, &sy);
		/* Copy the whole cell in case this was an occ within a big box */
		SetRect(&destRect, 
			sx - 2, sy - 2,			/* Add 2 for W  & N shorelines */ 	
			sx + map->vp->hw, 
			sy + map->vp->hh + 4);	/* Add 4 for SE & SW shorelines */

		/* Now copy offscreen map to window */
		copy_from_gworld(map, destRect);			
	}
	SetPort(oldport);
}

void
erase_selection(Map *map, Unit *unit)
{
	int sx, sy, sw, sh;
	Rect destRect;
	GrafPtr	oldport;
	
	GetPort(&oldport);
	
	GO_OFFSCREEN(map);

	draw_unselected_unit(map, unit);

	RETURN_ONSCREEN(map);
	
	/* Dont try to copy a selection outside the map! */
	if (!in_area(unit->x, unit->y))
	  return;
	/* Find the pixel coordinates of (x, y) */
	xform(map, unit->x, unit->y, &sx, &sy);
	/* Copy the whole cell in case this was an occ within a big box */
	SetRect(&destRect, 
		sx - 2, sy - 2,			/* Add 2 for W & N shorelines */ 	
		sx + map->vp->hw, 
		sy + map->vp->hh + 4);	/* Add 4 for SE & SW shorelines */

	/* Now copy offscreen map to window */
	copy_from_gworld(map, destRect);			

	SetPort(oldport);
}

/* This function draws the grid explicitly on top of the map. It is used when
   gridcolor differs from unseencolor, and also when map->erase_names is on,
   since erased names otherwise will leak into the grid. Drawing the grid
   explicitly is about 30% slower than using the quick method based on the
   background color and smaller cells. */

void 
draw_gridlines(Map *map, int x0, int y, int len)
{
	int xx, x, dir, x1, y1, mag, wid, dx1, dy1, sx, sy;

	mag = map->vp->power;
	/* (should use generic drawing style test) */
	if (mag < 4)
	  return;
	RGBForeColor(&gridcolor);
	PenPat(QDPat(black));
	PenSize(1, 1);

	for (xx = x0; xx < x0 + len; ++xx) {
		x = wrapx(xx);
		/* Dont draw outside the area (causes crashes) */
		if (!in_area(x, y))
		  continue;
		/* Dont draw grid if (x, y) is invisible */
		if (!terrain_visible(dside, x, y))
		  continue;
		/* Very important to use UNWRAPPED xx here! */
		xform(map, xx, y, &sx, &sy);

		for_all_directions(dir) {
			/* Dont draw SE, SW and E gridlines */
			if (dir == EAST || dir == SOUTHEAST || dir == SOUTHWEST)
			  continue;
			/* Dont draw grid lines on the edge or to invisible hexes */
			if (!point_in_dir(x, y, dir, &x1, &y1))
			  continue;
			if (!terrain_visible(dside, x1, y1))
			  continue;

			dx1 = bsx[mag][dir]; dy1 = bsy[mag][dir];
			if (map->vp->angle == 30)
			  dy1 /= 2;
			if (map->vp->angle == 15)
			  dy1 /= 4;
			if (dir == WEST)
			  dx1 -=1;
			MoveTo(sx + dx1, sy + dy1 - 1);

			dx1 = bsx[mag][dir+1]; dy1 = bsy[mag][dir+1];
			if (map->vp->angle == 30)
			  dy1 /= 2;
			if (map->vp->angle == 15)
			  dy1 /= 4;
			if (dir == WEST)
			  dx1 -=1;
			LineTo(sx + dx1, sy + dy1 - 1);
		}
	}
	PenNormal();
	ForeColor(blackColor);
}

/* Called by update_cell_display and draws only the names for named units in (x, y)
   Based on merged draw_units & draw_unit_and_occs that have been trimmed down
   Unlike draw_unit_and_occs there is no recursion since names of occs are not shown. */
		
void
draw_unit_names_only(Map *map, int x, int y)
{
	int xw = wrapx(x), sx, sy, sw, sh, sx2, sy2, sw2, sh2;
	Rect tmprect;
	Unit *unit;

	if (!map->see_all &! units_visible(dside, xw, y))
	  return;
	unit = unit_at(xw, y);
	if (unit == NULL)
	  return;
	xform(map, x, y, &sx, &sy);

	/* Draws unit names at small mags where contents of grouping boxes is not shown */
	if (map->vp->uw <= 16) {
		/* Adjust to unit part of cell. */
		sw = map->vp->uw;  sh = map->vp->uh;
		sx += (map->vp->hw - sw) / 2;  sy += (map->vp->hh - sw) / 2;
		/* Always draw city and town names at uniform size */
		if (!mobile(unit->type)) {
		  WRAP_SX(map, sx,
			draw_unit_name(unit, sx, sy, map->vp->uw, map->vp->uh, 
		  					     map->textmasks, map->optimize_fonts)); 
		} else {
		  WRAP_SX(map, sx,
			draw_unit_name(unit, sx, sy, sw, sh, map->textmasks, map->optimize_fonts));
		}
	} else {
		/* Draw unit names at mediun and high mags. */
		for_all_stack(xw, y, unit) {
			m_xform_unit(map, unit, &sx, &sy, &sw, &sh);

			/* Draws the name of a boxed carrier with visible occupants */
			if (unit->occupant
				&& sw > 8
				&& (dside->see_all
					|| unit->side == dside
					|| u_see_occupants(unit->type)
					|| side_owns_occupant(dside, unit))) {
				/* Transform carrier to the UL quarter of the box */
				m_xform_occupant(map, unit, unit, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
				/* Always draw city and town names at uniform size */
				if (!mobile(unit->type)) {
				  WRAP_SX(map, sx,
					draw_unit_name(unit, sx, sy, map->vp->uw, map->vp->uh, 
				  					      map->textmasks, map->optimize_fonts));  
				} else {
				  /* sw2 doubled moves the name out of the box so that it can be read more easily */

				  WRAP_SX(map, sx2,
				  	draw_unit_name(unit, sx2, sy2, 2 * sw2, sh2, map->textmasks, 
				  									map->optimize_fonts)); 
				}
			} else {
				/* Draw the names of all other units. */
				/* Always draw city and town names at uniform size */
				if (!mobile(unit->type)) {
				  WRAP_SX(map, sx,
				  	draw_unit_name(unit, sx, sy, map->vp->uw, map->vp->uh, 
				  					      map->textmasks, map->optimize_fonts));  
				} else {

				  WRAP_SX(map, sx,
				  	draw_unit_name(unit, sx, sy, sw, sh, map->textmasks, 
				  								map->optimize_fonts));
				}
			}
		}
	}
}
