/*****************************************************************************
 * $Id: mod_pony_keyboard.cc,v 1.2 2006/11/25 17:37:02 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Keyboard.
 * @remarks	By design, key presses with counterparts to GUI objects
 *			don't affect the config file.  The idea is that the keyboard
 *			causes only temporary changes.  Only when the user makes an
 *			effort to manipulate the GUI should the configuration be saved.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define MOD_PONY_KEYBOARD_CC 1
#include "mod_pony_common.hh"
#include "mod_pony_keyboard.hh"
#include "mod_joystick.hh"
#include "mod_base_craft.hh"
#include "mod_base_chase_plane.hh"
#include "mod_base_combat.hh"
#include "mod_pony_gui.hh"
#include "mod_pony_craft.hh"
#include "mod_pony_event.hh"
#include "gfx_stream.hh"

const fp stride = WORLD(40.0);  // for manual translation of object
const fp NORMAL_STRIDE_SCALE = 1.0;
const fp FAST_STRIDE_SCALE   = 7.0;

static const int sThrottlePercentTable[10] =
{
	0,		// 0
	20,		// 1
	30,		// 2
	40,		// 3
	50,		// 4
	60,		// 5
	70,		// 6
	80,		// 7
	90,		// 8
	100,	// 9
};

/*****************************************************************************
 * Shift/Ctrl magnify/minify maneuvering with cursor keys.
 * Control response is temporarily changed, restored when key is released
 * (effective until auto-rotate finishes).
 *****************************************************************************/
static Percent sResponseSaved = 50;
static bool sResponseSavedFlag = false;  // if sResponseSaved is valid
#define CHANGE_CONTROL_RESPONSE()												\
{																				\
	if ( keyEvent.mShift )														\
	{																			\
		sResponseSaved = GetControlResponse(); sResponseSavedFlag = true;		\
		SetControlResponse( dyna, 100 );										\
	}																			\
	else if ( keyEvent.mCtrl )													\
	{																			\
		sResponseSaved = GetControlResponse(); sResponseSavedFlag = true;		\
		SetControlResponse( dyna, 10 );											\
	}																			\
}
// To be called at key release.
#define RESTORE_CONTROL_RESPONSE()												\
{																				\
	if ( sResponseSavedFlag )													\
	{																			\
		sResponseSavedFlag = false;												\
		SetControlResponse( dyna, sResponseSaved );								\
	}																			\
}

/*****************************************************************************
 * Poll keyboard queue and process all events until queue is empty.
 *****************************************************************************/
void
ModulePony::PollKeyboard( void )
{
	for (;;)
	{
		KeyEvent keyEvent;
		if ( mInputQueue.GetKeyEvent( &keyEvent ) )
		{ 
			// timer() will display if redisplayInput is set.
			// Calling glutPostRedisplay() here out-of-sync with timer()
			// would result in turbulent rendering.
			// -- Merge not assign since either keyboard() or keyboard_special() can set this --
			if ( keyEvent.mSpecial )
			{
                if ( keyEvent.mPress )
                {
                    if ( KeyboardSpecial( *gEng.GetCurrent(), keyEvent ) )
                        gEng.mView.PostRedisplayInput();
                }
                else
                {
                    if ( KeyboardSpecialRelease( *gEng.GetCurrent(), keyEvent ) )
                        gEng.mView.PostRedisplayInput();
                }
			}
			else
			{
                if ( keyEvent.mPress )
                {
                    if ( Keyboard( *gEng.GetCurrent(), keyEvent ) )
                        gEng.mView.PostRedisplayInput();
                }
                else
                {
                    if ( KeyboardRelease( *gEng.GetCurrent(), keyEvent ) )
                        gEng.mView.PostRedisplayInput();
                }
			}
		}
		else
			break;
	}
}

/*****************************************************************************
 * @return True if the user has pressed any key (regardless if it is now released).
 * Used to clear the splash screen.
 *****************************************************************************/
static bool sAnyKeyWasPressed = false;
bool
ModulePony::IfAnyKeyWasPressed( void )
{
	return sAnyKeyWasPressed;
}

/*****************************************************************************
 * Helpers.
 *****************************************************************************/
static bool
IfImportantKey( int key )
{
	switch ( key )
	{
		case eGfxKey_ESC:
		case eGfxKey_CTRL_C:
      //case eGfxKey_CTRL_X:  // redefined as slowly yaw
		case eGfxKey_CTRL_P:
		case eGfxKey_HOME:
		case eGfxKey_END:
		{
			return true;
		}
		break;

		default:
		{
			return false;
		}
		break;
	}
    return false;  // shutup compiler
}

/*****************************************************************************
 * Common code.
 *****************************************************************************/
#define KEYBOARD_PROLOG                                                         \
	int key       = keyEvent.mKey;                                              \
	int keylc     = tolower( key );                                             \
	bool modifier = keyEvent.mShift | keyEvent.mCtrl | keyEvent.mAlt;           \
                                                                                \
	if ( ! GFXSYS_IF_READY() )                                                  \
		return false;  /* unexpected */                                         \
                                                                                \
CHECK_TYPESIG( &dyna, TYPESIG_DYNA );                                           \
                                                                                \
    if ( keyEvent.mPress )                                                      \
    {                                                                           \
        if ( ! sAnyKeyWasPressed ) gGui.SplashOff();                            \
        sAnyKeyWasPressed = true;                                               \
    }                                                                           \
                                                                                \
	/* Ignore most keys if paused. */                                           \
	if ( gEng.mView.IfFreeze()  &&  ! IfImportantKey( key ) )                   \
		return false;                                                           \
                                                                                \
	/* True if redisplay is needed. */                                          \
	bool redisplay = false;

#define KEYBOARD_EPILOG \
    keylc = keylc; modifier = modifier; \
    return redisplay;  // ^ shutup compiler

#define KEYBOARD_RELEASE_PROLOG			\
    KeyboardDynaDisableAutoRotate();	\
	RESTORE_CONTROL_RESPONSE();

#define KEYBOARD_RELEASE_EPILOG // NOP

/*****************************************************************************
 * Rotate Dynas by keyboard.
 *****************************************************************************/
static Dyna* sDynaAutoRotate;

// Prevent sDynaAutoRotate pointing to a destroyed Dyna.
// BTW, ref-cnting would be wrong: that'd keep a dead Dyna alive.
static void
Listener_Keyboard_DynaDestructed( DynaPtr dynaPtr )
{
	if ( dynaPtr == sDynaAutoRotate ) sDynaAutoRotate = NULL;
}

static void
KeyboardDynaSetAutoRotate( Dyna& dyna, uint axis, int dir )
{
ASSERT( CHECK_AXIS(axis) );
ASSERT( dir == 1 || dir == -1 );
    sDynaAutoRotate = &dyna;
    dyna.mPilot->SetAutoRotate( axis, dir );

	// sDynaAutoRotate may become an invalid ptr.
	// Install listener for destroyed Dynas.
	static int listening = 0;
	if ( !listening++ )
		gEngineEvent.mDynaChangeDestructing.Listen( Listener_Keyboard_DynaDestructed );
}

static void
KeyboardDynaDisableAutoRotate( void )
{
    if ( sDynaAutoRotate != NULL )
        sDynaAutoRotate->RemoveAnimation( eAnimation_AUTO_ROTATE );
    sDynaAutoRotate = NULL;
}

/*****************************************************************************
 * Keyboard handler (ASCII keys).
 * @return True if redisplay is needed.
 *****************************************************************************/
bool
ModulePony::Keyboard( Dyna& dyna, const KeyEvent& keyEvent )
{
    KEYBOARD_PROLOG

	switch ( key )
	{
		// q,w : [object] rotate Z axis (roll)
		case 'q': case 'w':
		{
			KeyboardDynaSetAutoRotate( dyna, AXIS_Z, PLUS_MINUS_1(&dyna==&gEng.mEye ? key=='w' : key=='q') );
			redisplay = true;
		}
		break;

		// a,s : [object] rotate X axis (pitch)
		case 'a': case 's':
		{
			KeyboardDynaSetAutoRotate( dyna, AXIS_X, PLUS_MINUS_1(&dyna==&gEng.mEye ? key=='s' : key=='a') );
			redisplay = true;
		}
		break;

		// z,x : [object] rotate Y axis (yaw)
		case 'z': case 'x':
		case 'Z': case 'X':
		case eGfxKey_CTRL_Z:
		case eGfxKey_CTRL_X:
		{
			CHANGE_CONTROL_RESPONSE();
			KeyboardDynaSetAutoRotate( dyna, AXIS_Y, PLUS_MINUS_1(keylc == 'z' || key == eGfxKey_CTRL_Z) );
			redisplay = true;
		}
		break;

		// e,r (forward/backward): [object] translate Z
		case 'e': case 'r':
		case 'E': case 'R':  // faster
		{
            fp scale = isupper(key) ? FAST_STRIDE_SCALE : NORMAL_STRIDE_SCALE;
			dyna.TranslateInc( AXIS_Z, PLUS_MINUS_1(keylc == 'e'), stride * scale );
			redisplay = true;
		}
		break;

		// d,f (left/right): [object] translate X
		case 'd': case 'f':
		case 'D': case 'F':  // faster
		{
            fp scale = isupper(key) ? FAST_STRIDE_SCALE : NORMAL_STRIDE_SCALE;
			dyna.TranslateInc( AXIS_X, PLUS_MINUS_1(keylc == 'd') * PLUS_MINUS_1(&dyna==&gEng.mEye), stride * scale );
			redisplay = true;
		}
		break;

		// c,v (up/down): [object] translate Y
		case 'c': case 'v':
        case 'C': case 'V':  // faster
		{
            fp scale = isupper(key) ? FAST_STRIDE_SCALE : NORMAL_STRIDE_SCALE;
			dyna.TranslateInc( AXIS_Y, PLUS_MINUS_1(keylc == 'v') * PLUS_MINUS_1(&dyna==&gEng.mEye), stride * scale );
			redisplay = true;
		}
		break;

		// o,p: [viewpoint] rotate eye roll
		case 'o': case 'p':
		{
			KeyboardDynaSetAutoRotate( gEng.mEye, AXIS_Z, PLUS_MINUS_1(key == 'p') );
			redisplay = true;
		}
		break;

		// l,';': [viewpoint] rotate eye pitch
		case 'l': case ';':
		{
			KeyboardDynaSetAutoRotate( gEng.mEye, AXIS_X, PLUS_MINUS_1(key == ';') );
			redisplay = true;
		}
		break;

		// '.','/': [viewpoint] rotate yaw
		case '.': case '/':
		{
			KeyboardDynaSetAutoRotate( gEng.mEye, AXIS_Y, PLUS_MINUS_1(key == '.') );
			redisplay = true;
		}
		break;

		// u,i: [viewpoint] translate Z
		case 'u': case 'i':
		{
			gEng.mEye.TranslateInc( AXIS_Z, PLUS_MINUS_1(key == 'u'), stride );
			redisplay = true;
		}
		break;

		// j,k: [viewpoint] translate X
		case 'j': case 'k':
		{
			gEng.mEye.TranslateInc( AXIS_X, PLUS_MINUS_1(key == 'j'), stride );
			redisplay = true;
		}
		break;

		// m,',: [viewpoint] translate Y
		case 'm': case ',':
		{
			gEng.mEye.TranslateInc( AXIS_Y, PLUS_MINUS_1(key == 'm'), stride );
			redisplay = true;
		}
		break;

		// g : toggle landing gear
		case 'g':
		{
			CraftF14Tomcat* tomcat = GetCraftF14TomcatOrNULL();
            if ( tomcat != NULL ) tomcat->ToggleLandingGears();
			redisplay = true;
		}
		break;

		// 1..0 (speed)
		// Subtle dual-purpose:
		// It functions like a throttle when only keyboard input is available.
		// For a joystick, it will amplify/diminish its throttle/wheel response.
		case '0':
		{
			dyna.SetThrottle( sThrottlePercentTable[key-'0'] );
			dyna.SetAnimation( eAnimation_NONE ); // stop
			redisplay = true;
		}
		break;

		case '1': case '2': case '3': case '4': case '5':
		case '6': case '7': case '8': case '9':
		{
			dyna.SetThrottle( sThrottlePercentTable[key-'0'] );
			if ( &dyna != &gEng.mEye || gEng.mEye.mEyeMode == Eye::eEyeMode_FLIGHT )
				dyna.AddAnimation( eAnimation_AUTO_TRANSLATE );
			dyna.Revive(); // let the user cheat after crashing
			redisplay = true;
		}
		break;

		// 1..0 with SHIFT (rotation rate)
		// Affects keyboard and joystick response.
		// Note: By intent, control response changed by Shift-[0-9] keys
	    // are considered temporary, and aren't saved to the config file,
		// unliked changes via the GUI.
		case '!': // 1
		{
			SetControlResponse( dyna, 10 );
			gGui.UpdateControlResponse();
		}
		break;

		case '@': // 2
		{
			SetControlResponse( dyna, 20 );
			gGui.UpdateControlResponse();
		}
		break;

		case '#': // 3
		{
			SetControlResponse( dyna, 30 );
			gGui.UpdateControlResponse();
		}
		break;

		case '$': // 4
		{
			SetControlResponse( dyna, 40 );
			gGui.UpdateControlResponse();
		}
		break;

		case '%': // 5
		{
			SetControlResponse( dyna, 50 );
			gGui.UpdateControlResponse();
		}
		break;

		case '^': // 6
		{
			SetControlResponse( dyna, 60 );
			gGui.UpdateControlResponse();
		}
		break;

		case '&': // 7
		{
			SetControlResponse( dyna, 70 );
			gGui.UpdateControlResponse();
		}
		break;

		case '*': // 8
		{
			SetControlResponse( dyna, 80 );
			gGui.UpdateControlResponse();
		}
		break;

		case '(': // 9
		{
			SetControlResponse( dyna, 90 );
			gGui.UpdateControlResponse();
		}
		break;

		case ')': // 0
		{
			SetControlResponse( dyna, 100 );
			gGui.UpdateControlResponse();
		}
		break;

		// space
		case ' ':
		{
			CombatFireGuns( *mPlayer, gEng.GetCurrent() != &gEng.mEye );
			redisplay = true;
		}
		break;

		// Backspace|Delete: reset
		case eGfxKey_BS: case eGfxKey_DEL:
		{
			gEng.ResetScene();
			gEng.mEye.mEyeMode = Eye::eEyeMode_DEFAULT;
			redisplay = true;
		}
		break;

		// Adjust chase plane distance.
		case eGfxKey_PLUS: case eGfxKey_MINUS:
		{
			dyna.mChasePlane->IncDistance( key==eGfxKey_PLUS ? -1 : 1 );
			redisplay = true;
		}
		break;

		// Save view/state.
		case eGfxKey_CTRL_S:
		{
			if ( SaveState(true) )
			{
				*gStreamOut << "saved state" << endl;
			}
			else
			{
				*gStreamError << "ERROR: couldn't save state" << endl;
			}
		}
		break;

		// pause/freeze key -- also handled by special key handler
		case eGfxKey_CTRL_P:
		{
			gEng.mView.ToggleFreeze();
			GFX_POST_REDISPLAY(); // special case
			gGui.UpdatePause();
			//redisplay = true;
		}
		break;

		// Cycle perspective key.
		case 'P':
		{
			gEng.CyclePerspective();
			redisplay = true;
		}
		break;

		// Toggle weather time.
		case 'W': case 'T':
		{
			gModule.mWeather.CycleTime();
			redisplay = true;
		}
		break;

//#if DEBUG
		// Cycle algorithms used to sort Objects (debug).
		case 'S':
		{
			extern void SorterCycleSortAlgorithm( void );
			SorterCycleSortAlgorithm();
			redisplay = true;
		}
		break;
//#endif

		// ESC: toggle menu screen
		case eGfxKey_ESC:
		{
			gGui.ToggleMenu();
			redisplay = true;
		}
		break;

		// (CTRL-X is recommended)
		// CTRL-X or CTRL-C: quit
		// GLUT has no kind of exit event, just call the usual C exit().
		//case eGfxKey_CTRL_X: // redefined as slow yaw
		case eGfxKey_CTRL_C:
		{
			gEng.Exit(0);
		}
		break;

		case 0:
		{
			// NOP
		}
		break;

		// Print info.
		case '?':
		{
			*gStreamOut << "seed = 0x" << std::hex << gEng.GetSeed() << std::dec << endl;
			gModuleBaseEvent.mPrintStats.Broadcast();
#if DEBUG
			*gStreamOut << gWorld.mLocus;
			*gStreamOut << "eye position = " << gEng.mEye.GetPosition() << endl;
#endif
			gEng.SetVerbose( ! gEng.IfVerbose() ); // toggle verbosity
			*gStreamOut << endl;

			if ( gEng.GetCurrent() == &gEng.mEye )
			{
				*gStreamOut << "Eye matrix: \n";
			}
			else
			{
				*gStreamOut << "Dyna matrix: \n";
			}
			PrintMatrix( gEng.GetCurrent()->mCm );

			redisplay = true;
		}
		break;

		// default:
		default:
		{
			*gStreamOut << "unhandled key = 0x" << std::hex << key << std::dec << endl;
		}
		break;
	}

    KEYBOARD_EPILOG
}

/*****************************************************************************
 * Keyboard release handler (ASCII keys).
 * @return True if redisplay is needed.
 *****************************************************************************/
bool
ModulePony::KeyboardRelease( Dyna& dyna, const KeyEvent& keyEvent )
{
	KEYBOARD_RELEASE_PROLOG
	KEYBOARD_RELEASE_EPILOG
    return false;
}

/*****************************************************************************
 * Keyboard handler (special keys).
 * @return True if redisplay is needed.
 *****************************************************************************/
bool
ModulePony::KeyboardSpecial( Dyna& dyna, const KeyEvent& keyEvent )
{
    KEYBOARD_PROLOG

	switch ( key )
	{
		// [viewpoint] translate Z
		// [object] rotate X (pitch)
		case eGfxKey_UP: case eGfxKey_DOWN:
		{
			if ( ! keyEvent.mAlt )
			{
				CHANGE_CONTROL_RESPONSE();
				if ( &dyna == &gEng.mEye )
				{
					if ( gEng.mEye.mEyeMode == Eye::eEyeMode_PAN )
                    {
						dyna.TranslateInc( AXIS_Z, PLUS_MINUS_1(key == eGfxKey_UP) );
                    }
					else // Eye::eEyeMode_FLIGHT
                    {
						KeyboardDynaSetAutoRotate( dyna, AXIS_X, PLUS_MINUS_1(key == eGfxKey_UP) );
                    }
				}
				else
				{
					KeyboardDynaSetAutoRotate( dyna, AXIS_X, PLUS_MINUS_1(key == eGfxKey_DOWN) );
				}
			}
			else
			{
				// Chase plane controls.
				dyna.mChasePlane->IncRotate( AXIS_X, PLUS_MINUS_1(key == eGfxKey_UP) );
			}
			redisplay = true;
		}
		break;

		// [viewpoint] rotate yaw
		// [object] rotate Z (roll)
		case eGfxKey_LEFT: case eGfxKey_RIGHT:
		{
			if ( ! keyEvent.mAlt )
			{
				CHANGE_CONTROL_RESPONSE();
				if ( &dyna == &gEng.mEye )
				{
					if ( gEng.mEye.mEyeMode == Eye::eEyeMode_PAN )
                    {
						KeyboardDynaSetAutoRotate( dyna, AXIS_Y, PLUS_MINUS_1(key == eGfxKey_LEFT) );
                    }
					else // Eye::eEyeMode_FLIGHT
                    {
						KeyboardDynaSetAutoRotate( dyna, AXIS_Z, PLUS_MINUS_1(key == eGfxKey_RIGHT) );
                    }
				}
				else
				{
					KeyboardDynaSetAutoRotate( dyna, AXIS_Z, PLUS_MINUS_1(key == eGfxKey_LEFT) );
				}
			}
			else
			{
				// Chase plane controls.
				dyna.mChasePlane->IncRotate( AXIS_Y, PLUS_MINUS_1(key == eGfxKey_RIGHT) );
			}
			redisplay = true;
		}
		break;

		// F1: first-person flight mode
		case eGfxKey_F1:
		{
			gGui.PrintStatus( "FLIGHT MODE" );
			gEng.mEye.mEyeMode = Eye::eEyeMode_FLIGHT;
			gEng.SetCurrent( &gEng.mEye );
			gEng.GetCurrent()->ClaimInput();
			gEng.mEye.SetAnimation( eAnimation_AUTO_TRANSLATE );
			redisplay = true;
			gGui.UpdateFlightView();
		}
		break;

		// F2: pan mode
		case eGfxKey_F2:
		{
			gGui.PrintStatus( "PAN MODE" );
			gEng.mEye.mEyeMode = Eye::eEyeMode_PAN;
			gEng.SetCurrent( &gEng.mEye );
			gEng.GetCurrent()->ClaimInput();
			dyna.SetAnimation( eAnimation_NONE ); // stop moving
			gGui.UpdateFlightView();
		}
		break;

		// F3: control player #1
		case eGfxKey_F3:
		{
			SetPlayer( 1 );
			gEng.SetCurrent( mPlayer1Craft );
			gEng.GetCurrent()->ClaimInput();
			gGui.PrintStatus( "CRAFT #1 SELECTED" );
			gGui.UpdateFlightView();
		}
		break;

		// F4: control player #2
		case eGfxKey_F4:
		{
			SetPlayer( 2 );
			gEng.SetCurrent( mPlayer2Craft );
			gEng.GetCurrent()->ClaimInput();
			gGui.PrintStatus( "CRAFT #2 SELECTED" );
			gGui.UpdateFlightView();
		}
		break;

		// switch joystick/keyboard control
		case eGfxKey_F5:
		{
			gJoy.Enable( ! gJoy.IfEnabled() );
			gGui.UpdateInputDevice();
			//redisplay = true;
		}
		break;

#if 0 // TODO FIXME
		// Adjust F-14 Tomcat wings.  NOP for other craft.
		case eGfxKey_F11:
		{
			CraftF14Tomcat* tomcat = GetCraftF14TomcatOrNULL();
            if ( tomcat != NULL ) tomcat->ManualWingSweep( modifier ? -1 : 1 );
   			redisplay = true;
		}
		break;
#endif

		// chase plane view
		case eGfxKey_F12:
		{
			if ( modifier )
			{
				dyna.mChasePlane->Reset();
				redisplay = true;
			}
			else
			{
				gEng.mEye.ToggleAnimation( eAnimation_CHASE_PLANE );
				if ( gEng.mEye.QueryAnimation( eAnimation_CHASE_PLANE ) )
					gGui.PrintStatus( "CHASE PLANE VIEW ON" );
				else
					gGui.PrintStatus( "CHASE PLANE VIEW OFF" );
				//redisplay = true;
			}
		}
		break;

		// TODO reinstate this when terrain-follow is reinstated
		// Toggle auto-pilot.
		case eGfxKey_F6:
		{
			*gStreamOut << "F6 auto-pilot temporarily removed \n";
		}
		break;

#if DEBUG
		case eGfxKey_F8:
		{
			PrintMatrix( gEng.GetCurrent()->mCm );
		}
		break;
#endif

		// pause/freeze key -- also handled by regular key handler
		case eGfxKey_HOME: case eGfxKey_END:
		{
			gEng.mView.ToggleFreeze();
			gGui.UpdatePause();
			GFX_POST_REDISPLAY(); // special case
			//redisplay = true;
		}
		break;

		default:
		{
			*gStreamOut << "unhandled special key = 0x" << std::hex << key << std::dec << endl;
		}
		break;
	}

    KEYBOARD_EPILOG
}

/*****************************************************************************
 * Keyboard release handler (non-ASCII keys).
 * @return True if redisplay is needed.
 *****************************************************************************/
bool
ModulePony::KeyboardSpecialRelease( Dyna& dyna, const KeyEvent& keyEvent )
{
    KEYBOARD_PROLOG
	KEYBOARD_RELEASE_PROLOG

	switch ( key )
	{
        // Asymmetry: Ailerons are rotated by an overriden Dyna::Rotate(),
        // but are rotated back to level when key is released.
        // TODO: This should be generalized/replaced as ailerons
        // also can be controlled by autopilot and joystick.

		// [object] rotate Z (roll)
        // [object] rotate X (pitch)
		case eGfxKey_LEFT: case eGfxKey_RIGHT: case eGfxKey_UP: case eGfxKey_DOWN:
		{
    		if ( &dyna != &gEng.mEye )
			{
       			CraftF14Tomcat* tomcat = GetCraftF14TomcatOrNULL();
                if ( tomcat != NULL ) tomcat->AdjustTailplanes( 0, 0 );
			}
    		redisplay = true;
		}
		break;
    }

    KEYBOARD_RELEASE_EPILOG
    KEYBOARD_EPILOG
}
